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

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

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

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

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

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

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

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

80
#include "plug-in-types.h"
81

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

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

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

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

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"
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;

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

      plug_in_destroy (plug_in);
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return plug_in;
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

	  open_plug_ins = g_slist_prepend (open_plug_ins, plug_in);
	}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

  return return_vals;
}

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

  return_vals = NULL;

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

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

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

	  plug_in_push (plug_in);

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

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

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

	  plug_in_pop ();

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

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

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

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

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

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

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

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

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

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

      g_free (args);
    }
}

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Elliot Lee's avatar
Elliot Lee committed
1002
    case GP_PROC_RETURN:
1003
      plug_in_handle_proc_return (plug_in, msg->data);
1004
      plug_in_close (plug_in, FALSE);
Elliot Lee's avatar
Elliot Lee committed
1005
      break;
Michael Natterer's avatar
Michael Natterer committed
1006

Elliot Lee's avatar
Elliot Lee committed
1007
    case GP_TEMP_PROC_RUN:
1008 1009
      g_warning ("plug_in_handle_message(): "
		 "received a temp proc run message (should not happen)");
1010
      plug_in_close (plug_in, TRUE);
Elliot Lee's avatar
Elliot Lee committed
1011
      break;
Michael Natterer's avatar
Michael Natterer committed
1012

Elliot Lee's avatar
Elliot Lee committed
1013
    case GP_TEMP_PROC_RETURN:
1014
      plug_in_handle_proc_return (plug_in, msg->data);
1015
      gimp_main_loop_quit (plug_in->gimp);
Elliot Lee's avatar
Elliot Lee committed
1016
      break;
Michael Natterer's avatar
Michael Natterer committed
1017

Elliot Lee's avatar
Elliot Lee committed
1018
    case GP_PROC_INSTALL:
1019
      plug_in_handle_proc_install (plug_in, msg->data);
Elliot Lee's avatar
Elliot Lee committed
1020
      break;
Michael Natterer's avatar
Michael Natterer committed
1021

Elliot Lee's avatar
Elliot Lee committed
1022
    case GP_PROC_UNINSTALL: