plug-in-message.c 46.3 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
#include "config/gimpguiconfig.h"
88

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

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

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

108
109
#include "libgimp/gimpintl.h"

110

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

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


120
121
122
123
124
125
typedef struct _PlugInHelpPathDef PlugInHelpPathDef;

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


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

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

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

static void       plug_in_prep_for_exec  (gpointer            data);
165

166

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

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

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
176

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

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

184
185
186
187
188
189
190
191
192
193

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

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

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

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

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

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

      plug_in_destroy (plug_in);
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

396
  if (! g_path_is_absolute (name))
Elliot Lee's avatar
Elliot Lee committed
397
    {
398
399
400
401
402
403
      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);
404
405

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

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

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

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

  return plug_in;
}

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

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

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

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

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

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

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

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

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

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

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

Manish Singh's avatar
Manish Singh committed
514
#if defined(G_WITH_CYGWIN) || defined(__EMX__)
Tor Lillqvist's avatar
Tor Lillqvist committed
515
      /* Set to binary mode */
516
517
518
519
      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
520
521
#endif

522
523
524
      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
525
      plug_in->his_write = g_io_channel_unix_new (my_read[1]);
Elliot Lee's avatar
Elliot Lee committed
526

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

532
533
534
535
536
      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);

537
538
539
540
541
      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
542
543
      /* Remember the file descriptors for the pipes.
       */
544
545
546
547
      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
548
549

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

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

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

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

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

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

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

Elliot Lee's avatar
Elliot Lee committed
600
601
      if (!plug_in->synchronous)
	{
602
603
604
605
606
	  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
607
608
609
610
611

	  open_plug_ins = g_slist_prepend (open_plug_ins, plug_in);
	}

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

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

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

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

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

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

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

      /*  give the plug-in some time (10 ms)  */
643
#ifndef G_OS_WIN32
644
645
646
      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
647
#else
648
      Sleep (10);
Tor Lillqvist's avatar
Tor Lillqvist committed
649
#endif
650
    }
Elliot Lee's avatar
Elliot Lee committed
651

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return return_vals;
}

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

  return_vals = NULL;

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

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

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

	  plug_in_push (plug_in);

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

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

838
839
840
	  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
841
842
843
844
845
846
847
848
849
	    {
	      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);

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

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

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

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

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

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

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

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

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

      g_free (args);
    }
}

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

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

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

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

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

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

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

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

951
  if (! got_message)
952
953
954
955
    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."),
956
957
	       g_path_get_basename (plug_in->args[0]),
	       plug_in->args[0]);
958

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

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

static void
968
969
plug_in_handle_message (PlugIn      *plug_in,
                        WireMessage *msg)
Elliot Lee's avatar
Elliot Lee committed
970
971
972
973
{
  switch (msg->type)
    {
    case GP_QUIT:
974
      plug_in_handle_quit (plug_in);
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_CONFIG:
978
979
      g_warning ("plug_in_handle_message(): "
		 "received a config message (should not happen)");
980
      plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
981
      break;
Michael Natterer's avatar
Michael Natterer committed
982

Elliot Lee's avatar
Elliot Lee committed
983
    case GP_TILE_REQ:
984
      plug_in_handle_tile_req (plug_in, msg->data);
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_ACK:
988
989
      g_warning ("plug_in_handle_message(): "
		 "received a tile ack 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_TILE_DATA:
994
995
      g_warning ("plug_in_handle_message(): "
		 "received a tile data message (should not happen)");
996
      plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
997
      break;
Michael Natterer's avatar
Michael Natterer committed
998

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