plug-in-message.c 47.1 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
86
#include "config/gimpcoreconfig.h"

87
#include "core/gimp.h"
88
#include "core/gimpcontext.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
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
      path = plug_in_search_in_path (gimp->config->plug_in_path, name);
398
399

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

410
411
412
  plug_in = g_new0 (PlugIn, 1);

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

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

  return plug_in;
}

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

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

462
      if (plug_in->progress)
463
	plug_in_progress_end (plug_in);
464

Elliot Lee's avatar
Elliot Lee committed
465
466
467
      if (plug_in == current_plug_in)
	plug_in_pop ();

468
      g_free (plug_in);
Elliot Lee's avatar
Elliot Lee committed
469
470
471
    }
}

472
473
static void
plug_in_prep_for_exec (gpointer data)
Tor Lillqvist's avatar
Tor Lillqvist committed
474
{
475
476
#if !defined(G_OS_WIN32) && !defined (G_WITH_CYGWIN) && !defined(__EMX__)
  PlugIn *plug_in = data;
Tor Lillqvist's avatar
Tor Lillqvist committed
477

478
479
  g_io_channel_unref (plug_in->my_read);
  plug_in->my_read  = NULL;
Tor Lillqvist's avatar
Tor Lillqvist committed
480

481
482
483
484
  g_io_channel_unref (plug_in->my_write);
  plug_in->my_write  = NULL;
#endif
}
Tor Lillqvist's avatar
Tor Lillqvist committed
485

486
gboolean
Elliot Lee's avatar
Elliot Lee committed
487
488
plug_in_open (PlugIn *plug_in)
{
489
490
  gint my_read[2];
  gint my_write[2];
491
492
  gchar **envp;
  GError *error = NULL;
Elliot Lee's avatar
Elliot Lee committed
493

494
495
  g_return_val_if_fail (plug_in != NULL, FALSE);

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

Manish Singh's avatar
Manish Singh committed
508
#if defined(G_WITH_CYGWIN) || defined(__EMX__)
Tor Lillqvist's avatar
Tor Lillqvist committed
509
      /* Set to binary mode */
510
511
512
513
      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
514
515
#endif

516
517
518
      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
519
      plug_in->his_write = g_io_channel_unix_new (my_read[1]);
Elliot Lee's avatar
Elliot Lee committed
520

521
522
523
524
525
      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);

526
527
528
529
530
      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);

531
532
533
534
535
      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
536
537
      /* Remember the file descriptors for the pipes.
       */
538
539
540
541
      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
542
543

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

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

Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
561
#ifdef __EMX__
562
563
      fcntl (my_read[0], F_SETFD, 1);
      fcntl (my_write[1], F_SETFD, 1);
Asbjørn Pettersen's avatar
Asbjørn Pettersen committed
564
#endif
Elliot Lee's avatar
Elliot Lee committed
565

566
567
568
569
570
571
572
573
574
575
576
      /* 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
577
	{
578
          g_message ("Unable to run Plug-In: \"%s\"\n(%s)\n%s",
579
		     g_path_get_basename (plug_in->args[0]),
580
581
582
583
		     plug_in->args[0],
		     error->message);
          g_error_free (error);

Elliot Lee's avatar
Elliot Lee committed
584
          plug_in_destroy (plug_in);
585
          return FALSE;
Elliot Lee's avatar
Elliot Lee committed
586
587
	}

Tor Lillqvist's avatar
Tor Lillqvist committed
588
589
      g_io_channel_unref (plug_in->his_read);
      plug_in->his_read  = NULL;
590

Tor Lillqvist's avatar
Tor Lillqvist committed
591
592
593
      g_io_channel_unref (plug_in->his_write);
      plug_in->his_write = NULL;

Elliot Lee's avatar
Elliot Lee committed
594
595
      if (!plug_in->synchronous)
	{
596
597
598
599
600
	  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
601
602
603
604
605

	  open_plug_ins = g_slist_prepend (open_plug_ins, plug_in);
	}

      plug_in->open = TRUE;
606
      return TRUE;
Elliot Lee's avatar
Elliot Lee committed
607
608
    }

609
  return FALSE;
Elliot Lee's avatar
Elliot Lee committed
610
611
612
}

void
613
614
plug_in_close (PlugIn   *plug_in,
	       gboolean  kill_it)
Elliot Lee's avatar
Elliot Lee committed
615
{
616
  gint status;
617
#ifndef G_OS_WIN32
Elliot Lee's avatar
Elliot Lee committed
618
  struct timeval tv;
Tor Lillqvist's avatar
Tor Lillqvist committed
619
#endif
Elliot Lee's avatar
Elliot Lee committed
620

621
622
  g_return_if_fail (plug_in != NULL);
  g_return_if_fail (plug_in->open == TRUE);
Elliot Lee's avatar
Elliot Lee committed
623

624
625
626
627
  if (! plug_in->open)
    return;

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

629
630
631
632
  /*  Ask the filter to exit gracefully  */
  if (kill_it && plug_in->pid)
    {
      plug_in_push (plug_in);
633
      gp_quit_write (plug_in->my_write, plug_in);
634
635
636
      plug_in_pop ();

      /*  give the plug-in some time (10 ms)  */
637
#ifndef G_OS_WIN32
638
639
640
      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
641
#else
642
      Sleep (10);
Tor Lillqvist's avatar
Tor Lillqvist committed
643
#endif
644
    }
Elliot Lee's avatar
Elliot Lee committed
645

646
  /* If necessary, kill the filter. */
647
#ifndef G_OS_WIN32
648
649
  if (kill_it && plug_in->pid)
    status = kill (plug_in->pid, SIGKILL);
Elliot Lee's avatar
Elliot Lee committed
650

651
652
653
654
655
  /* 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
656
#else
657
658
659
660
661
662
663
664
665
666
667
  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
668
	{
669
670
	  Sleep(10);
	  dwTries--;
Tor Lillqvist's avatar
Tor Lillqvist committed
671
	}
672
      if (STILL_ACTIVE == dwExitCode)
Tor Lillqvist's avatar
Tor Lillqvist committed
673
	{
674
675
	  g_warning("Terminating %s ...", plug_in->args[0]);
	  TerminateProcess ((HANDLE) plug_in->pid, 0);
Tor Lillqvist's avatar
Tor Lillqvist committed
676
	}
677
678
    }
#endif
Elliot Lee's avatar
Elliot Lee committed
679

680
  plug_in->pid = 0;
Elliot Lee's avatar
Elliot Lee committed
681

682
683
684
685
  /* Remove the input handler. */
  if (plug_in->input_id)
    {
      g_source_remove (plug_in->input_id);
Elliot Lee's avatar
Elliot Lee committed
686
      plug_in->input_id = 0;
687
688
689
690
691
692
    }

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

711
  wire_clear_error ();
Elliot Lee's avatar
Elliot Lee committed
712

713
  if (plug_in->progress)
714
    plug_in_progress_end (plug_in);
Elliot Lee's avatar
Elliot Lee committed
715

716
717
  if (plug_in->recurse)
    {
718
      gimp_main_loop_quit (plug_in->gimp);
719

720
721
      plug_in->recurse = FALSE;
    }
Elliot Lee's avatar
Elliot Lee committed
722

723
  plug_in->synchronous = FALSE;
Elliot Lee's avatar
Elliot Lee committed
724

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

738
739
740
  /* Close any dialogs that this plugin might have opened */
  brush_select_dialogs_check ();
  gradient_select_dialogs_check ();
741
742
  palette_select_dialogs_check ();
  pattern_select_dialogs_check ();
743

744
  open_plug_ins = g_slist_remove (open_plug_ins, plug_in);
Elliot Lee's avatar
Elliot Lee committed
745
746
747
748
749
750
}

static Argument *
plug_in_get_current_return_vals (ProcRecord *proc_rec)
{
  Argument *return_vals;
751
  gint      nargs;
Elliot Lee's avatar
Elliot Lee committed
752
753
754
755

  /* Return the status code plus the current return values. */
  nargs = proc_rec->num_values + 1;
  if (current_return_vals && current_return_nvals == nargs)
756
757
758
    {
      return_vals = current_return_vals;
    }
Elliot Lee's avatar
Elliot Lee committed
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
  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;
781
  current_return_vals  = NULL;
Elliot Lee's avatar
Elliot Lee committed
782
783
784
785

  return return_vals;
}

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

  return_vals = NULL;

802
  if (proc_rec->proc_type == GIMP_TEMPORARY)
Elliot Lee's avatar
Elliot Lee committed
803
    {
804
      return_vals = plug_in_temp_run (proc_rec, args, argc);
Elliot Lee's avatar
Elliot Lee committed
805
806
807
      goto done;
    }

808
  plug_in = plug_in_new (gimp, proc_rec->exec_method.plug_in.filename);
Elliot Lee's avatar
Elliot Lee committed
809
810
811
812
813
814
815
816
817

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

	  plug_in_push (plug_in);

818
819
820
821
	  config.version      = GP_VERSION;
	  config.tile_width   = TILE_WIDTH;
	  config.tile_height  = TILE_HEIGHT;
	  config.shm_ID       = shm_ID;
822
823
	  config.gamma        = gimp->config->gamma_val;
	  config.install_cmap = gimp->config->install_cmap;
824
          config.unused       = 0;
825
          config.min_colors   = CLAMP (gimp->config->min_colors, 27, 256);
826
827
828
	  config.gdisp_ID     = gdisp_ID;

	  proc_run.name    = proc_rec->name;
829
	  proc_run.nparams = argc;
830
	  proc_run.params  = plug_in_args_to_params (args, argc, FALSE);
Elliot Lee's avatar
Elliot Lee committed
831

832
833
834
	  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
835
836
837
838
839
840
841
842
843
	    {
	      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);

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

	  if (plug_in->recurse)
	    {
855
              gimp_main_loop (gimp);
856

Elliot Lee's avatar
Elliot Lee committed
857
858
859
860
	      return_vals = plug_in_get_current_return_vals (proc_rec);
	    }
	}
    }
861

862
 done:
Elliot Lee's avatar
Elliot Lee committed
863
864
865
866
867
868
869
870
871
  if (return_vals && destroy_values)
    {
      procedural_db_destroy_args (return_vals, proc_rec->num_values);
      return_vals = NULL;
    }
  return return_vals;
}

void
872
873
874
875
876
plug_in_repeat (Gimp    *gimp,
                gint     display_ID,
                gint     image_ID,
                gint     drawable_ID,
                gboolean with_interface)
Elliot Lee's avatar
Elliot Lee committed
877
{
878
879
  Argument    *args;
  gint         i;
Elliot Lee's avatar
Elliot Lee committed
880
881
882
883

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

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

890
      /* initialize the first three plug-in arguments  */
891
892
      args[0].value.pdb_int = (with_interface ? 
                               GIMP_RUN_INTERACTIVE : GIMP_RUN_WITH_LAST_VALS);
893
894
      args[1].value.pdb_int = image_ID;
      args[2].value.pdb_int = drawable_ID;
Elliot Lee's avatar
Elliot Lee committed
895
896

      /* run the plug-in procedure */
897
      plug_in_run (gimp, last_plug_in, args, 3, FALSE, TRUE, display_ID);
Elliot Lee's avatar
Elliot Lee committed
898
899
900
901
902

      g_free (args);
    }
}

Tor Lillqvist's avatar
Tor Lillqvist committed
903
static gboolean
904
905
906
plug_in_recv_message (GIOChannel   *channel,
		      GIOCondition  cond,
		      gpointer	    data)
Elliot Lee's avatar
Elliot Lee committed
907
{
908
909
  PlugIn   *plug_in;
  gboolean  got_message = FALSE;
910

911
912
913
914
  plug_in = (PlugIn *) data;

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

916
  if (plug_in->my_read == NULL)
Tor Lillqvist's avatar
Tor Lillqvist committed
917
    return TRUE;
Elliot Lee's avatar
Elliot Lee committed
918

919
920
921
922
923
924
  if (cond & (G_IO_IN | G_IO_PRI))
    {
      WireMessage msg;

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

925
      if (! wire_read_msg (plug_in->my_read, &msg, plug_in))
926
	{
927
	  plug_in_close (plug_in, TRUE);
928
	}
929
      else
930
	{
931
	  plug_in_handle_message (plug_in, &msg);
932
933
934
935
936
937
	  wire_destroy (&msg);
	  got_message = TRUE;
	}
    }

  if (cond & (G_IO_ERR | G_IO_HUP))
Elliot Lee's avatar
Elliot Lee committed
938
    {
939
      if (plug_in->open)
940
	{
941
	  plug_in_close (plug_in, TRUE);
942
	}
Elliot Lee's avatar
Elliot Lee committed
943
944
    }

945
  if (! got_message)
946
947
948
949
    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."),
950
951
	       g_path_get_basename (plug_in->args[0]),
	       plug_in->args[0]);
952

953
954
  if (! plug_in->open)
    plug_in_destroy (plug_in);
Elliot Lee's avatar
Elliot Lee committed
955
956
  else
    plug_in_pop ();
957

Tor Lillqvist's avatar
Tor Lillqvist committed
958
  return TRUE;
Elliot Lee's avatar
Elliot Lee committed
959
960
961
}

static void
962
963
plug_in_handle_message (PlugIn      *plug_in,
                        WireMessage *msg)
Elliot Lee's avatar
Elliot Lee committed
964
965
966
967
{
  switch (msg->type)
    {
    case GP_QUIT:
968
      plug_in_handle_quit (plug_in);
Elliot Lee's avatar
Elliot Lee committed
969
      break;
Michael Natterer's avatar
Michael Natterer committed
970

Elliot Lee's avatar
Elliot Lee committed
971
    case GP_CONFIG:
972
973
      g_warning ("plug_in_handle_message(): "
		 "received a config message (should not happen)");
974
      plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
975
      break;
Michael Natterer's avatar
Michael Natterer committed
976

Elliot Lee's avatar
Elliot Lee committed
977
    case GP_TILE_REQ:
978
      plug_in_handle_tile_req (plug_in, msg->data);
Elliot Lee's avatar
Elliot Lee committed
979
      break;
Michael Natterer's avatar
Michael Natterer committed
980

Elliot Lee's avatar
Elliot Lee committed
981
    case GP_TILE_ACK:
982
983
      g_warning ("plug_in_handle_message(): "
		 "received a tile ack message (should not happen)");
984
      plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
985
      break;
Michael Natterer's avatar
Michael Natterer committed
986

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

Elliot Lee's avatar
Elliot Lee committed
993
    case GP_PROC_RUN:
994
      plug_in_handle_proc_run (plug_in, msg->data);
Elliot Lee's avatar
Elliot Lee committed
995
      break;
Michael Natterer's avatar
Michael Natterer committed
996

Elliot Lee's avatar
Elliot Lee committed
997
    case GP_PROC_RETURN:
998
      plug_in_handle_proc_return (plug_in, msg->