plug-in-message.c 48.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
#ifdef __EMX__
#include <fcntl.h>
#include <process.h>
#define _O_BINARY O_BINARY
#define _P_NOWAIT P_NOWAIT
Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
66
#define xspawnv spawnv
Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
67
68
#endif

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

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

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

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

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

86
#include "core/gimp.h"
87
#include "core/gimpcontext.h"
Michael Natterer's avatar
Michael Natterer committed
88
#include "core/gimpcoreconfig.h"
89
#include "core/gimpdrawable.h"
90
#include "core/gimpenvirontable.h"
91
#include "core/gimpimage.h"
92

93
#include "gui/plug-in-menus.h"
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
#include "appenv.h"
108
#include "app_procs.h"
Elliot Lee's avatar
Elliot Lee committed
109
110
#include "gimprc.h"

111
112
#include "libgimp/gimpintl.h"

113

114
typedef struct _PlugInBlocked PlugInBlocked;
Elliot Lee's avatar
Elliot Lee committed
115
116
117
118

struct _PlugInBlocked
{
  PlugIn *plug_in;
119
  gchar  *proc_name;
Elliot Lee's avatar
Elliot Lee committed
120
121
122
};


123
124
125
126
127
128
typedef struct _PlugInHelpPathDef PlugInHelpPathDef;

struct _PlugInHelpPathDef
{
  gchar *prog_name;
  gchar *help_path;
129
130
131
};


132
static gboolean plug_in_write             (GIOChannel	     *channel,
Elliot Lee's avatar
Elliot Lee committed
133
					   guint8            *buf,
134
135
136
137
				           gulong             count,
                                           gpointer           user_data);
static gboolean plug_in_flush             (GIOChannel        *channel,
                                           gpointer           user_data);
138
139
static void     plug_in_push              (PlugIn            *plug_in);
static void     plug_in_pop               (void);
Tor Lillqvist's avatar
Tor Lillqvist committed
140
141
142
static gboolean plug_in_recv_message	  (GIOChannel	     *channel,
					   GIOCondition	      cond,
					   gpointer	      data);
143
144
145
146
147
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);
148
static void plug_in_handle_proc_run       (PlugIn            *plug_in,
149
                                           GPProcRun         *proc_run);
150
static void plug_in_handle_proc_return    (PlugIn            *plug_in,
151
                                           GPProcReturn      *proc_return);
152
static void plug_in_handle_proc_install   (PlugIn            *plug_in,
153
                                           GPProcInstall     *proc_install);
154
static void plug_in_handle_proc_uninstall (PlugIn            *plug_in,
155
                                           GPProcUninstall   *proc_uninstall);
Michael Natterer's avatar
Michael Natterer committed
156
static void plug_in_handle_extension_ack  (PlugIn            *plug_in);
157
static void plug_in_handle_has_init       (PlugIn            *plug_in);
Elliot Lee's avatar
Elliot Lee committed
158

159
160
161
162
163
static Argument * plug_in_temp_run       (ProcRecord *proc_rec,
					  Argument   *args,
					  gint        argc);
static void       plug_in_init_shm       (void);

164
165
static gchar    * plug_in_search_in_path (gchar      *search_path,
					  gchar      *filename);
166

167

168
169
PlugIn     *current_plug_in    = NULL;
ProcRecord *last_plug_in       = NULL;
Elliot Lee's avatar
Elliot Lee committed
170

171
172
static GSList     *open_plug_ins    = NULL;
static GSList     *blocked_plug_ins = NULL;
173
174
175
176

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
177

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

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

185
186
187
188
189
190
191
192
193
194

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
195
196
197
198
  shm_ID = shmget (IPC_PRIVATE,
                   TILE_WIDTH * TILE_HEIGHT * 4,
                   IPC_CREAT | 0600);

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

231
232
233
234
235
236
237
238
239
  /* 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,
240
					  FILE_MAP_ALL_ACCESS,
241
242
243
244
245
					  0, 0, tileByteSize);
      
      /* Verify that we mapped our view */
      if (shm_addr)
	shm_ID = pid;
246
247
248
249
      else
	{
	  g_warning ("MapViewOfFile error: %d... disabling shared memory transport\n", GetLastError());
	}
250
251
252
253
254
255
256
257
258
    }
  else
    {
      g_warning ("CreateFileMapping error: %d... disabling shared memory transport\n", GetLastError());
    }
#endif
#endif
}

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

Elliot Lee's avatar
Elliot Lee committed
264
265
266
267
268
269
270
271
272
273
274
275
  /* 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.
   */
  if (use_shm)
276
    plug_in_init_shm ();
Elliot Lee's avatar
Elliot Lee committed
277
278
279
}

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

      plug_in_destroy (plug_in);
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

395
396
  g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL);

397
  if (! g_path_is_absolute (name))
Elliot Lee's avatar
Elliot Lee committed
398
    {
399
      path = plug_in_search_in_path (gimp->config->plug_in_path, name);
400
401

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

412
413
414
  plug_in = g_new0 (PlugIn, 1);

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

416
417
  plug_in->open               = FALSE;
  plug_in->query              = FALSE;
418
  plug_in->init               = FALSE;
419
420
421
422
423
424
  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");
425
426
  plug_in->args[2]            = NULL;
  plug_in->args[3]            = NULL;
427
428
429
430
431
432
433
434
  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
435
  plug_in->write_buffer_index = 0;
436
437
438
  plug_in->temp_proc_defs     = NULL;
  plug_in->progress           = NULL;
  plug_in->user_data          = NULL;
Elliot Lee's avatar
Elliot Lee committed
439
440
441
442
443
444
445
446
447

  return plug_in;
}

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

      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]);

464
      if (plug_in->progress)
465
	plug_in_progress_end (plug_in);
466

Elliot Lee's avatar
Elliot Lee committed
467
468
469
      if (plug_in == current_plug_in)
	plug_in_pop ();

470
      g_free (plug_in);
Elliot Lee's avatar
Elliot Lee committed
471
472
473
    }
}

Tor Lillqvist's avatar
Tor Lillqvist committed
474
475
476
477
478
#ifdef G_OS_WIN32
/* The Microsoft _spawnv() does not allow to run scripts. But
 * this is essential to get scripting extension up and running.
 * Following the replacement function xspawnv().
 */
479
480
481
gint
xspawnv (gint                mode,
	 const gchar        *cmdname,
482
	 const gchar *const *argv)
Tor Lillqvist's avatar
Tor Lillqvist committed
483
{
484
485
486
487
  gchar sExecutable[_MAX_PATH*2];
  gchar** sArgsList;
  gchar sCmndLine[1024];
  gchar* sPath;
Tor Lillqvist's avatar
Tor Lillqvist committed
488
  HINSTANCE hInst;
489
490
  gint i;
  gint pid;
Tor Lillqvist's avatar
Tor Lillqvist committed
491
492

  /* only use it if _spawnv fails */
493
  pid = _spawnv (mode, cmdname, argv);
Tor Lillqvist's avatar
Tor Lillqvist committed
494
495
496
497
498
499
  if (pid != -1) return pid;

  /* stuff parameters into one cmndline */
  sCmndLine[0] = 0;
  for (i = 1; argv[i] != NULL; i++)
    {
500
501
       strcat (sCmndLine, argv[i]);
       strcat (sCmndLine, " ");
Tor Lillqvist's avatar
Tor Lillqvist committed
502
503
    }
  /* remove last blank */
504
  sCmndLine[strlen (sCmndLine)-1] = 0;
Tor Lillqvist's avatar
Tor Lillqvist committed
505
506

  /* do we really need _spawnv (ShelExecute seems not to do it)*/
507
508
509
  if (32 <= (int) FindExecutable (cmdname, 
				  gimp_directory (),
				  sExecutable))
Tor Lillqvist's avatar
Tor Lillqvist committed
510
    {
511
      /* g_print("_spawnlp %s %s %s", sExecutable, cmdname, sCmndLine); */
Tor Lillqvist's avatar
Tor Lillqvist committed
512
      
513
      pid = _spawnlp (mode, sExecutable, "-c", cmdname, sCmndLine, NULL);
Tor Lillqvist's avatar
Tor Lillqvist committed
514
515
516
    }
  else
    {
517
      g_warning ("Execution error for: %s", cmdname);
Tor Lillqvist's avatar
Tor Lillqvist committed
518
519
520
521
522
523
524
      return -1;
    }
  return pid;
}

#endif /* G_OS_WIN32 */

525
gboolean
Elliot Lee's avatar
Elliot Lee committed
526
527
plug_in_open (PlugIn *plug_in)
{
528
529
  gint my_read[2];
  gint my_write[2];
Elliot Lee's avatar
Elliot Lee committed
530

531
532
  g_return_val_if_fail (plug_in != NULL, FALSE);

Elliot Lee's avatar
Elliot Lee committed
533
534
535
536
537
538
  if (plug_in)
    {
      /* Open two pipes. (Bidirectional communication).
       */
      if ((pipe (my_read) == -1) || (pipe (my_write) == -1))
	{
539
	  g_message ("pipe() failed: Unable to start Plug-In \"%s\"\n(%s)",
540
541
		     g_path_get_basename (plug_in->args[0]),
		     plug_in->args[0]);
542
	  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
543
544
	}

Manish Singh's avatar
Manish Singh committed
545
#if defined(G_WITH_CYGWIN) || defined(__EMX__)
Tor Lillqvist's avatar
Tor Lillqvist committed
546
      /* Set to binary mode */
547
548
549
550
      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
551
552
#endif

553
554
555
      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
556
      plug_in->his_write = g_io_channel_unix_new (my_read[1]);
Elliot Lee's avatar
Elliot Lee committed
557

558
559
560
561
562
      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);

563
564
565
566
567
      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);

568
569
570
571
572
      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
573
574
      /* Remember the file descriptors for the pipes.
       */
575
576
577
578
      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
579
580

      /* Set the rest of the command line arguments.
581
       * FIXME: this is ugly.  Pass in the mode as a separate argument?
Elliot Lee's avatar
Elliot Lee committed
582
583
584
585
586
       */
      if (plug_in->query)
	{
	  plug_in->args[4] = g_strdup ("-query");
	}
587
588
589
590
591
      else if (plug_in->init)
	{
	  plug_in->args[4] = g_strdup ("-init");
	}
      else  
Elliot Lee's avatar
Elliot Lee committed
592
	{
593
	  plug_in->args[4] = g_strdup ("-run");
Elliot Lee's avatar
Elliot Lee committed
594
595
	}

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

Elliot Lee's avatar
Elliot Lee committed
598
599
600
601
      /* Fork another process. We'll remember the process id
       *  so that we can later use it to kill the filter if
       *  necessary.
       */
Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
602
#ifdef __EMX__
603
604
      fcntl (my_read[0], F_SETFD, 1);
      fcntl (my_write[1], F_SETFD, 1);
Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
605
#endif
Manish Singh's avatar
Manish Singh committed
606
#if defined(G_OS_WIN32) || defined (G_WITH_CYGWIN) || defined(__EMX__)
607
      plug_in->pid = xspawnv (_P_NOWAIT, plug_in->args[0], plug_in->args);
Tor Lillqvist's avatar
Tor Lillqvist committed
608
609
      if (plug_in->pid == -1)
#else
Elliot Lee's avatar
Elliot Lee committed
610
611
612
613
      plug_in->pid = fork ();

      if (plug_in->pid == 0)
	{
614
615
	  g_io_channel_unref (plug_in->my_read);
	  plug_in->my_read  = NULL;
616

617
618
619
	  g_io_channel_unref (plug_in->my_write);
	  plug_in->my_write  = NULL;

Elliot Lee's avatar
Elliot Lee committed
620
621
622
623
          /* Execute the filter. The "_exit" call should never
           *  be reached, unless some strange error condition
           *  exists.
           */
624
625
          execve (plug_in->args[0], plug_in->args,
	          gimp_environ_table_get_envp (plug_in->gimp->environ_table));
Elliot Lee's avatar
Elliot Lee committed
626
627
628
          _exit (1);
	}
      else if (plug_in->pid == -1)
Tor Lillqvist's avatar
Tor Lillqvist committed
629
#endif
Elliot Lee's avatar
Elliot Lee committed
630
	{
631
          g_message ("fork() failed: Unable to run Plug-In: \"%s\"\n(%s)",
632
633
		     g_path_get_basename (plug_in->args[0]),
		     plug_in->args[0]);
Elliot Lee's avatar
Elliot Lee committed
634
          plug_in_destroy (plug_in);
635
          return FALSE;
Elliot Lee's avatar
Elliot Lee committed
636
637
	}

Tor Lillqvist's avatar
Tor Lillqvist committed
638
639
      g_io_channel_unref (plug_in->his_read);
      plug_in->his_read  = NULL;
640

Tor Lillqvist's avatar
Tor Lillqvist committed
641
642
643
      g_io_channel_unref (plug_in->his_write);
      plug_in->his_write = NULL;

Elliot Lee's avatar
Elliot Lee committed
644
645
      if (!plug_in->synchronous)
	{
646
647
648
649
650
	  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
651
652
653
654
655

	  open_plug_ins = g_slist_prepend (open_plug_ins, plug_in);
	}

      plug_in->open = TRUE;
656
      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
657
658
    }

659
  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
660
661
662
}

void
663
664
plug_in_close (PlugIn   *plug_in,
	       gboolean  kill_it)
Elliot Lee's avatar
Elliot Lee committed
665
{
666
  gint status;
667
#ifndef G_OS_WIN32
Elliot Lee's avatar
Elliot Lee committed
668
  struct timeval tv;
Tor Lillqvist's avatar
Tor Lillqvist committed
669
#endif
Elliot Lee's avatar
Elliot Lee committed
670

671
672
  g_return_if_fail (plug_in != NULL);
  g_return_if_fail (plug_in->open == TRUE);
Elliot Lee's avatar
Elliot Lee committed
673

674
675
676
677
  if (! plug_in->open)
    return;

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

679
680
681
682
  /*  Ask the filter to exit gracefully  */
  if (kill_it && plug_in->pid)
    {
      plug_in_push (plug_in);
683
      gp_quit_write (plug_in->my_write, plug_in);
684
685
686
      plug_in_pop ();

      /*  give the plug-in some time (10 ms)  */
687
#ifndef G_OS_WIN32
688
689
690
      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
691
#else
692
      Sleep (10);
Tor Lillqvist's avatar
Tor Lillqvist committed
693
#endif
694
    }
Elliot Lee's avatar
Elliot Lee committed
695

696
  /* If necessary, kill the filter. */
697
#ifndef G_OS_WIN32
698
699
  if (kill_it && plug_in->pid)
    status = kill (plug_in->pid, SIGKILL);
Elliot Lee's avatar
Elliot Lee committed
700

701
702
703
704
705
  /* 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
706
#else
707
708
709
710
711
712
713
714
715
716
717
  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
718
	{
719
720
	  Sleep(10);
	  dwTries--;
Tor Lillqvist's avatar
Tor Lillqvist committed
721
	}
722
      if (STILL_ACTIVE == dwExitCode)
Tor Lillqvist's avatar
Tor Lillqvist committed
723
	{
724
725
	  g_warning("Terminating %s ...", plug_in->args[0]);
	  TerminateProcess ((HANDLE) plug_in->pid, 0);
Tor Lillqvist's avatar
Tor Lillqvist committed
726
	}
727
728
    }
#endif
Elliot Lee's avatar
Elliot Lee committed
729

730
  plug_in->pid = 0;
Elliot Lee's avatar
Elliot Lee committed
731

732
733
734
735
  /* Remove the input handler. */
  if (plug_in->input_id)
    {
      g_source_remove (plug_in->input_id);
Elliot Lee's avatar
Elliot Lee committed
736
      plug_in->input_id = 0;
737
738
739
740
741
742
    }

  /* Close the pipes. */
  if (plug_in->my_read != NULL)
    {
      g_io_channel_unref (plug_in->my_read);
Tor Lillqvist's avatar
Tor Lillqvist committed
743
      plug_in->my_read = NULL;
744
745
746
747
    }
  if (plug_in->my_write != NULL)
    {
      g_io_channel_unref (plug_in->my_write);
Tor Lillqvist's avatar
Tor Lillqvist committed
748
      plug_in->my_write = NULL;
749
750
751
752
    }
  if (plug_in->his_read != NULL)
    {
      g_io_channel_unref (plug_in->his_read);
Tor Lillqvist's avatar
Tor Lillqvist committed
753
      plug_in->his_read = NULL;
754
755
756
757
    }
  if (plug_in->his_write != NULL)
    {
      g_io_channel_unref (plug_in->his_write);
Tor Lillqvist's avatar
Tor Lillqvist committed
758
      plug_in->his_write = NULL;
759
    }
Elliot Lee's avatar
Elliot Lee committed
760

761
  wire_clear_error ();
Elliot Lee's avatar
Elliot Lee committed
762

763
  if (plug_in->progress)
764
    plug_in_progress_end (plug_in);
Elliot Lee's avatar
Elliot Lee committed
765

766
767
  if (plug_in->recurse)
    {
768
      gimp_main_loop_quit (plug_in->gimp);
769

770
771
      plug_in->recurse = FALSE;
    }
Elliot Lee's avatar
Elliot Lee committed
772

773
  plug_in->synchronous = FALSE;
Elliot Lee's avatar
Elliot Lee committed
774

775
776
777
778
  /* Unregister any temporary procedures. */
  if (plug_in->temp_proc_defs)
    {
      g_slist_foreach (plug_in->temp_proc_defs,
779
780
		       (GFunc) plug_ins_proc_def_remove,
		       plug_in->gimp);
781
782
783
      g_slist_foreach (plug_in->temp_proc_defs,
                       (GFunc) g_free,
                       NULL);
784
785
786
      g_slist_free (plug_in->temp_proc_defs);
      plug_in->temp_proc_defs = NULL;
    }
Elliot Lee's avatar
Elliot Lee committed
787

788
789
790
  /* Close any dialogs that this plugin might have opened */
  brush_select_dialogs_check ();
  gradient_select_dialogs_check ();
791
792
  palette_select_dialogs_check ();
  pattern_select_dialogs_check ();
793

794
  open_plug_ins = g_slist_remove (open_plug_ins, plug_in);
Elliot Lee's avatar
Elliot Lee committed
795
796
797
798
799
800
}

static Argument *
plug_in_get_current_return_vals (ProcRecord *proc_rec)
{
  Argument *return_vals;
801
  gint      nargs;
Elliot Lee's avatar
Elliot Lee committed
802
803
804
805

  /* Return the status code plus the current return values. */
  nargs = proc_rec->num_values + 1;
  if (current_return_vals && current_return_nvals == nargs)
806
807
808
    {
      return_vals = current_return_vals;
    }
Elliot Lee's avatar
Elliot Lee committed
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
  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;
831
  current_return_vals  = NULL;
Elliot Lee's avatar
Elliot Lee committed
832
833
834
835

  return return_vals;
}

836
Argument *
837
838
plug_in_run (Gimp       *gimp,
             ProcRecord *proc_rec,
Elliot Lee's avatar
Elliot Lee committed
839
	     Argument   *args,
840
841
842
843
	     gint        argc,
	     gboolean    synchronous,   
	     gboolean    destroy_values,
	     gint        gdisp_ID)
Elliot Lee's avatar
Elliot Lee committed
844
{
845
846
847
848
  GPConfig   config;
  GPProcRun  proc_run;
  Argument  *return_vals;
  PlugIn    *plug_in;
Elliot Lee's avatar
Elliot Lee committed
849
850
851

  return_vals = NULL;

852
  if (proc_rec->proc_type == GIMP_TEMPORARY)
Elliot Lee's avatar
Elliot Lee committed
853
    {
854
      return_vals = plug_in_temp_run (proc_rec, args, argc);
Elliot Lee's avatar
Elliot Lee committed
855
856
857
      goto done;
    }

858
  plug_in = plug_in_new (gimp, proc_rec->exec_method.plug_in.filename);
Elliot Lee's avatar
Elliot Lee committed
859
860
861
862
863
864
865
866
867

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

	  plug_in_push (plug_in);

868
869
870
871
	  config.version      = GP_VERSION;
	  config.tile_width   = TILE_WIDTH;
	  config.tile_height  = TILE_HEIGHT;
	  config.shm_ID       = shm_ID;
872
873
	  config.gamma        = gimprc.gamma_val;
	  config.install_cmap = gimprc.install_cmap;
874
875
          config.unused       = 0;
          config.min_colors   = CLAMP (gimprc.min_colors, 27, 256);
876
877
878
	  config.gdisp_ID     = gdisp_ID;

	  proc_run.name    = proc_rec->name;
879
	  proc_run.nparams = argc;
880
	  proc_run.params  = plug_in_args_to_params (args, argc, FALSE);
Elliot Lee's avatar
Elliot Lee committed
881

882
883
884
	  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
885
886
887
888
889
890
891
892
893
	    {
	      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);

894
895
	  /*  If this is an automatically installed extension, wait for an
	   *  installation-confirmation message
Elliot Lee's avatar
Elliot Lee committed
896
	   */
897
	  if ((proc_rec->proc_type == GIMP_EXTENSION) &&
898
	      (proc_rec->num_args == 0))
899
            {
900
              gimp_main_loop (gimp);
901
            }
Elliot Lee's avatar
Elliot Lee committed
902
903
904

	  if (plug_in->recurse)
	    {
905
              gimp_main_loop (gimp);
906

Elliot Lee's avatar
Elliot Lee committed
907
908
909
910
	      return_vals = plug_in_get_current_return_vals (proc_rec);
	    }
	}
    }
911

912
 done:
Elliot Lee's avatar
Elliot Lee committed
913
914
915
916
917
918
919
920
921
  if (return_vals && destroy_values)
    {
      procedural_db_destroy_args (return_vals, proc_rec->num_values);
      return_vals = NULL;
    }
  return return_vals;
}

void
922
923
924
925
926
plug_in_repeat (Gimp    *gimp,
                gint     display_ID,
                gint     image_ID,
                gint     drawable_ID,
                gboolean with_interface)
Elliot Lee's avatar
Elliot Lee committed
927
{
928
929
  Argument    *args;
  gint         i;
Elliot Lee's avatar
Elliot Lee committed
930
931
932
933

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

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

940
      /* initialize the first three plug-in arguments  */
941
942
      args[0].value.pdb_int = (with_interface ? 
                               GIMP_RUN_INTERACTIVE : GIMP_RUN_WITH_LAST_VALS);
943
944
      args[1].value.pdb_int = image_ID;
      args[2].value.pdb_int = drawable_ID;
Elliot Lee's avatar
Elliot Lee committed
945
946

      /* run the plug-in procedure */
947
      plug_in_run (gimp, last_plug_in, args, 3, FALSE, TRUE, display_ID);
Elliot Lee's avatar
Elliot Lee committed
948
949
950
951
952

      g_free (args);
    }
}

Tor Lillqvist's avatar
Tor Lillqvist committed
953
static gboolean
954
955
956
plug_in_recv_message (GIOChannel   *channel,
		      GIOCondition  cond,
		      gpointer	    data)
Elliot Lee's avatar
Elliot Lee committed
957
{
958
959
  PlugIn   *plug_in;
  gboolean  got_message = FALSE;
960

961
962
963
964
  plug_in = (PlugIn *) data;

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

966
  if (plug_in->my_read == NULL)
Tor Lillqvist's avatar
Tor Lillqvist committed
967
    return TRUE;
Elliot Lee's avatar
Elliot Lee committed
968

969
970
971
972
973
974
  if (cond & (G_IO_IN | G_IO_PRI))
    {
      WireMessage msg;

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

975
      if (! wire_read_msg (plug_in->my_read, &msg, plug_in))
976
	{
977
	  plug_in_close (plug_in, TRUE);
978
	}
979
      else
980
	{
981
	  plug_in_handle_message (plug_in, &msg);
982
983
984
985
986
987
	  wire_destroy (&msg);
	  got_message = TRUE;
	}
    }

  if (cond & (G_IO_ERR | G_IO_HUP))
Elliot Lee's avatar
Elliot Lee committed
988
    {
989
      if (plug_in->open)
990
	{
991
	  plug_in_close (plug_in, TRUE);
992
	}
Elliot Lee's avatar
Elliot Lee committed
993
994
    }

995
  if (! got_message)
996
997
998
999
    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."),
1000
1001
	       g_path_get_basename (plug_in->args[0]),
	       plug_in->args[0]);
1002

1003
1004
  if (! plug_in->open)
    plug_in_destroy (plug_in);
Elliot Lee's avatar
Elliot Lee committed
1005
1006
  else
    plug_in_pop ();
1007

Tor Lillqvist's avatar
Tor Lillqvist committed
1008
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
1009
1010
1011
}

static void
1012
1013
plug_in_handle_message (PlugIn