plug-in-message.c 46.2 KB
Newer Older
Elliot Lee's avatar
Elliot Lee committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/* The GIMP -- an image manipulation program
 * Copyright (C) 1995 Spencer Kimball and Peter Mattis
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
16
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
Elliot Lee's avatar
Elliot Lee committed
17
 */
18

Elliot Lee's avatar
Elliot Lee committed
19
#include "config.h"
Tor Lillqvist's avatar
Tor Lillqvist committed
20

Manish Singh's avatar
Manish Singh committed
21
22
23
24
#ifdef HAVE_SYS_PARAM_H
#include <sys/param.h>
#endif

25
#include <gtk/gtk.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
26

Elliot Lee's avatar
Elliot Lee committed
27
28
29
#include <signal.h>
#include <stdlib.h>
#include <string.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
30
#ifdef HAVE_SYS_WAIT_H
Elliot Lee's avatar
Elliot Lee committed
31
#include <sys/wait.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
32
33
#endif
#ifdef HAVE_SYS_TIME_H
Elliot Lee's avatar
Elliot Lee committed
34
#include <sys/time.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
35
#endif
36

Elliot Lee's avatar
Elliot Lee committed
37
#include <time.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
38
#ifdef HAVE_UNISTD_H
Elliot Lee's avatar
Elliot Lee committed
39
#include <unistd.h>
Tor Lillqvist's avatar
Tor Lillqvist committed
40
41
#endif

Manish Singh's avatar
Manish Singh committed
42
#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
Tor Lillqvist's avatar
Tor Lillqvist committed
43
44
45
46
#define STRICT
#include <windows.h>
#include <process.h>

47
#ifdef G_OS_WIN32
Tor Lillqvist's avatar
Tor Lillqvist committed
48
49
50
51
#include <fcntl.h>
#include <io.h>
#endif

Manish Singh's avatar
Manish Singh committed
52
#ifdef G_WITH_CYGWIN
Tor Lillqvist's avatar
Tor Lillqvist committed
53
54
55
56
57
58
59
60
#define O_TEXT		0x0100	/* text file */
#define _O_TEXT		0x0100	/* text file */
#define O_BINARY	0x0200	/* binary file */
#define _O_BINARY	0x0200	/* binary file */
#endif

#endif

Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
61
62
63
64
65
66
67
#ifdef __EMX__
#include <fcntl.h>
#include <process.h>
#define _O_BINARY O_BINARY
#define _P_NOWAIT P_NOWAIT
#endif

68
69
70
71
72
73
74
75
#ifdef HAVE_IPC_H
#include <sys/ipc.h>
#endif

#ifdef HAVE_SHM_H
#include <sys/shm.h>
#endif

76
77
78
#include "libgimpbase/gimpbase.h"
#include "libgimpbase/gimpprotocol.h"
#include "libgimpbase/gimpwire.h"
79

Michael Natterer's avatar
Michael Natterer committed
80
#include "plug-in-types.h"
81

Michael Natterer's avatar
Michael Natterer committed
82
83
84
#include "base/tile.h"
#include "base/tile-manager.h"

85
#include "config/gimpcoreconfig.h"
86
#include "config/gimpconfig-path.h"
87

88
#include "core/gimp.h"
89
#include "core/gimpcontext.h"
90
#include "core/gimpdrawable.h"
91
#include "core/gimpenvirontable.h"
92
#include "core/gimpimage.h"
93
94
95

#include "gui/brush-select.h"
#include "gui/gradient-select.h"
96
#include "gui/palette-select.h"
97
98
#include "gui/pattern-select.h"

Michael Natterer's avatar
Michael Natterer committed
99
#include "plug-in.h"
100
101
102
#include "plug-ins.h"
#include "plug-in-def.h"
#include "plug-in-params.h"
103
#include "plug-in-proc.h"
104
#include "plug-in-progress.h"
105
#include "plug-in-rc.h"
Michael Natterer's avatar
Michael Natterer committed
106

107
108
#include "libgimp/gimpintl.h"

109

110
typedef struct _PlugInBlocked PlugInBlocked;
Elliot Lee's avatar
Elliot Lee committed
111
112
113
114

struct _PlugInBlocked
{
  PlugIn *plug_in;
115
  gchar  *proc_name;
Elliot Lee's avatar
Elliot Lee committed
116
117
118
};


119
120
121
122
123
124
typedef struct _PlugInHelpPathDef PlugInHelpPathDef;

struct _PlugInHelpPathDef
{
  gchar *prog_name;
  gchar *help_path;
125
126
127
};


128
static gboolean plug_in_write             (GIOChannel	     *channel,
Elliot Lee's avatar
Elliot Lee committed
129
					   guint8            *buf,
130
131
132
133
				           gulong             count,
                                           gpointer           user_data);
static gboolean plug_in_flush             (GIOChannel        *channel,
                                           gpointer           user_data);
134
135
static void     plug_in_push              (PlugIn            *plug_in);
static void     plug_in_pop               (void);
Tor Lillqvist's avatar
Tor Lillqvist committed
136
137
138
static gboolean plug_in_recv_message	  (GIOChannel	     *channel,
					   GIOCondition	      cond,
					   gpointer	      data);
139
140
141
142
143
static void plug_in_handle_message        (PlugIn            *plug_in,
                                           WireMessage       *msg);
static void plug_in_handle_quit           (PlugIn            *plug_in);
static void plug_in_handle_tile_req       (PlugIn            *plug_in,
                                           GPTileReq         *tile_req);
144
static void plug_in_handle_proc_run       (PlugIn            *plug_in,
145
                                           GPProcRun         *proc_run);
146
static void plug_in_handle_proc_return    (PlugIn            *plug_in,
147
                                           GPProcReturn      *proc_return);
148
static void plug_in_handle_proc_install   (PlugIn            *plug_in,
149
                                           GPProcInstall     *proc_install);
150
static void plug_in_handle_proc_uninstall (PlugIn            *plug_in,
151
                                           GPProcUninstall   *proc_uninstall);
Michael Natterer's avatar
Michael Natterer committed
152
static void plug_in_handle_extension_ack  (PlugIn            *plug_in);
153
static void plug_in_handle_has_init       (PlugIn            *plug_in);
Elliot Lee's avatar
Elliot Lee committed
154

155
156
157
static Argument * plug_in_temp_run       (ProcRecord         *proc_rec,
					  Argument           *args,
					  gint                argc);
158
159
static void       plug_in_init_shm       (void);

160
161
162
163
static gchar    * plug_in_search_in_path (gchar              *search_path,
					  gchar              *filename);

static void       plug_in_prep_for_exec  (gpointer            data);
164

165

166
167
PlugIn     *current_plug_in    = NULL;
ProcRecord *last_plug_in       = NULL;
Elliot Lee's avatar
Elliot Lee committed
168

169
170
static GSList     *open_plug_ins    = NULL;
static GSList     *blocked_plug_ins = NULL;
171
172
173
174

static GSList     *plug_in_stack              = NULL;
static Argument   *current_return_vals        = NULL;
static gint        current_return_nvals       = 0;
Elliot Lee's avatar
Elliot Lee committed
175

176
static gint    shm_ID   = -1;
Elliot Lee's avatar
Elliot Lee committed
177
178
static guchar *shm_addr = NULL;

Manish Singh's avatar
Manish Singh committed
179
#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
Tor Lillqvist's avatar
Tor Lillqvist committed
180
181
182
static HANDLE shm_handle;
#endif

183
184
185
186
187
188
189
190
191
192

static void
plug_in_init_shm (void)
{
  /* allocate a piece of shared memory for use in transporting tiles
   *  to plug-ins. if we can't allocate a piece of shared memory then
   *  we'll fall back on sending the data over the pipe.
   */
  
#ifdef HAVE_SHM_H
193
194
195
196
  shm_ID = shmget (IPC_PRIVATE,
                   TILE_WIDTH * TILE_HEIGHT * 4,
                   IPC_CREAT | 0600);

197
  if (shm_ID == -1)
198
    g_message ("shmget() failed: Disabling shared memory tile transport.");
199
200
  else
    {
201
202
      shm_addr = (guchar *) shmat (shm_ID, NULL, 0);
      if (shm_addr == (guchar *) -1)
203
	{
204
	  g_message ("shmat() failed: Disabling shared memory tile transport.");
205
	  shmctl (shm_ID, IPC_RMID, NULL);
206
207
208
209
	  shm_ID = -1;
	}
      
#ifdef	IPC_RMID_DEFERRED_RELEASE
210
211
      if (shm_addr != (guchar *) -1)
	shmctl (shm_ID, IPC_RMID, NULL);
212
213
214
#endif
    }
#else
Manish Singh's avatar
Manish Singh committed
215
#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
216
217
218
  /* Use Win32 shared memory mechanisms for
   * transfering tile data.
   */
219
220
221
  gint  pid;
  gchar fileMapName[MAX_PATH];
  gint  tileByteSize = TILE_WIDTH * TILE_HEIGHT * 4;
222
223
224
225
226
  
  /* Our shared memory id will be our process ID */
  pid = GetCurrentProcessId ();
  
  /* From the id, derive the file map name */
227
228
  g_snprintf (fileMapName, sizeof (fileMapName), "GIMP%d.SHM", pid);

229
230
231
232
233
234
235
236
237
  /* Create the file mapping into paging space */
  shm_handle = CreateFileMapping ((HANDLE) 0xFFFFFFFF, NULL,
				  PAGE_READWRITE, 0,
				  tileByteSize, fileMapName);
  
  if (shm_handle)
    {
      /* Map the shared memory into our address space for use */
      shm_addr = (guchar *) MapViewOfFile(shm_handle,
238
					  FILE_MAP_ALL_ACCESS,
239
240
241
242
243
					  0, 0, tileByteSize);
      
      /* Verify that we mapped our view */
      if (shm_addr)
	shm_ID = pid;
244
245
246
247
      else
	{
	  g_warning ("MapViewOfFile error: %d... disabling shared memory transport\n", GetLastError());
	}
248
249
250
251
252
253
254
255
256
    }
  else
    {
      g_warning ("CreateFileMapping error: %d... disabling shared memory transport\n", GetLastError());
    }
#endif
#endif
}

Elliot Lee's avatar
Elliot Lee committed
257
void
258
plug_in_init (Gimp *gimp)
Elliot Lee's avatar
Elliot Lee committed
259
{
260
261
  g_return_if_fail (GIMP_IS_GIMP (gimp));

Elliot Lee's avatar
Elliot Lee committed
262
263
264
265
266
267
268
269
270
271
272
  /* initialize the gimp protocol library and set the read and
   *  write handlers.
   */
  gp_init ();
  wire_set_writer (plug_in_write);
  wire_set_flusher (plug_in_flush);

  /* allocate a piece of shared memory for use in transporting tiles
   *  to plug-ins. if we can't allocate a piece of shared memory then
   *  we'll fall back on sending the data over the pipe.
   */
273
  if (gimp->use_shm)
274
    plug_in_init_shm ();
Elliot Lee's avatar
Elliot Lee committed
275
276
277
}

void
278
plug_in_kill (Gimp *gimp)
Elliot Lee's avatar
Elliot Lee committed
279
280
281
282
{
  GSList *tmp;
  PlugIn *plug_in;
  
Manish Singh's avatar
Manish Singh committed
283
#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN)
Tor Lillqvist's avatar
Tor Lillqvist committed
284
285
  CloseHandle (shm_handle);
#else
286
#ifdef HAVE_SHM_H
Elliot Lee's avatar
Elliot Lee committed
287
288
289
#ifndef	IPC_RMID_DEFERRED_RELEASE
  if (shm_ID != -1)
    {
290
291
      shmdt ((gchar *) shm_addr);
      shmctl (shm_ID, IPC_RMID, NULL);
Elliot Lee's avatar
Elliot Lee committed
292
293
294
    }
#else	/* IPC_RMID_DEFERRED_RELEASE */
  if (shm_ID != -1)
295
    shmdt ((gchar *) shm_addr);
Elliot Lee's avatar
Elliot Lee committed
296
#endif
Tor Lillqvist's avatar
Tor Lillqvist committed
297
#endif
298
299
#endif
 
Elliot Lee's avatar
Elliot Lee committed
300
301
302
303
304
305
306
307
308
309
310
  tmp = open_plug_ins;
  while (tmp)
    {
      plug_in = tmp->data;
      tmp = tmp->next;

      plug_in_destroy (plug_in);
    }
}

void
311
312
plug_in_call_query (Gimp      *gimp,
                    PlugInDef *plug_in_def)
Elliot Lee's avatar
Elliot Lee committed
313
{
314
315
  PlugIn      *plug_in;
  WireMessage  msg;
Sven Neumann's avatar
Sven Neumann committed
316

317
  plug_in = plug_in_new (gimp, plug_in_def->prog);
Elliot Lee's avatar
Elliot Lee committed
318

319
  if (plug_in)
Elliot Lee's avatar
Elliot Lee committed
320
    {
321
322
323
      plug_in->query       = TRUE;
      plug_in->synchronous = TRUE;
      plug_in->user_data   = plug_in_def;
Elliot Lee's avatar
Elliot Lee committed
324

325
      if (plug_in_open (plug_in))
Elliot Lee's avatar
Elliot Lee committed
326
	{
327
	  plug_in_push (plug_in);
Elliot Lee's avatar
Elliot Lee committed
328

329
	  while (plug_in->open)
Elliot Lee's avatar
Elliot Lee committed
330
	    {
331
	      if (! wire_read_msg (plug_in->my_read, &msg, plug_in))
332
                {
333
                  plug_in_close (plug_in, TRUE);
334
335
336
                }
	      else 
		{
337
		  plug_in_handle_message (plug_in, &msg);
338
339
		  wire_destroy (&msg);
		}
Elliot Lee's avatar
Elliot Lee committed
340
341
	    }

342
343
	  plug_in_pop ();
	  plug_in_destroy (plug_in);
Elliot Lee's avatar
Elliot Lee committed
344
345
	}
    }
346
}
347

Elliot Lee's avatar
Elliot Lee committed
348
void
349
350
plug_in_call_init (Gimp      *gimp,
                   PlugInDef *plug_in_def)
Elliot Lee's avatar
Elliot Lee committed
351
{
352
353
  PlugIn      *plug_in;
  WireMessage  msg;
Elliot Lee's avatar
Elliot Lee committed
354

355
  plug_in = plug_in_new (gimp, plug_in_def->prog);
Elliot Lee's avatar
Elliot Lee committed
356

357
  if (plug_in)
358
    {
359
360
361
      plug_in->init        = TRUE;
      plug_in->synchronous = TRUE;
      plug_in->user_data   = plug_in_def;
362

363
      if (plug_in_open (plug_in))
364
	{
365
	  plug_in_push (plug_in);
Elliot Lee's avatar
Elliot Lee committed
366

367
	  while (plug_in->open)
Elliot Lee's avatar
Elliot Lee committed
368
	    {
369
	      if (! wire_read_msg (plug_in->my_read, &msg, plug_in))
370
                {
371
                  plug_in_close (plug_in, TRUE);
372
373
374
                }
	      else 
		{
375
		  plug_in_handle_message (plug_in, &msg);
376
377
		  wire_destroy (&msg);
		}
Elliot Lee's avatar
Elliot Lee committed
378
	    }
379

380
381
	  plug_in_pop ();
	  plug_in_destroy (plug_in);
Elliot Lee's avatar
Elliot Lee committed
382
383
	}
    }
384
385
}

386
PlugIn *
387
388
plug_in_new (Gimp  *gimp,
             gchar *name)
Elliot Lee's avatar
Elliot Lee committed
389
390
{
  PlugIn *plug_in;
391
  gchar  *path;
Elliot Lee's avatar
Elliot Lee committed
392

393
394
  g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);

395
  if (! g_path_is_absolute (name))
Elliot Lee's avatar
Elliot Lee committed
396
    {
397
398
399
400
401
402
      gchar *plug_in_path;

      plug_in_path = gimp_config_path_expand (gimp->config->plug_in_path,
                                              FALSE, NULL); 
      path = plug_in_search_in_path (plug_in_path, name);
      g_free (plug_in_path);
403
404

      if (! path)
Elliot Lee's avatar
Elliot Lee committed
405
	{
406
	  g_message (_("Unable to locate Plug-In: \"%s\""), name);
Elliot Lee's avatar
Elliot Lee committed
407
408
409
410
411
412
413
414
	  return NULL;
	}
    }
  else
    {
      path = name;
    }

415
416
417
  plug_in = g_new0 (PlugIn, 1);

  plug_in->gimp               = gimp;
Elliot Lee's avatar
Elliot Lee committed
418

419
420
  plug_in->open               = FALSE;
  plug_in->query              = FALSE;
421
  plug_in->init               = FALSE;
422
423
424
425
426
427
  plug_in->synchronous        = FALSE;
  plug_in->recurse            = FALSE;
  plug_in->busy               = FALSE;
  plug_in->pid                = 0;
  plug_in->args[0]            = g_strdup (path);
  plug_in->args[1]            = g_strdup ("-gimp");
428
429
  plug_in->args[2]            = NULL;
  plug_in->args[3]            = NULL;
430
431
432
433
434
435
436
437
  plug_in->args[4]            = NULL;
  plug_in->args[5]            = NULL;
  plug_in->args[6]            = NULL;
  plug_in->my_read            = NULL;
  plug_in->my_write           = NULL;
  plug_in->his_read           = NULL;
  plug_in->his_write          = NULL;
  plug_in->input_id           = 0;
Elliot Lee's avatar
Elliot Lee committed
438
  plug_in->write_buffer_index = 0;
439
440
441
  plug_in->temp_proc_defs     = NULL;
  plug_in->progress           = NULL;
  plug_in->user_data          = NULL;
Elliot Lee's avatar
Elliot Lee committed
442
443
444
445
446
447
448
449
450

  return plug_in;
}

void
plug_in_destroy (PlugIn *plug_in)
{
  if (plug_in)
    {
451
452
      if (plug_in->open)
	plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
453
454
455
456
457
458
459
460
461
462
463
464
465
466

      if (plug_in->args[0])
	g_free (plug_in->args[0]);
      if (plug_in->args[1])
	g_free (plug_in->args[1]);
      if (plug_in->args[2])
	g_free (plug_in->args[2]);
      if (plug_in->args[3])
	g_free (plug_in->args[3]);
      if (plug_in->args[4])
	g_free (plug_in->args[4]);
      if (plug_in->args[5])
	g_free (plug_in->args[5]);

467
      if (plug_in->progress)
468
	plug_in_progress_end (plug_in);
469

Elliot Lee's avatar
Elliot Lee committed
470
471
472
      if (plug_in == current_plug_in)
	plug_in_pop ();

473
      g_free (plug_in);
Elliot Lee's avatar
Elliot Lee committed
474
475
476
    }
}

477
478
static void
plug_in_prep_for_exec (gpointer data)
Tor Lillqvist's avatar
Tor Lillqvist committed
479
{
480
481
#if !defined(G_OS_WIN32) && !defined (G_WITH_CYGWIN) && !defined(__EMX__)
  PlugIn *plug_in = data;
Tor Lillqvist's avatar
Tor Lillqvist committed
482

483
484
  g_io_channel_unref (plug_in->my_read);
  plug_in->my_read  = NULL;
Tor Lillqvist's avatar
Tor Lillqvist committed
485

486
487
488
489
  g_io_channel_unref (plug_in->my_write);
  plug_in->my_write  = NULL;
#endif
}
Tor Lillqvist's avatar
Tor Lillqvist committed
490

491
gboolean
Elliot Lee's avatar
Elliot Lee committed
492
493
plug_in_open (PlugIn *plug_in)
{
494
495
  gint my_read[2];
  gint my_write[2];
496
497
  gchar **envp;
  GError *error = NULL;
Elliot Lee's avatar
Elliot Lee committed
498

499
500
  g_return_val_if_fail (plug_in != NULL, FALSE);

Elliot Lee's avatar
Elliot Lee committed
501
502
503
504
505
506
  if (plug_in)
    {
      /* Open two pipes. (Bidirectional communication).
       */
      if ((pipe (my_read) == -1) || (pipe (my_write) == -1))
	{
507
	  g_message ("pipe() failed: Unable to start Plug-In \"%s\"\n(%s)",
508
509
		     g_path_get_basename (plug_in->args[0]),
		     plug_in->args[0]);
510
	  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
511
512
	}

Manish Singh's avatar
Manish Singh committed
513
#if defined(G_WITH_CYGWIN) || defined(__EMX__)
Tor Lillqvist's avatar
Tor Lillqvist committed
514
      /* Set to binary mode */
515
516
517
518
      setmode (my_read[0], _O_BINARY);
      setmode (my_write[0], _O_BINARY);
      setmode (my_read[1], _O_BINARY);
      setmode (my_write[1], _O_BINARY);
Tor Lillqvist's avatar
Tor Lillqvist committed
519
520
#endif

521
522
523
      plug_in->my_read   = g_io_channel_unix_new (my_read[0]);
      plug_in->my_write  = g_io_channel_unix_new (my_write[1]);
      plug_in->his_read  = g_io_channel_unix_new (my_write[0]);
Tor Lillqvist's avatar
Tor Lillqvist committed
524
      plug_in->his_write = g_io_channel_unix_new (my_read[1]);
Elliot Lee's avatar
Elliot Lee committed
525

526
527
528
529
530
      g_io_channel_set_encoding (plug_in->my_read, NULL, NULL);
      g_io_channel_set_encoding (plug_in->my_write, NULL, NULL);
      g_io_channel_set_encoding (plug_in->his_read, NULL, NULL);
      g_io_channel_set_encoding (plug_in->his_write, NULL, NULL);

531
532
533
534
535
      g_io_channel_set_buffered (plug_in->my_read, FALSE);
      g_io_channel_set_buffered (plug_in->my_write, FALSE);
      g_io_channel_set_buffered (plug_in->his_read, FALSE);
      g_io_channel_set_buffered (plug_in->his_write, FALSE);

536
537
538
539
540
      g_io_channel_set_close_on_unref (plug_in->my_read, TRUE);
      g_io_channel_set_close_on_unref (plug_in->my_write, TRUE);
      g_io_channel_set_close_on_unref (plug_in->his_read, TRUE);
      g_io_channel_set_close_on_unref (plug_in->his_write, TRUE);

Elliot Lee's avatar
Elliot Lee committed
541
542
      /* Remember the file descriptors for the pipes.
       */
543
544
545
546
      plug_in->args[2] =
	g_strdup_printf ("%d", g_io_channel_unix_get_fd (plug_in->his_read));
      plug_in->args[3] =
	g_strdup_printf ("%d", g_io_channel_unix_get_fd (plug_in->his_write));
Elliot Lee's avatar
Elliot Lee committed
547
548

      /* Set the rest of the command line arguments.
549
       * FIXME: this is ugly.  Pass in the mode as a separate argument?
Elliot Lee's avatar
Elliot Lee committed
550
551
552
553
554
       */
      if (plug_in->query)
	{
	  plug_in->args[4] = g_strdup ("-query");
	}
555
556
557
558
559
      else if (plug_in->init)
	{
	  plug_in->args[4] = g_strdup ("-init");
	}
      else  
Elliot Lee's avatar
Elliot Lee committed
560
	{
561
	  plug_in->args[4] = g_strdup ("-run");
Elliot Lee's avatar
Elliot Lee committed
562
563
	}

564
      plug_in->args[5] = g_strdup_printf ("%d", plug_in->gimp->stack_trace_mode);
565

Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
566
#ifdef __EMX__
567
568
      fcntl (my_read[0], F_SETFD, 1);
      fcntl (my_write[1], F_SETFD, 1);
Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
569
#endif
Elliot Lee's avatar
Elliot Lee committed
570

571
572
573
574
575
576
577
578
579
580
581
      /* Fork another process. We'll remember the process id
       *  so that we can later use it to kill the filter if
       *  necessary.
       */
      envp = gimp_environ_table_get_envp (plug_in->gimp->environ_table);
      if (! g_spawn_async (NULL, plug_in->args, envp,
                           G_SPAWN_LEAVE_DESCRIPTORS_OPEN |
                           G_SPAWN_DO_NOT_REAP_CHILD,
                           plug_in_prep_for_exec, plug_in,
                           &plug_in->pid,
                           &error))
Elliot Lee's avatar
Elliot Lee committed
582
	{
583
          g_message ("Unable to run Plug-In: \"%s\"\n(%s)\n%s",
584
		     g_path_get_basename (plug_in->args[0]),
585
586
587
588
		     plug_in->args[0],
		     error->message);
          g_error_free (error);

Elliot Lee's avatar
Elliot Lee committed
589
          plug_in_destroy (plug_in);
590
          return FALSE;
Elliot Lee's avatar
Elliot Lee committed
591
592
	}

Tor Lillqvist's avatar
Tor Lillqvist committed
593
594
      g_io_channel_unref (plug_in->his_read);
      plug_in->his_read  = NULL;
595

Tor Lillqvist's avatar
Tor Lillqvist committed
596
597
598
      g_io_channel_unref (plug_in->his_write);
      plug_in->his_write = NULL;

Elliot Lee's avatar
Elliot Lee committed
599
600
      if (!plug_in->synchronous)
	{
601
602
603
604
605
	  plug_in->input_id =
	    g_io_add_watch (plug_in->my_read,
			    G_IO_IN | G_IO_PRI | G_IO_ERR | G_IO_HUP,
			    plug_in_recv_message,
			    plug_in);
Elliot Lee's avatar
Elliot Lee committed
606
607
608
609
610

	  open_plug_ins = g_slist_prepend (open_plug_ins, plug_in);
	}

      plug_in->open = TRUE;
611
      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
612
613
    }

614
  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
615
616
617
}

void
618
619
plug_in_close (PlugIn   *plug_in,
	       gboolean  kill_it)
Elliot Lee's avatar
Elliot Lee committed
620
{
621
  gint status;
622
#ifndef G_OS_WIN32
Elliot Lee's avatar
Elliot Lee committed
623
  struct timeval tv;
Tor Lillqvist's avatar
Tor Lillqvist committed
624
#endif
Elliot Lee's avatar
Elliot Lee committed
625

626
627
  g_return_if_fail (plug_in != NULL);
  g_return_if_fail (plug_in->open == TRUE);
Elliot Lee's avatar
Elliot Lee committed
628

629
630
631
632
  if (! plug_in->open)
    return;

  plug_in->open = FALSE;
Elliot Lee's avatar
Elliot Lee committed
633

634
635
636
637
  /*  Ask the filter to exit gracefully  */
  if (kill_it && plug_in->pid)
    {
      plug_in_push (plug_in);
638
      gp_quit_write (plug_in->my_write, plug_in);
639
640
641
      plug_in_pop ();

      /*  give the plug-in some time (10 ms)  */
642
#ifndef G_OS_WIN32
643
644
645
      tv.tv_sec  = 0;
      tv.tv_usec = 100;	/* But this is 0.1 ms? */
      select (0, NULL, NULL, NULL, &tv);
Tor Lillqvist's avatar
Tor Lillqvist committed
646
#else
647
      Sleep (10);
Tor Lillqvist's avatar
Tor Lillqvist committed
648
#endif
649
    }
Elliot Lee's avatar
Elliot Lee committed
650

651
  /* If necessary, kill the filter. */
652
#ifndef G_OS_WIN32
653
654
  if (kill_it && plug_in->pid)
    status = kill (plug_in->pid, SIGKILL);
Elliot Lee's avatar
Elliot Lee committed
655

656
657
658
659
660
  /* Wait for the process to exit. This will happen
   *  immediately if it was just killed.
   */
  if (plug_in->pid)
    waitpid (plug_in->pid, &status, 0);
Tor Lillqvist's avatar
Tor Lillqvist committed
661
#else
662
663
664
665
666
667
668
669
670
671
672
  if (kill_it && plug_in->pid)
    {
      /* Trying to avoid TerminateProcess (does mostly work).
       * Otherwise some of our needed DLLs may get into an unstable state
       * (see Win32 API docs).
       */
      DWORD dwExitCode = STILL_ACTIVE;
      DWORD dwTries  = 10;
      while ((STILL_ACTIVE == dwExitCode)
	     && GetExitCodeProcess((HANDLE) plug_in->pid, &dwExitCode)
	     && (dwTries > 0))
Tor Lillqvist's avatar
Tor Lillqvist committed
673
	{
674
675
	  Sleep(10);
	  dwTries--;
Tor Lillqvist's avatar
Tor Lillqvist committed
676
	}
677
      if (STILL_ACTIVE == dwExitCode)
Tor Lillqvist's avatar
Tor Lillqvist committed
678
	{
679
680
	  g_warning("Terminating %s ...", plug_in->args[0]);
	  TerminateProcess ((HANDLE) plug_in->pid, 0);
Tor Lillqvist's avatar
Tor Lillqvist committed
681
	}
682
683
    }
#endif
Elliot Lee's avatar
Elliot Lee committed
684

685
  plug_in->pid = 0;
Elliot Lee's avatar
Elliot Lee committed
686

687
688
689
690
  /* Remove the input handler. */
  if (plug_in->input_id)
    {
      g_source_remove (plug_in->input_id);
Elliot Lee's avatar
Elliot Lee committed
691
      plug_in->input_id = 0;
692
693
694
695
696
697
    }

  /* Close the pipes. */
  if (plug_in->my_read != NULL)
    {
      g_io_channel_unref (plug_in->my_read);
Tor Lillqvist's avatar
Tor Lillqvist committed
698
      plug_in->my_read = NULL;
699
700
701
702
    }
  if (plug_in->my_write != NULL)
    {
      g_io_channel_unref (plug_in->my_write);
Tor Lillqvist's avatar
Tor Lillqvist committed
703
      plug_in->my_write = NULL;
704
705
706
707
    }
  if (plug_in->his_read != NULL)
    {
      g_io_channel_unref (plug_in->his_read);
Tor Lillqvist's avatar
Tor Lillqvist committed
708
      plug_in->his_read = NULL;
709
710
711
712
    }
  if (plug_in->his_write != NULL)
    {
      g_io_channel_unref (plug_in->his_write);
Tor Lillqvist's avatar
Tor Lillqvist committed
713
      plug_in->his_write = NULL;
714
    }
Elliot Lee's avatar
Elliot Lee committed
715

716
  wire_clear_error ();
Elliot Lee's avatar
Elliot Lee committed
717

718
  if (plug_in->progress)
719
    plug_in_progress_end (plug_in);
Elliot Lee's avatar
Elliot Lee committed
720

721
722
  if (plug_in->recurse)
    {
723
      gimp_main_loop_quit (plug_in->gimp);
724

725
726
      plug_in->recurse = FALSE;
    }
Elliot Lee's avatar
Elliot Lee committed
727

728
  plug_in->synchronous = FALSE;
Elliot Lee's avatar
Elliot Lee committed
729

730
731
732
733
  /* Unregister any temporary procedures. */
  if (plug_in->temp_proc_defs)
    {
      g_slist_foreach (plug_in->temp_proc_defs,
734
735
		       (GFunc) plug_ins_proc_def_remove,
		       plug_in->gimp);
736
737
738
      g_slist_foreach (plug_in->temp_proc_defs,
                       (GFunc) g_free,
                       NULL);
739
740
741
      g_slist_free (plug_in->temp_proc_defs);
      plug_in->temp_proc_defs = NULL;
    }
Elliot Lee's avatar
Elliot Lee committed
742

743
744
745
  /* Close any dialogs that this plugin might have opened */
  brush_select_dialogs_check ();
  gradient_select_dialogs_check ();
746
747
  palette_select_dialogs_check ();
  pattern_select_dialogs_check ();
748

749
  open_plug_ins = g_slist_remove (open_plug_ins, plug_in);
Elliot Lee's avatar
Elliot Lee committed
750
751
752
753
754
755
}

static Argument *
plug_in_get_current_return_vals (ProcRecord *proc_rec)
{
  Argument *return_vals;
756
  gint      nargs;
Elliot Lee's avatar
Elliot Lee committed
757
758
759
760

  /* Return the status code plus the current return values. */
  nargs = proc_rec->num_values + 1;
  if (current_return_vals && current_return_nvals == nargs)
761
762
763
    {
      return_vals = current_return_vals;
    }
Elliot Lee's avatar
Elliot Lee committed
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
  else if (current_return_vals)
    {
      /* Allocate new return values of the correct size. */
      return_vals = procedural_db_return_args (proc_rec, FALSE);

      /* Copy all of the arguments we can. */
      memcpy (return_vals, current_return_vals,
	      sizeof (Argument) * MIN (current_return_nvals, nargs));

      /* Free the old argument pointer.  This will cause a memory leak
	 only if there were more values returned than we need (which
	 shouldn't ever happen). */
      g_free (current_return_vals);
    }
  else
    {
      /* Just return a dummy set of values. */
      return_vals = procedural_db_return_args (proc_rec, FALSE);
    }

  /* We have consumed any saved values, so clear them. */
  current_return_nvals = 0;
786
  current_return_vals  = NULL;
Elliot Lee's avatar
Elliot Lee committed
787
788
789
790

  return return_vals;
}

791
Argument *
792
793
plug_in_run (Gimp       *gimp,
             ProcRecord *proc_rec,
Elliot Lee's avatar
Elliot Lee committed
794
	     Argument   *args,
795
796
797
798
	     gint        argc,
	     gboolean    synchronous,   
	     gboolean    destroy_values,
	     gint        gdisp_ID)
Elliot Lee's avatar
Elliot Lee committed
799
{
800
801
802
803
  GPConfig   config;
  GPProcRun  proc_run;
  Argument  *return_vals;
  PlugIn    *plug_in;
Elliot Lee's avatar
Elliot Lee committed
804
805
806

  return_vals = NULL;

807
  if (proc_rec->proc_type == GIMP_TEMPORARY)
Elliot Lee's avatar
Elliot Lee committed
808
    {
809
      return_vals = plug_in_temp_run (proc_rec, args, argc);
Elliot Lee's avatar
Elliot Lee committed
810
811
812
      goto done;
    }

813
  plug_in = plug_in_new (gimp, proc_rec->exec_method.plug_in.filename);
Elliot Lee's avatar
Elliot Lee committed
814
815
816
817
818
819
820
821
822

  if (plug_in)
    {
      if (plug_in_open (plug_in))
	{
	  plug_in->recurse = synchronous;

	  plug_in_push (plug_in);

823
824
825
826
	  config.version      = GP_VERSION;
	  config.tile_width   = TILE_WIDTH;
	  config.tile_height  = TILE_HEIGHT;
	  config.shm_ID       = shm_ID;
827
828
	  config.gamma        = gimp->config->gamma_val;
	  config.install_cmap = gimp->config->install_cmap;
829
          config.unused       = 0;
830
          config.min_colors   = CLAMP (gimp->config->min_colors, 27, 256);
831
832
833
	  config.gdisp_ID     = gdisp_ID;

	  proc_run.name    = proc_rec->name;
834
	  proc_run.nparams = argc;
835
	  proc_run.params  = plug_in_args_to_params (args, argc, FALSE);
Elliot Lee's avatar
Elliot Lee committed
836

837
838
839
	  if (! gp_config_write (plug_in->my_write, &config, plug_in)     ||
	      ! gp_proc_run_write (plug_in->my_write, &proc_run, plug_in) ||
	      ! wire_flush (plug_in->my_write, plug_in))
Elliot Lee's avatar
Elliot Lee committed
840
841
842
843
844
845
846
847
848
	    {
	      return_vals = procedural_db_return_args (proc_rec, FALSE);
	      goto done;
	    }

	  plug_in_pop ();

	  plug_in_params_destroy (proc_run.params, proc_run.nparams, FALSE);

849
850
	  /*  If this is an automatically installed extension, wait for an
	   *  installation-confirmation message
Elliot Lee's avatar
Elliot Lee committed
851
	   */
852
	  if ((proc_rec->proc_type == GIMP_EXTENSION) &&
853
	      (proc_rec->num_args == 0))
854
            {
855
              gimp_main_loop (gimp);
856
            }
Elliot Lee's avatar
Elliot Lee committed
857
858
859

	  if (plug_in->recurse)
	    {
860
              gimp_main_loop (gimp);
861

Elliot Lee's avatar
Elliot Lee committed
862
863
864
865
	      return_vals = plug_in_get_current_return_vals (proc_rec);
	    }
	}
    }
866

867
 done:
Elliot Lee's avatar
Elliot Lee committed
868
869
870
871
872
873
874
875
876
  if (return_vals && destroy_values)
    {
      procedural_db_destroy_args (return_vals, proc_rec->num_values);
      return_vals = NULL;
    }
  return return_vals;
}

void
877
878
879
880
881
plug_in_repeat (Gimp    *gimp,
                gint     display_ID,
                gint     image_ID,
                gint     drawable_ID,
                gboolean with_interface)
Elliot Lee's avatar
Elliot Lee committed
882
{
883
884
  Argument    *args;
  gint         i;
Elliot Lee's avatar
Elliot Lee committed
885
886
887
888

  if (last_plug_in)
    {
      /* construct the procedures arguments */
889
      args = g_new (Argument, 3);
Elliot Lee's avatar
Elliot Lee committed
890

891
892
      /* initialize the first three argument types */
      for (i = 0; i < 3; i++)
Elliot Lee's avatar
Elliot Lee committed
893
894
	args[i].arg_type = last_plug_in->args[i].arg_type;

895
      /* initialize the first three plug-in arguments  */
896
897
      args[0].value.pdb_int = (with_interface ? 
                               GIMP_RUN_INTERACTIVE : GIMP_RUN_WITH_LAST_VALS);
898
899
      args[1].value.pdb_int = image_ID;
      args[2].value.pdb_int = drawable_ID;
Elliot Lee's avatar
Elliot Lee committed
900
901

      /* run the plug-in procedure */
902
      plug_in_run (gimp, last_plug_in, args, 3, FALSE, TRUE, display_ID);
Elliot Lee's avatar
Elliot Lee committed
903
904
905
906
907

      g_free (args);
    }
}

Tor Lillqvist's avatar
Tor Lillqvist committed
908
static gboolean
909
910
911
plug_in_recv_message (GIOChannel   *channel,
		      GIOCondition  cond,
		      gpointer	    data)
Elliot Lee's avatar
Elliot Lee committed
912
{
913
914
  PlugIn   *plug_in;
  gboolean  got_message = FALSE;
915

916
917
918
919
  plug_in = (PlugIn *) data;

  if (plug_in != current_plug_in)
    plug_in_push (plug_in);
Elliot Lee's avatar
Elliot Lee committed
920

921
  if (plug_in->my_read == NULL)
Tor Lillqvist's avatar
Tor Lillqvist committed
922
    return TRUE;
Elliot Lee's avatar
Elliot Lee committed
923

924
925
926
927
928
929
  if (cond & (G_IO_IN | G_IO_PRI))
    {
      WireMessage msg;

      memset (&msg, 0, sizeof (WireMessage));

930
      if (! wire_read_msg (plug_in->my_read, &msg, plug_in))
931
	{
932
	  plug_in_close (plug_in, TRUE);
933
	}
934
      else
935
	{
936
	  plug_in_handle_message (plug_in, &msg);
937
938
939
940
941
942
	  wire_destroy (&msg);
	  got_message = TRUE;
	}
    }

  if (cond & (G_IO_ERR | G_IO_HUP))
Elliot Lee's avatar
Elliot Lee committed
943
    {
944
      if (plug_in->open)
945
	{
946
	  plug_in_close (plug_in, TRUE);
947
	}
Elliot Lee's avatar
Elliot Lee committed
948
949
    }

950
  if (! got_message)
951
952
953
954
    g_message (_("Plug-In crashed: \"%s\"\n(%s)\n\n"
		 "The dying Plug-In may have messed up GIMP's internal state.\n"
		 "You may want to save your images and restart GIMP\n"
		 "to be on the safe side."),
955
956
	       g_path_get_basename (plug_in->args[0]),
	       plug_in->args[0]);
957

958
959
  if (! plug_in->open)
    plug_in_destroy (plug_in);
Elliot Lee's avatar
Elliot Lee committed
960
961
  else
    plug_in_pop ();
962

Tor Lillqvist's avatar
Tor Lillqvist committed
963
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
964
965
966
}

static void
967
968
plug_in_handle_message (PlugIn      *plug_in,
                        WireMessage *msg)
Elliot Lee's avatar
Elliot Lee committed
969
970
971
972
{
  switch (msg->type)
    {
    case GP_QUIT:
973
      plug_in_handle_quit (plug_in);
Elliot Lee's avatar
Elliot Lee committed
974
      break;
Michael Natterer's avatar
Michael Natterer committed
975

Elliot Lee's avatar
Elliot Lee committed
976
    case GP_CONFIG:
977
978
      g_warning ("plug_in_handle_message(): "
		 "received a config message (should not happen)");
979
      plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
980
      break;
Michael Natterer's avatar
Michael Natterer committed
981

Elliot Lee's avatar
Elliot Lee committed
982
    case GP_TILE_REQ:
983
      plug_in_handle_tile_req (plug_in, msg->data);
Elliot Lee's avatar
Elliot Lee committed
984
      break;
Michael Natterer's avatar
Michael Natterer committed
985

Elliot Lee's avatar
Elliot Lee committed
986
    case GP_TILE_ACK:
987
988
      g_warning ("plug_in_handle_message(): "
		 "received a tile ack message (should not happen)");
989
      plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
990
      break;
Michael Natterer's avatar
Michael Natterer committed
991

Elliot Lee's avatar
Elliot Lee committed
992
    case GP_TILE_DATA:
993
994
      g_warning ("plug_in_handle_message(): "
		 "received a tile data message (should not happen)");
995
      plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
996
      break;
Michael Natterer's avatar
Michael Natterer committed
997

Elliot Lee's avatar
Elliot Lee committed
998
    case GP_PROC_RUN:
999
      plug_in_handle_proc_run (plug_in, msg->data);
Elliot Lee's avatar
Elliot Lee committed
1000
      break;
Michael Natterer's avatar
Michael Natterer committed
1001

Elliot Lee's avatar
Elliot Lee committed
1002
    case GP_PROC_RETURN: