diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 16:23:22 +0000 |
commit | e42129241681dde7adae7d20697e7b421682fbb4 (patch) | |
tree | af1fe815a5e639e68e59fabd8395ec69458b3e5e /libgimp/gimp.c | |
parent | Initial commit. (diff) | |
download | gimp-upstream.tar.xz gimp-upstream.zip |
Adding upstream version 2.10.22.upstream/2.10.22upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'libgimp/gimp.c')
-rw-r--r-- | libgimp/gimp.c | 2588 |
1 files changed, 2588 insertions, 0 deletions
diff --git a/libgimp/gimp.c b/libgimp/gimp.c new file mode 100644 index 0000000..f0734ff --- /dev/null +++ b/libgimp/gimp.c @@ -0,0 +1,2588 @@ +/* LIBGIMP - The GIMP Library + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * gimp.c + * + * This library is free software: you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 3 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * <https://www.gnu.org/licenses/>. + */ + +#include "config.h" + +#define _GNU_SOURCE /* for the sigaction stuff */ + +#include <errno.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/types.h> + +#ifdef HAVE_SYS_TIME_H +#include <sys/time.h> +#endif + +#ifdef HAVE_SYS_PARAM_H +#include <sys/param.h> +#endif + +#ifdef HAVE_SYS_WAIT_H +#include <sys/wait.h> +#endif + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#ifndef WAIT_ANY +#define WAIT_ANY -1 +#endif + +#include <gtk/gtk.h> /* need GDK_WINDOWING_FOO defines */ + +#ifndef G_OS_WIN32 +#include "libgimpbase/gimpsignal.h" + +#else + +#ifdef HAVE_EXCHNDL +#include <time.h> +#include <exchndl.h> +#endif + +#include <signal.h> +#endif + +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif + +#if defined(USE_SYSV_SHM) + +#ifdef HAVE_IPC_H +#include <sys/ipc.h> +#endif + +#ifdef HAVE_SHM_H +#include <sys/shm.h> +#endif + +#elif defined(USE_POSIX_SHM) + +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif + +#include <fcntl.h> +#include <sys/mman.h> + +#endif /* USE_POSIX_SHM */ + +#ifdef GDK_WINDOWING_QUARTZ +#include <Cocoa/Cocoa.h> +#endif + +#if defined(G_OS_WIN32) || defined(G_WITH_CYGWIN) +# ifdef STRICT +# undef STRICT +# endif +# define STRICT + +# ifdef _WIN32_WINNT +# undef _WIN32_WINNT +# endif +# define _WIN32_WINNT 0x0601 + +# include <windows.h> +# include <tlhelp32.h> +# undef RGB +# define USE_WIN32_SHM 1 +#endif + +#include <locale.h> + +#include "libgimpbase/gimpbase.h" +#include "libgimpbase/gimpbase-private.h" +#include "libgimpbase/gimpprotocol.h" +#include "libgimpbase/gimpwire.h" + +#include "gimp.h" +#include "gimpunitcache.h" + +#include "libgimp-intl.h" + + +/** + * SECTION: gimp + * @title: Gimp + * @short_description: Main functions needed for building a GIMP plug-in. + * This header includes all other GIMP Library headers. + * + * Main functions needed for building a GIMP plug-in. This header + * includes all other GIMP Library headers. + **/ + + +#define TILE_MAP_SIZE (_tile_width * _tile_height * 32) + +#define ERRMSG_SHM_FAILED "Could not attach to gimp shared memory segment" + +/* Maybe this should go in a public header if we add other things to it */ +typedef enum +{ + GIMP_DEBUG_PID = 1 << 0, + GIMP_DEBUG_FATAL_WARNINGS = 1 << 1, + GIMP_DEBUG_QUERY = 1 << 2, + GIMP_DEBUG_INIT = 1 << 3, + GIMP_DEBUG_RUN = 1 << 4, + GIMP_DEBUG_QUIT = 1 << 5, + + GIMP_DEBUG_DEFAULT = (GIMP_DEBUG_RUN | GIMP_DEBUG_FATAL_WARNINGS) +} GimpDebugFlag; + +#define WRITE_BUFFER_SIZE 1024 + +void gimp_read_expect_msg (GimpWireMessage *msg, + gint type); + + +static void gimp_close (void); +static void gimp_debug_stop (void); +static void gimp_message_func (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer data); +static void gimp_fatal_func (const gchar *log_domain, + GLogLevelFlags flags, + const gchar *message, + gpointer data); +#ifdef G_OS_WIN32 +#ifdef HAVE_EXCHNDL +static LONG WINAPI gimp_plugin_sigfatal_handler (PEXCEPTION_POINTERS pExceptionInfo); +#endif +#else +static void gimp_plugin_sigfatal_handler (gint sig_num); +#endif +static gboolean gimp_plugin_io_error_handler (GIOChannel *channel, + GIOCondition cond, + gpointer data); +static gboolean gimp_write (GIOChannel *channel, + const guint8 *buf, + gulong count, + gpointer user_data); +static gboolean gimp_flush (GIOChannel *channel, + gpointer user_data); +static void gimp_loop (void); +static void gimp_config (GPConfig *config); +static void gimp_proc_run (GPProcRun *proc_run); +static void gimp_temp_proc_run (GPProcRun *proc_run); +static void gimp_process_message (GimpWireMessage *msg); +static void gimp_single_message (void); +static gboolean gimp_extension_read (GIOChannel *channel, + GIOCondition condition, + gpointer data); + +static void gimp_set_pdb_error (const GimpParam *return_vals, + gint n_return_vals); + + +#if defined G_OS_WIN32 && defined HAVE_EXCHNDL +static LPTOP_LEVEL_EXCEPTION_FILTER _prevExceptionFilter = NULL; +static gchar *plug_in_backtrace_path = NULL; +#endif + +static GIOChannel *_readchannel = NULL; +GIOChannel *_writechannel = NULL; + +#ifdef USE_WIN32_SHM +static HANDLE shm_handle; +#endif + +static gint _tile_width = -1; +static gint _tile_height = -1; +static gint _shm_ID = -1; +static guchar *_shm_addr = NULL; +static gboolean _show_tool_tips = TRUE; +static gboolean _show_help_button = TRUE; +static gboolean _export_profile = FALSE; +static gboolean _export_exif = FALSE; +static gboolean _export_xmp = FALSE; +static gboolean _export_iptc = FALSE; +static GimpCheckSize _check_size = GIMP_CHECK_SIZE_MEDIUM_CHECKS; +static GimpCheckType _check_type = GIMP_CHECK_TYPE_GRAY_CHECKS; +static gint _min_colors = 144; +static gint _gdisp_ID = -1; +static gchar *_wm_class = NULL; +static gchar *_display_name = NULL; +static gint _monitor_number = 0; +static guint32 _timestamp = 0; +static gchar *_icon_theme_dir = NULL; +static const gchar *progname = NULL; + +static gchar write_buffer[WRITE_BUFFER_SIZE]; +static gulong write_buffer_index = 0; + +static GimpStackTraceMode stack_trace_mode = GIMP_STACK_TRACE_NEVER; + +static GHashTable *temp_proc_ht = NULL; + +static guint gimp_debug_flags = 0; + +static const GDebugKey gimp_debug_keys[] = +{ + { "pid", GIMP_DEBUG_PID }, + { "fatal-warnings", GIMP_DEBUG_FATAL_WARNINGS }, + { "fw", GIMP_DEBUG_FATAL_WARNINGS }, + { "query", GIMP_DEBUG_QUERY }, + { "init", GIMP_DEBUG_INIT }, + { "run", GIMP_DEBUG_RUN }, + { "quit", GIMP_DEBUG_QUIT }, + { "on", GIMP_DEBUG_DEFAULT } +}; + +static GimpPlugInInfo PLUG_IN_INFO; + + +static GimpPDBStatusType pdb_error_status = GIMP_PDB_SUCCESS; +static gchar *pdb_error_message = NULL; + + +/** + * gimp_main: + * @info: the PLUG_IN_INFO structure + * @argc: the number of arguments + * @argv: the arguments + * + * The main procedure that must be called with the PLUG_IN_INFO structure + * and the 'argc' and 'argv' that are passed to "main". + * + * Returns: an exit status as defined by the C library, + * on success EXIT_SUCCESS. + **/ +gint +gimp_main (const GimpPlugInInfo *info, + gint argc, + gchar *argv[]) +{ + enum + { + ARG_PROGNAME, + ARG_GIMP, + ARG_READ_FD, + ARG_WRITE_FD, + ARG_MODE, + ARG_STACK_TRACE_MODE, + + N_ARGS + }; + + gchar *basename; + const gchar *env_string; + gchar *debug_string; + +#ifdef G_OS_WIN32 + gint i, j, k; + + /* Reduce risks */ + { + typedef BOOL (WINAPI *t_SetDllDirectoryA) (LPCSTR lpPathName); + t_SetDllDirectoryA p_SetDllDirectoryA; + + p_SetDllDirectoryA = (t_SetDllDirectoryA) GetProcAddress (GetModuleHandle ("kernel32.dll"), + "SetDllDirectoryA"); + if (p_SetDllDirectoryA) + (*p_SetDllDirectoryA) (""); + } + + /* On Windows, set DLL search path to $INSTALLDIR/bin so that GEGL + file operations can find their respective file library DLLs (such + as jasper, etc.) without needing to set external PATH. */ + { + const gchar *install_dir; + gchar *bin_dir; + LPWSTR w_bin_dir; + int n; + + w_bin_dir = NULL; + install_dir = gimp_installation_directory (); + bin_dir = g_build_filename (install_dir, "bin", NULL); + + n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + bin_dir, -1, NULL, 0); + if (n == 0) + goto out; + + w_bin_dir = g_malloc_n (n + 1, sizeof (wchar_t)); + n = MultiByteToWideChar (CP_UTF8, MB_ERR_INVALID_CHARS, + bin_dir, -1, + w_bin_dir, (n + 1) * sizeof (wchar_t)); + if (n == 0) + goto out; + + SetDllDirectoryW (w_bin_dir); + + out: + if (w_bin_dir) + g_free (w_bin_dir); + g_free (bin_dir); + } + +#ifdef HAVE_EXCHNDL + /* Use Dr. Mingw (dumps backtrace on crash) if it is available. */ + { + time_t t; + gchar *filename; + gchar *dir; + + /* This has to be the non-roaming directory (i.e., the local + directory) as backtraces correspond to the binaries on this + system. */ + dir = g_build_filename (g_get_user_data_dir (), + GIMPDIR, GIMP_USER_VERSION, "CrashLog", + NULL); + /* Ensure the path exists. */ + g_mkdir_with_parents (dir, 0700); + + time (&t); + filename = g_strdup_printf ("%s-crash-%" G_GUINT64_FORMAT ".txt", + g_get_prgname(), t); + plug_in_backtrace_path = g_build_filename (dir, filename, NULL); + g_free (filename); + g_free (dir); + + /* Similar to core crash handling in app/signals.c, the order here + * is very important! + */ + if (! _prevExceptionFilter) + _prevExceptionFilter = SetUnhandledExceptionFilter (gimp_plugin_sigfatal_handler); + + ExcHndlInit (); + ExcHndlSetLogFileNameA (plug_in_backtrace_path); + } +#endif + +#ifndef _WIN64 + { + typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags); + t_SetProcessDEPPolicy p_SetProcessDEPPolicy; + + p_SetProcessDEPPolicy = GetProcAddress (GetModuleHandle ("kernel32.dll"), + "SetProcessDEPPolicy"); + if (p_SetProcessDEPPolicy) + (*p_SetProcessDEPPolicy) (PROCESS_DEP_ENABLE|PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION); + } +#endif + + /* Group all our windows together on the taskbar */ + { + typedef HRESULT (WINAPI *t_SetCurrentProcessExplicitAppUserModelID) (PCWSTR lpPathName); + t_SetCurrentProcessExplicitAppUserModelID p_SetCurrentProcessExplicitAppUserModelID; + + p_SetCurrentProcessExplicitAppUserModelID = (t_SetCurrentProcessExplicitAppUserModelID) GetProcAddress (GetModuleHandle ("shell32.dll"), + "SetCurrentProcessExplicitAppUserModelID"); + if (p_SetCurrentProcessExplicitAppUserModelID) + (*p_SetCurrentProcessExplicitAppUserModelID) (L"gimp.GimpApplication"); + } + + /* Check for exe file name with spaces in the path having been split up + * by buggy NT C runtime, or something. I don't know why this happens + * on NT (including w2k), but not on w95/98. + */ + + for (i = 1; i < argc; i++) + { + k = strlen (argv[i]); + + if (k > 10) + { + if (g_ascii_strcasecmp (argv[i] + k - 4, ".exe") == 0) + { + /* Found the end of the executable name, most probably. + * Splice the parts of the name back together. + */ + GString *s; + + s = g_string_new (argv[ARG_PROGNAME]); + + for (j = 1; j <= i; j++) + { + s = g_string_append_c (s, ' '); + s = g_string_append (s, argv[j]); + } + + argv[ARG_PROGNAME] = s->str; + + /* Move rest of argv down */ + for (j = 1; j < argc - i; j++) + argv[j] = argv[j + i]; + + argv[argc - i] = NULL; + argc -= i; + + break; + } + } + } +#endif + + g_assert (info != NULL); + + PLUG_IN_INFO = *info; + + if ((argc != N_ARGS) || (strcmp (argv[ARG_GIMP], "-gimp") != 0)) + { + g_printerr ("%s is a GIMP plug-in and must be run by GIMP to be used\n", + argv[ARG_PROGNAME]); + return 1; + } + + gimp_env_init (TRUE); + + progname = argv[ARG_PROGNAME]; + + basename = g_path_get_basename (progname); + + g_set_prgname (basename); + + env_string = g_getenv ("GIMP_PLUGIN_DEBUG"); + + if (env_string) + { + const gchar *debug_messages; + + debug_string = strchr (env_string, ','); + + if (debug_string) + { + gint len = debug_string - env_string; + + if ((strlen (basename) == len) && + (strncmp (basename, env_string, len) == 0)) + { + gimp_debug_flags = + g_parse_debug_string (debug_string + 1, + gimp_debug_keys, + G_N_ELEMENTS (gimp_debug_keys)); + } + } + else if (strcmp (env_string, basename) == 0) + { + gimp_debug_flags = GIMP_DEBUG_DEFAULT; + } + + /* make debug output visible by setting G_MESSAGES_DEBUG */ + debug_messages = g_getenv ("G_MESSAGES_DEBUG"); + + if (debug_messages) + { + gchar *tmp = g_strconcat (debug_messages, ",LibGimp", NULL); + g_setenv ("G_MESSAGES_DEBUG", tmp, TRUE); + g_free (tmp); + } + else + { + g_setenv ("G_MESSAGES_DEBUG", "LibGimp", TRUE); + } + } + + g_free (basename); + + stack_trace_mode = (GimpStackTraceMode) CLAMP (atoi (argv[ARG_STACK_TRACE_MODE]), + GIMP_STACK_TRACE_NEVER, + GIMP_STACK_TRACE_ALWAYS); + +#ifndef G_OS_WIN32 + /* No use catching these on Win32, the user won't get any meaningful + * stack trace from glib anyhow. It's better to let Windows inform + * about the program error, and offer debugging if the plug-in + * has been built with MSVC, and the user has MSVC installed. + */ + gimp_signal_private (SIGHUP, gimp_plugin_sigfatal_handler, 0); + gimp_signal_private (SIGINT, gimp_plugin_sigfatal_handler, 0); + gimp_signal_private (SIGQUIT, gimp_plugin_sigfatal_handler, 0); + gimp_signal_private (SIGTERM, gimp_plugin_sigfatal_handler, 0); + + gimp_signal_private (SIGABRT, gimp_plugin_sigfatal_handler, 0); + gimp_signal_private (SIGBUS, gimp_plugin_sigfatal_handler, 0); + gimp_signal_private (SIGSEGV, gimp_plugin_sigfatal_handler, 0); + gimp_signal_private (SIGFPE, gimp_plugin_sigfatal_handler, 0); + + /* Ignore SIGPIPE from crashing Gimp */ + gimp_signal_private (SIGPIPE, SIG_IGN, 0); + + /* Restart syscalls interrupted by SIGCHLD */ + gimp_signal_private (SIGCHLD, SIG_DFL, SA_RESTART); +#endif + +#ifdef G_OS_WIN32 + _readchannel = g_io_channel_win32_new_fd (atoi (argv[ARG_READ_FD])); + _writechannel = g_io_channel_win32_new_fd (atoi (argv[ARG_WRITE_FD])); +#else + _readchannel = g_io_channel_unix_new (atoi (argv[ARG_READ_FD])); + _writechannel = g_io_channel_unix_new (atoi (argv[ARG_WRITE_FD])); +#endif + + g_io_channel_set_encoding (_readchannel, NULL, NULL); + g_io_channel_set_encoding (_writechannel, NULL, NULL); + + g_io_channel_set_buffered (_readchannel, FALSE); + g_io_channel_set_buffered (_writechannel, FALSE); + + g_io_channel_set_close_on_unref (_readchannel, TRUE); + g_io_channel_set_close_on_unref (_writechannel, TRUE); + + gp_init (); + + gimp_wire_set_writer (gimp_write); + gimp_wire_set_flusher (gimp_flush); + + gimp_enums_init (); + + /* initialize units */ + { + GimpUnitVtable vtable; + + vtable.unit_get_number_of_units = _gimp_unit_cache_get_number_of_units; + vtable.unit_get_number_of_built_in_units = + _gimp_unit_cache_get_number_of_built_in_units; + vtable.unit_new = _gimp_unit_cache_new; + vtable.unit_get_deletion_flag = _gimp_unit_cache_get_deletion_flag; + vtable.unit_set_deletion_flag = _gimp_unit_cache_set_deletion_flag; + vtable.unit_get_factor = _gimp_unit_cache_get_factor; + vtable.unit_get_digits = _gimp_unit_cache_get_digits; + vtable.unit_get_identifier = _gimp_unit_cache_get_identifier; + vtable.unit_get_symbol = _gimp_unit_cache_get_symbol; + vtable.unit_get_abbreviation = _gimp_unit_cache_get_abbreviation; + vtable.unit_get_singular = _gimp_unit_cache_get_singular; + vtable.unit_get_plural = _gimp_unit_cache_get_plural; + + gimp_base_init (&vtable); + } + + /* initialize i18n support */ + + setlocale (LC_ALL, ""); + + bindtextdomain (GETTEXT_PACKAGE"-libgimp", gimp_locale_directory ()); +#ifdef HAVE_BIND_TEXTDOMAIN_CODESET + bind_textdomain_codeset (GETTEXT_PACKAGE"-libgimp", "UTF-8"); +#endif + + + /* set handler both for the "LibGimp" and "" domains */ + { + const gchar * const log_domains[] = + { + "LibGimp", + "LibGimpBase", + "LibGimpColor", + "LibGimpConfig", + "LibGimpMath", + "LibGimpModule", + "LibGimpThumb", + "LibGimpWidgets" + }; + gint i; + + for (i = 0; i < G_N_ELEMENTS (log_domains); i++) + g_log_set_handler (log_domains[i], + G_LOG_LEVEL_MESSAGE, + gimp_message_func, + NULL); + + g_log_set_handler (NULL, + G_LOG_LEVEL_MESSAGE, + gimp_message_func, + NULL); + } + + if (gimp_debug_flags & GIMP_DEBUG_FATAL_WARNINGS) + { + GLogLevelFlags fatal_mask; + + fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK); + fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL; + g_log_set_always_fatal (fatal_mask); + + g_log_set_handler (NULL, + G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL | + G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL, + gimp_fatal_func, NULL); + } + else + { + g_log_set_handler (NULL, + G_LOG_LEVEL_ERROR | G_LOG_FLAG_FATAL, + gimp_fatal_func, NULL); + } + + if (strcmp (argv[ARG_MODE], "-query") == 0) + { + if (PLUG_IN_INFO.init_proc) + gp_has_init_write (_writechannel, NULL); + + if (gimp_debug_flags & GIMP_DEBUG_QUERY) + gimp_debug_stop (); + + if (PLUG_IN_INFO.query_proc) + (* PLUG_IN_INFO.query_proc) (); + + gimp_close (); + + return EXIT_SUCCESS; + } + + if (strcmp (argv[ARG_MODE], "-init") == 0) + { + if (gimp_debug_flags & GIMP_DEBUG_INIT) + gimp_debug_stop (); + + if (PLUG_IN_INFO.init_proc) + (* PLUG_IN_INFO.init_proc) (); + + gimp_close (); + + return EXIT_SUCCESS; + } + + if (gimp_debug_flags & GIMP_DEBUG_RUN) + gimp_debug_stop (); + else if (gimp_debug_flags & GIMP_DEBUG_PID) + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Here I am!"); + + temp_proc_ht = g_hash_table_new (g_str_hash, g_str_equal); + + g_io_add_watch (_readchannel, + G_IO_ERR | G_IO_HUP, + gimp_plugin_io_error_handler, + NULL); + + gimp_loop (); + + return EXIT_SUCCESS; +} + +/** + * gimp_quit: + * + * Forcefully causes the GIMP library to exit and close down its + * connection to main gimp application. This function never returns. + **/ +void +gimp_quit (void) +{ + gimp_close (); + +#if defined G_OS_WIN32 && defined HAVE_EXCHNDL + if (plug_in_backtrace_path) + g_free (plug_in_backtrace_path); +#endif + + exit (EXIT_SUCCESS); +} + +/** + * gimp_install_procedure: + * @name: the procedure's name. + * @blurb: a short text describing what the procedure does. + * @help: the help text for the procedure (usually considerably + * longer than @blurb). + * @author: the procedure's author(s). + * @copyright: the procedure's copyright. + * @date: the date the procedure was added. + * @menu_label: the label to use for the procedure's menu entry, + * or #NULL if the procedure has no menu entry. + * @image_types: the drawable types the procedure can handle. + * @type: the type of the procedure. + * @n_params: the number of parameters the procedure takes. + * @n_return_vals: the number of return values the procedure returns. + * @params: the procedure's parameters. + * @return_vals: the procedure's return values. + * + * Installs a new procedure with the PDB (procedural database). + * + * Call this function from within your plug-in's query() function for + * each procedure your plug-in implements. + * + * The @name parameter is mandatory and should be unique, or it will + * overwrite an already existing procedure (overwrite procedures only + * if you know what you're doing). + * + * The @blurb, @help, @author, @copyright and @date parameters are + * optional but then you shouldn't write procedures without proper + * documentation, should you. + * + * @menu_label defines the label that should be used for the + * procedure's menu entry. The position where to register in the menu + * hierarchy is chosen using gimp_plugin_menu_register(). This + * function also still accepts the old (pre-2.2) way of registering a + * menu entry and takes a string in the form + * "<Domain>/Path/To/My/Menu" + * (e.g. "<Image>/Filters/Render/Useless"). + * + * Note that registering a full (pre-2.2-style) menu path is + * deprecated and will cause a failure in GIMP 3.0 and newer. + * + * It is possible to register a procedure only for keyboard-shortcut + * activation by passing a @menu_label to gimp_install_procedure() but + * not registering any menu path with gimp_plugin_menu_register(). In + * this case, the given @menu_label will only be used as the + * procedure's user-visible name in the keyboard shortcut editor. + * + * @image_types is a comma separated list of image types, or actually + * drawable types, that this procedure can deal with. Wildcards are + * possible here, so you could say "RGB*" instead of "RGB, RGBA" or + * "*" for all image types. If the procedure doesn't need an image to + * run, use the empty string. + * + * @type must be one of %GIMP_PLUGIN or %GIMP_EXTENSION. Note that + * temporary procedures must be installed using + * gimp_install_temp_proc(). + * + * NOTE: Unlike the GIMP 1.2 API, %GIMP_EXTENSION no longer means + * that the procedure's menu prefix is <Toolbox>, but that + * it will install temporary procedures. Therefore, the GIMP core + * will wait until the %GIMP_EXTENSION procedure has called + * gimp_extension_ack(), which means that the procedure has done + * its initialization, installed its temporary procedures and is + * ready to run. + * + * <emphasis>Not calling gimp_extension_ack() from a %GIMP_EXTENSION + * procedure will cause the GIMP core to lock up.</emphasis> + * + * Additionally, a %GIMP_EXTENSION procedure with no parameters + * (@n_params == 0 and @params == #NULL) is an "automatic" extension + * that will be automatically started on each GIMP startup. + **/ +void +gimp_install_procedure (const gchar *name, + const gchar *blurb, + const gchar *help, + const gchar *author, + const gchar *copyright, + const gchar *date, + const gchar *menu_label, + const gchar *image_types, + GimpPDBProcType type, + gint n_params, + gint n_return_vals, + const GimpParamDef *params, + const GimpParamDef *return_vals) +{ + GPProcInstall proc_install; + + g_return_if_fail (name != NULL); + g_return_if_fail (type != GIMP_INTERNAL); + g_return_if_fail ((n_params == 0 && params == NULL) || + (n_params > 0 && params != NULL)); + g_return_if_fail ((n_return_vals == 0 && return_vals == NULL) || + (n_return_vals > 0 && return_vals != NULL)); + + proc_install.name = (gchar *) name; + proc_install.blurb = (gchar *) blurb; + proc_install.help = (gchar *) help; + proc_install.author = (gchar *) author; + proc_install.copyright = (gchar *) copyright; + proc_install.date = (gchar *) date; + proc_install.menu_path = (gchar *) menu_label; + proc_install.image_types = (gchar *) image_types; + proc_install.type = type; + proc_install.nparams = n_params; + proc_install.nreturn_vals = n_return_vals; + proc_install.params = (GPParamDef *) params; + proc_install.return_vals = (GPParamDef *) return_vals; + + if (! gp_proc_install_write (_writechannel, &proc_install, NULL)) + gimp_quit (); +} + +/** + * gimp_install_temp_proc: + * @name: the procedure's name. + * @blurb: a short text describing what the procedure does. + * @help: the help text for the procedure (usually considerably + * longer than @blurb). + * @author: the procedure's author(s). + * @copyright: the procedure's copyright. + * @date: the date the procedure was added. + * @menu_label: the procedure's menu label, or #NULL if the procedure has + * no menu entry. + * @image_types: the drawable types the procedure can handle. + * @type: the type of the procedure. + * @n_params: the number of parameters the procedure takes. + * @n_return_vals: the number of return values the procedure returns. + * @params: the procedure's parameters. + * @return_vals: the procedure's return values. + * @run_proc: the function to call for executing the procedure. + * + * Installs a new temporary procedure with the PDB (procedural database). + * + * A temporary procedure is a procedure which is only available while + * one of your plug-in's "real" procedures is running. + * + * See gimp_install_procedure() for most details. + * + * @type <emphasis>must</emphasis> be %GIMP_TEMPORARY or the function + * will fail. + * + * @run_proc is the function which will be called to execute the + * procedure. + * + * NOTE: Normally, plug-in communication is triggered by the plug-in + * and the GIMP core only responds to the plug-in's requests. You must + * explicitly enable receiving of temporary procedure run requests + * using either gimp_extension_enable() or + * gimp_extension_process(). See this functions' documentation for + * details. + **/ +void +gimp_install_temp_proc (const gchar *name, + const gchar *blurb, + const gchar *help, + const gchar *author, + const gchar *copyright, + const gchar *date, + const gchar *menu_label, + const gchar *image_types, + GimpPDBProcType type, + gint n_params, + gint n_return_vals, + const GimpParamDef *params, + const GimpParamDef *return_vals, + GimpRunProc run_proc) +{ + g_return_if_fail (name != NULL); + g_return_if_fail ((n_params == 0 && params == NULL) || + (n_params > 0 && params != NULL)); + g_return_if_fail ((n_return_vals == 0 && return_vals == NULL) || + (n_return_vals > 0 && return_vals != NULL)); + g_return_if_fail (type == GIMP_TEMPORARY); + g_return_if_fail (run_proc != NULL); + + gimp_install_procedure (name, + blurb, help, + author, copyright, date, + menu_label, + image_types, + type, + n_params, n_return_vals, + params, return_vals); + + /* Insert the temp proc run function into the hash table */ + g_hash_table_insert (temp_proc_ht, g_strdup (name), (gpointer) run_proc); +} + +/** + * gimp_uninstall_temp_proc: + * @name: the procedure's name + * + * Uninstalls a temporary procedure which has previously been + * installed using gimp_install_temp_proc(). + **/ +void +gimp_uninstall_temp_proc (const gchar *name) +{ + GPProcUninstall proc_uninstall; + gpointer hash_name; + gboolean found; + + g_return_if_fail (name != NULL); + + proc_uninstall.name = (gchar *) name; + + if (! gp_proc_uninstall_write (_writechannel, &proc_uninstall, NULL)) + gimp_quit (); + + found = g_hash_table_lookup_extended (temp_proc_ht, name, &hash_name, NULL); + if (found) + { + g_hash_table_remove (temp_proc_ht, (gpointer) name); + g_free (hash_name); + } +} + +/** + * gimp_run_procedure: + * @name: the name of the procedure to run + * @n_return_vals: return location for the number of return values + * @...: list of procedure parameters + * + * This function calls a GIMP procedure and returns its return values. + * + * The procedure's parameters are given by a va_list in the format + * (type, value, type, value) and must be terminated by %GIMP_PDB_END. + * + * This function converts the va_list of parameters into an array and + * passes them to gimp_run_procedure2(). Please look there for further + * information. + * + * Return value: the procedure's return values unless there was an error, + * in which case the zero-th return value will be the error status, and + * the first return value will be a string detailing the error. + **/ +GimpParam * +gimp_run_procedure (const gchar *name, + gint *n_return_vals, + ...) +{ + GimpPDBArgType param_type; + GimpParam *return_vals; + GimpParam *params = NULL; + gint n_params = 0; + va_list args; + gint i; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (n_return_vals != NULL, NULL); + + va_start (args, n_return_vals); + param_type = va_arg (args, GimpPDBArgType); + + while (param_type != GIMP_PDB_END) + { + switch (param_type) + { + case GIMP_PDB_INT32: + case GIMP_PDB_DISPLAY: + case GIMP_PDB_IMAGE: + case GIMP_PDB_ITEM: + case GIMP_PDB_LAYER: + case GIMP_PDB_CHANNEL: + case GIMP_PDB_DRAWABLE: + case GIMP_PDB_SELECTION: + case GIMP_PDB_VECTORS: + case GIMP_PDB_STATUS: + (void) va_arg (args, gint); + break; + case GIMP_PDB_INT16: + (void) va_arg (args, gint); + break; + case GIMP_PDB_INT8: + (void) va_arg (args, gint); + break; + case GIMP_PDB_FLOAT: + (void) va_arg (args, gdouble); + break; + case GIMP_PDB_STRING: + (void) va_arg (args, gchar *); + break; + case GIMP_PDB_INT32ARRAY: + (void) va_arg (args, gint32 *); + break; + case GIMP_PDB_INT16ARRAY: + (void) va_arg (args, gint16 *); + break; + case GIMP_PDB_INT8ARRAY: + (void) va_arg (args, gint8 *); + break; + case GIMP_PDB_FLOATARRAY: + (void) va_arg (args, gdouble *); + break; + case GIMP_PDB_STRINGARRAY: + (void) va_arg (args, gchar **); + break; + case GIMP_PDB_COLOR: + case GIMP_PDB_COLORARRAY: + (void) va_arg (args, GimpRGB *); + break; + case GIMP_PDB_PARASITE: + (void) va_arg (args, GimpParasite *); + break; + case GIMP_PDB_END: + break; + } + + n_params++; + + param_type = va_arg (args, GimpPDBArgType); + } + + va_end (args); + + params = g_new0 (GimpParam, n_params); + + va_start (args, n_return_vals); + + for (i = 0; i < n_params; i++) + { + params[i].type = va_arg (args, GimpPDBArgType); + + switch (params[i].type) + { + case GIMP_PDB_INT32: + params[i].data.d_int32 = (gint32) va_arg (args, gint); + break; + case GIMP_PDB_INT16: + params[i].data.d_int16 = (gint16) va_arg (args, gint); + break; + case GIMP_PDB_INT8: + params[i].data.d_int8 = (guint8) va_arg (args, gint); + break; + case GIMP_PDB_FLOAT: + params[i].data.d_float = (gdouble) va_arg (args, gdouble); + break; + case GIMP_PDB_STRING: + params[i].data.d_string = va_arg (args, gchar *); + break; + case GIMP_PDB_INT32ARRAY: + params[i].data.d_int32array = va_arg (args, gint32 *); + break; + case GIMP_PDB_INT16ARRAY: + params[i].data.d_int16array = va_arg (args, gint16 *); + break; + case GIMP_PDB_INT8ARRAY: + params[i].data.d_int8array = va_arg (args, guint8 *); + break; + case GIMP_PDB_FLOATARRAY: + params[i].data.d_floatarray = va_arg (args, gdouble *); + break; + case GIMP_PDB_STRINGARRAY: + params[i].data.d_stringarray = va_arg (args, gchar **); + break; + case GIMP_PDB_COLOR: + params[i].data.d_color = *va_arg (args, GimpRGB *); + break; + case GIMP_PDB_ITEM: + params[i].data.d_item = va_arg (args, gint32); + break; + case GIMP_PDB_DISPLAY: + params[i].data.d_display = va_arg (args, gint32); + break; + case GIMP_PDB_IMAGE: + params[i].data.d_image = va_arg (args, gint32); + break; + case GIMP_PDB_LAYER: + params[i].data.d_layer = va_arg (args, gint32); + break; + case GIMP_PDB_CHANNEL: + params[i].data.d_channel = va_arg (args, gint32); + break; + case GIMP_PDB_DRAWABLE: + params[i].data.d_drawable = va_arg (args, gint32); + break; + case GIMP_PDB_SELECTION: + params[i].data.d_selection = va_arg (args, gint32); + break; + case GIMP_PDB_COLORARRAY: + params[i].data.d_colorarray = va_arg (args, GimpRGB *); + break; + case GIMP_PDB_VECTORS: + params[i].data.d_vectors = va_arg (args, gint32); + break; + case GIMP_PDB_PARASITE: + { + GimpParasite *parasite = va_arg (args, GimpParasite *); + + if (parasite == NULL) + { + params[i].data.d_parasite.name = NULL; + params[i].data.d_parasite.data = NULL; + } + else + { + params[i].data.d_parasite.name = parasite->name; + params[i].data.d_parasite.flags = parasite->flags; + params[i].data.d_parasite.size = parasite->size; + params[i].data.d_parasite.data = parasite->data; + } + } + break; + case GIMP_PDB_STATUS: + params[i].data.d_status = va_arg (args, gint32); + break; + case GIMP_PDB_END: + break; + } + } + + va_end (args); + + return_vals = gimp_run_procedure2 (name, n_return_vals, n_params, params); + + g_free (params); + + return return_vals; +} + +void +gimp_read_expect_msg (GimpWireMessage *msg, + gint type) +{ + while (TRUE) + { + if (! gimp_wire_read_msg (_readchannel, msg, NULL)) + gimp_quit (); + + if (msg->type == type) + return; /* up to the caller to call wire_destroy() */ + + if (msg->type == GP_TEMP_PROC_RUN || msg->type == GP_QUIT) + { + gimp_process_message (msg); + } + else + { + g_error ("unexpected message: %d", msg->type); + } + + gimp_wire_destroy (msg); + } +} + +/** + * gimp_run_procedure2: + * @name: the name of the procedure to run + * @n_return_vals: return location for the number of return values + * @n_params: the number of parameters the procedure takes. + * @params: the procedure's parameters array. + * + * This function calls a GIMP procedure and returns its return values. + * To get more information about the available procedures and the + * parameters they expect, please have a look at the Procedure Browser + * as found in the Xtns menu in GIMP's toolbox. + * + * As soon as you don't need the return values any longer, you should + * free them using gimp_destroy_params(). + * + * Return value: the procedure's return values unless there was an error, + * in which case the zero-th return value will be the error status, and + * if there are two values returned, the other return value will be a + * string detailing the error. + **/ +GimpParam * +gimp_run_procedure2 (const gchar *name, + gint *n_return_vals, + gint n_params, + const GimpParam *params) +{ + GPProcRun proc_run; + GPProcReturn *proc_return; + GimpWireMessage msg; + GimpParam *return_vals; + + g_return_val_if_fail (name != NULL, NULL); + g_return_val_if_fail (n_return_vals != NULL, NULL); + + proc_run.name = (gchar *) name; + proc_run.nparams = n_params; + proc_run.params = (GPParam *) params; + + if (! gp_proc_run_write (_writechannel, &proc_run, NULL)) + gimp_quit (); + + gimp_read_expect_msg (&msg, GP_PROC_RETURN); + + proc_return = msg.data; + + *n_return_vals = proc_return->nparams; + return_vals = (GimpParam *) proc_return->params; + + proc_return->nparams = 0; + proc_return->params = NULL; + + gimp_wire_destroy (&msg); + + gimp_set_pdb_error (return_vals, *n_return_vals); + + return return_vals; +} + +/** + * gimp_destroy_params: + * @params: the #GimpParam array to destroy + * @n_params: the number of elements in the array + * + * Destroys a #GimpParam array as returned by gimp_run_procedure() or + * gimp_run_procedure2(). + **/ +void +gimp_destroy_params (GimpParam *params, + gint n_params) +{ + gp_params_destroy ((GPParam *) params, n_params); +} + +/** + * gimp_destroy_paramdefs: + * @paramdefs: the #GimpParamDef array to destroy + * @n_params: the number of elements in the array + * + * Destroys a #GimpParamDef array as returned by + * gimp_procedural_db_proc_info(). + **/ +void +gimp_destroy_paramdefs (GimpParamDef *paramdefs, + gint n_params) +{ + while (n_params--) + { + g_free (paramdefs[n_params].name); + g_free (paramdefs[n_params].description); + } + + g_free (paramdefs); +} + +/** + * gimp_get_pdb_error: + * + * Retrieves the error message from the last procedure call. + * + * If a procedure call fails, then it might pass an error message with + * the return values. Plug-ins that are using the libgimp C wrappers + * don't access the procedure return values directly. Thus libgimp + * stores the error message and makes it available with this + * function. The next procedure call unsets the error message again. + * + * The returned string is owned by libgimp and must not be freed or + * modified. + * + * Return value: the error message + * + * Since: 2.6 + **/ +const gchar * +gimp_get_pdb_error (void) +{ + if (pdb_error_message && strlen (pdb_error_message)) + return pdb_error_message; + + switch (pdb_error_status) + { + case GIMP_PDB_SUCCESS: + /* procedure executed successfully */ + return _("success"); + + case GIMP_PDB_EXECUTION_ERROR: + /* procedure execution failed */ + return _("execution error"); + + case GIMP_PDB_CALLING_ERROR: + /* procedure called incorrectly */ + return _("calling error"); + + case GIMP_PDB_CANCEL: + /* procedure execution cancelled */ + return _("cancelled"); + + default: + return "invalid return status"; + } +} + +/** + * gimp_get_pdb_status: + * + * Retrieves the status from the last procedure call. + * + * Return value: the #GimpPDBStatusType. + * + * Since: 2.10 + **/ +GimpPDBStatusType +gimp_get_pdb_status (void) +{ + return pdb_error_status; +} + +/** + * gimp_tile_width: + * + * Returns the tile width GIMP is using. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the tile_width + **/ +guint +gimp_tile_width (void) +{ + return _tile_width; +} + +/** + * gimp_tile_height: + * + * Returns the tile height GIMP is using. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the tile_height + **/ +guint +gimp_tile_height (void) +{ + return _tile_height; +} + +/** + * gimp_shm_ID: + * + * Returns the shared memory ID used for passing tile data between the + * GIMP core and the plug-in. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the shared memory ID + **/ +gint +gimp_shm_ID (void) +{ + return _shm_ID; +} + +/** + * gimp_shm_addr: + * + * Returns the address of the shared memory segment used for passing + * tile data between the GIMP core and the plug-in. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the shared memory address + **/ +guchar * +gimp_shm_addr (void) +{ + return _shm_addr; +} + +/** + * gimp_gamma: + * + * Returns the global gamma value GIMP and all its plug-ins should + * use. + * + * This is a constant value. + * + * NOTE: This function will always return 2.2, the gamma value for + * sRGB. If you need the actual gamma value of a drawable, look at its + * format. + * + * See also: gimp_drawable_get_format(). + * + * @Deprecated: 2.8.4 + * + * Return value: the gamma value + **/ +gdouble +gimp_gamma (void) +{ + return 2.2; +} + +/** + * gimp_install_cmap: + * + * Returns whether or not the plug-in should allocate an own colormap + * when running on an 8 bit display. See also: gimp_min_colors(). + * + * This is a constant value given at plug-in configuration time. + * + * @Deprecated: 2.8 + * + * Return value: the install_cmap boolean + **/ +gboolean +gimp_install_cmap (void) +{ + return FALSE; +} + +/** + * gimp_min_colors: + * + * Returns the minimum number of colors to use when allocating an own + * colormap on 8 bit displays. + * + * This is a constant value given at plug-in configuration time. + * + * See also: gimp_install_cmap() + * + * @Deprecated: 2.8 + * + * Return value: the minimum number of colors to allocate + **/ +gint +gimp_min_colors (void) +{ + return _min_colors; +} + +/** + * gimp_show_tool_tips: + * + * Returns whether or not the plug-in should show tool-tips. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the show_tool_tips boolean + **/ +gboolean +gimp_show_tool_tips (void) +{ + return _show_tool_tips; +} + +/** + * gimp_show_help_button: + * + * Returns whether or not GimpDialog should automatically add a help + * button if help_func and help_id are given. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the show_help_button boolean + * + * Since: 2.2 + **/ +gboolean +gimp_show_help_button (void) +{ + return _show_help_button; +} + +/** + * gimp_export_color_profile: + * + * Returns whether file plug-ins should default to exporting the + * image's color profile. + * + * Return value: TRUE if preferences are set to export the color profile. + * + * Since: 2.10.4 + **/ +gboolean +gimp_export_color_profile (void) +{ + return _export_profile; +} + +/** + * gimp_export_exif: + * + * Returns whether file plug-ins should default to exporting Exif + * metadata, according preferences (original settings is #FALSE since + * metadata can contain sensitive information). + * + * Return value: TRUE if preferences are set to export Exif. + * + * Since: 2.10 + **/ +gboolean +gimp_export_exif (void) +{ + return _export_exif; +} + +/** + * gimp_export_xmp: + * + * Returns whether file plug-ins should default to exporting XMP + * metadata, according preferences (original settings is #FALSE since + * metadata can contain sensitive information). + * + * Return value: TRUE if preferences are set to export XMP. + * + * Since: 2.10 + **/ +gboolean +gimp_export_xmp (void) +{ + return _export_xmp; +} + +/** + * gimp_export_iptc: + * + * Returns whether file plug-ins should default to exporting IPTC + * metadata, according preferences (original settings is #FALSE since + * metadata can contain sensitive information). + * + * Return value: TRUE if preferences are set to export IPTC. + * + * Since: 2.10 + **/ +gboolean +gimp_export_iptc (void) +{ + return _export_iptc; +} + +/** + * gimp_check_size: + * + * Returns the size of the checkerboard to be used in previews. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the check_size value + * + * Since: 2.2 + **/ +GimpCheckSize +gimp_check_size (void) +{ + return _check_size; +} + +/** + * gimp_check_type: + * + * Returns the type of the checkerboard to be used in previews. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the check_type value + * + * Since: 2.2 + **/ +GimpCheckType +gimp_check_type (void) +{ + return _check_type; +} + +/** + * gimp_default_display: + * + * Returns the default display ID. This corresponds to the display the + * running procedure's menu entry was invoked from. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the default display ID + **/ +gint32 +gimp_default_display (void) +{ + return _gdisp_ID; +} + +/** + * gimp_wm_class: + * + * Returns the window manager class to be used for plug-in windows. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the window manager class + **/ +const gchar * +gimp_wm_class (void) +{ + return _wm_class; +} + +/** + * gimp_display_name: + * + * Returns the display to be used for plug-in windows. + * + * This is a constant value given at plug-in configuration time. + * Will return #NULL if GIMP has been started with no GUI, either + * via "--no-interface" flag, or a console build. + * + * Return value: the display name + **/ +const gchar * +gimp_display_name (void) +{ + return _display_name; +} + +/** + * gimp_monitor_number: + * + * Returns the monitor number to be used for plug-in windows. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the monitor number + **/ +gint +gimp_monitor_number (void) +{ + return _monitor_number; +} + +/** + * gimp_user_time: + * + * Returns the timestamp of the user interaction that should be set on + * the plug-in window. This is handled transparently, plug-in authors + * do not have to care about it. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: timestamp for plug-in window + * + * Since: 2.6 + **/ +guint32 +gimp_user_time (void) +{ + return _timestamp; +} + +/** + * gimp_get_icon_theme_dir: + * + * Returns the directory of the current icon theme. + * + * This is a constant value given at plug-in configuration time. + * + * Return value: the icon theme directory + * + * Since: 2.10.4 + **/ +const gchar * +gimp_icon_theme_dir (void) +{ + return _icon_theme_dir; +} + +/** + * gimp_get_progname: + * + * Returns the plug-in's executable name. + * + * Return value: the executable name + **/ +const gchar * +gimp_get_progname (void) +{ + return progname; +} + +/** + * gimp_extension_ack: + * + * Notify the main GIMP application that the extension has been properly + * initialized and is ready to run. + * + * This function <emphasis>must</emphasis> be called from every + * procedure that was registered as #GIMP_EXTENSION. + * + * Subsequently, extensions can process temporary procedure run + * requests using either gimp_extension_enable() or + * gimp_extension_process(). + * + * See also: gimp_install_procedure(), gimp_install_temp_proc() + **/ +void +gimp_extension_ack (void) +{ + if (! gp_extension_ack_write (_writechannel, NULL)) + gimp_quit (); +} + +/** + * gimp_extension_enable: + * + * Enables asynchronous processing of messages from the main GIMP + * application. + * + * Normally, a plug-in is not called by GIMP except for the call to + * the procedure it implements. All subsequent communication is + * triggered by the plug-in and all messages sent from GIMP to the + * plug-in are just answers to requests the plug-in made. + * + * If the plug-in however registered temporary procedures using + * gimp_install_temp_proc(), it needs to be able to receive requests + * to execute them. Usually this will be done by running + * gimp_extension_process() in an endless loop. + * + * If the plug-in cannot use gimp_extension_process(), i.e. if it has + * a GUI and is hanging around in a #GMainLoop, it must call + * gimp_extension_enable(). + * + * Note that the plug-in does not need to be a #GIMP_EXTENSION to + * register temporary procedures. + * + * See also: gimp_install_procedure(), gimp_install_temp_proc() + **/ +void +gimp_extension_enable (void) +{ + static gboolean callback_added = FALSE; + + if (! callback_added) + { + g_io_add_watch (_readchannel, G_IO_IN | G_IO_PRI, gimp_extension_read, + NULL); + + callback_added = TRUE; + } +} + +/** + * gimp_extension_process: + * @timeout: The timeout (in ms) to use for the select() call. + * + * Processes one message sent by GIMP and returns. + * + * Call this function in an endless loop after calling + * gimp_extension_ack() to process requests for running temporary + * procedures. + * + * See gimp_extension_enable() for an asynchronous way of doing the + * same if running an endless loop is not an option. + * + * See also: gimp_install_procedure(), gimp_install_temp_proc() + **/ +void +gimp_extension_process (guint timeout) +{ +#ifndef G_OS_WIN32 + gint select_val; + + do + { + fd_set readfds; + struct timeval tv; + struct timeval *tvp; + + if (timeout) + { + tv.tv_sec = timeout / 1000; + tv.tv_usec = (timeout % 1000) * 1000; + tvp = &tv; + } + else + tvp = NULL; + + FD_ZERO (&readfds); + FD_SET (g_io_channel_unix_get_fd (_readchannel), &readfds); + + if ((select_val = select (FD_SETSIZE, &readfds, NULL, NULL, tvp)) > 0) + { + gimp_single_message (); + } + else if (select_val == -1 && errno != EINTR) + { + perror ("gimp_extension_process"); + gimp_quit (); + } + } + while (select_val == -1 && errno == EINTR); +#else + /* Zero means infinite wait for us, but g_poll and + * g_io_channel_win32_poll use -1 to indicate + * infinite wait. + */ + GPollFD pollfd; + + if (timeout == 0) + timeout = -1; + + g_io_channel_win32_make_pollfd (_readchannel, G_IO_IN, &pollfd); + + if (g_io_channel_win32_poll (&pollfd, 1, timeout) == 1) + gimp_single_message (); +#endif +} + +/** + * gimp_parasite_find: + * @name: The name of the parasite to find. + * + * Deprecated: Use gimp_get_parasite() instead. + * + * Returns: The found parasite. + **/ +GimpParasite * +gimp_parasite_find (const gchar *name) +{ + return gimp_get_parasite (name); +} + +/** + * gimp_parasite_attach: + * @parasite: The parasite to attach. + * + * Deprecated: Use gimp_attach_parasite() instead. + * + * Returns: TRUE on success. + **/ +gboolean +gimp_parasite_attach (const GimpParasite *parasite) +{ + return gimp_attach_parasite (parasite); +} + +/** + * gimp_parasite_detach: + * @name: The name of the parasite to detach. + * + * Deprecated: Use gimp_detach_parasite() instead. + * + * Returns: TRUE on success. + **/ +gboolean +gimp_parasite_detach (const gchar *name) +{ + return gimp_detach_parasite (name); +} + +/** + * gimp_parasite_list: + * @num_parasites: The number of attached parasites. + * @parasites: The names of currently attached parasites. + * + * Deprecated: Use gimp_get_parasite_list() instead. + * + * Returns: TRUE on success. + **/ +gboolean +gimp_parasite_list (gint *num_parasites, + gchar ***parasites) +{ + *parasites = gimp_get_parasite_list (num_parasites); + + return *parasites != NULL; +} + +/** + * gimp_attach_new_parasite: + * @name: the name of the #GimpParasite to create and attach. + * @flags: the flags set on the #GimpParasite. + * @size: the size of the parasite data in bytes. + * @data: a pointer to the data attached with the #GimpParasite. + * + * Convenience function that creates a parasite and attaches it + * to GIMP. + * + * Deprecated: Use gimp_attach_parasite() instead. + * + * Return value: TRUE on successful creation and attachment of + * the new parasite. + * + * See Also: gimp_attach_parasite() + */ +gboolean +gimp_attach_new_parasite (const gchar *name, + gint flags, + gint size, + gconstpointer data) +{ + GimpParasite *parasite = gimp_parasite_new (name, flags, size, data); + gboolean success; + + success = gimp_attach_parasite (parasite); + + gimp_parasite_free (parasite); + + return success; +} + + +/* private functions */ + +static void +gimp_close (void) +{ + if (gimp_debug_flags & GIMP_DEBUG_QUIT) + gimp_debug_stop (); + + if (PLUG_IN_INFO.quit_proc) + (* PLUG_IN_INFO.quit_proc) (); + +#if defined(USE_SYSV_SHM) + + if ((_shm_ID != -1) && _shm_addr) + shmdt ((char *) _shm_addr); + +#elif defined(USE_WIN32_SHM) + + if (shm_handle) + CloseHandle (shm_handle); + +#elif defined(USE_POSIX_SHM) + + if ((_shm_ID != -1) && (_shm_addr != MAP_FAILED)) + munmap (_shm_addr, TILE_MAP_SIZE); + +#endif + + gp_quit_write (_writechannel, NULL); +} + +static void +gimp_debug_stop (void) +{ +#ifndef G_OS_WIN32 + + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Waiting for debugger..."); + raise (SIGSTOP); + +#else + + HANDLE hThreadSnap = NULL; + THREADENTRY32 te32 = { 0 }; + pid_t opid = getpid (); + + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + "Debugging (restart externally): %ld", + (long int) opid); + + hThreadSnap = CreateToolhelp32Snapshot (TH32CS_SNAPTHREAD, 0); + if (hThreadSnap == INVALID_HANDLE_VALUE) + { + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, + "error getting threadsnap - debugging impossible"); + return; + } + + te32.dwSize = sizeof (THREADENTRY32); + + if (Thread32First (hThreadSnap, &te32)) + { + do + { + if (te32.th32OwnerProcessID == opid) + { + HANDLE hThread = OpenThread (THREAD_SUSPEND_RESUME, FALSE, + te32.th32ThreadID); + SuspendThread (hThread); + CloseHandle (hThread); + } + } + while (Thread32Next (hThreadSnap, &te32)); + } + else + { + g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "error getting threads"); + } + + CloseHandle (hThreadSnap); + +#endif +} + +static void +gimp_message_func (const gchar *log_domain, + GLogLevelFlags log_level, + const gchar *message, + gpointer data) +{ + gimp_message (message); +} + +static void +gimp_fatal_func (const gchar *log_domain, + GLogLevelFlags flags, + const gchar *message, + gpointer data) +{ + const gchar *level; + + switch (flags & G_LOG_LEVEL_MASK) + { + case G_LOG_LEVEL_WARNING: + level = "WARNING"; + break; + case G_LOG_LEVEL_CRITICAL: + level = "CRITICAL"; + break; + case G_LOG_LEVEL_ERROR: + level = "ERROR"; + break; + default: + level = "FATAL"; + break; + } + + g_printerr ("%s: %s: %s\n", + progname, level, message); + +#ifndef G_OS_WIN32 + switch (stack_trace_mode) + { + case GIMP_STACK_TRACE_NEVER: + break; + + case GIMP_STACK_TRACE_QUERY: + { + sigset_t sigset; + + sigemptyset (&sigset); + sigprocmask (SIG_SETMASK, &sigset, NULL); + gimp_stack_trace_query (progname); + } + break; + + case GIMP_STACK_TRACE_ALWAYS: + { + sigset_t sigset; + + sigemptyset (&sigset); + sigprocmask (SIG_SETMASK, &sigset, NULL); + gimp_stack_trace_print (progname, stdout, NULL); + } + break; + } +#endif + + /* Do not end with gimp_quit(). + * We want the plug-in to continue its normal crash course, otherwise + * we won't get the "Plug-in crashed" error in GIMP. + */ + exit (EXIT_FAILURE); +} + +#ifdef G_OS_WIN32 + +#ifdef HAVE_EXCHNDL +static LONG WINAPI +gimp_plugin_sigfatal_handler (PEXCEPTION_POINTERS pExceptionInfo) +{ + g_printerr ("%s: fatal error\n", progname); + + SetUnhandledExceptionFilter (_prevExceptionFilter); + + /* For simplicity, do not make a difference between QUERY and ALWAYS + * on Windows (at least not for now). + */ + if (stack_trace_mode != GIMP_STACK_TRACE_NEVER && + g_file_test (plug_in_backtrace_path, G_FILE_TEST_IS_REGULAR)) + { + FILE *stream; + guchar buffer[256]; + size_t read_len; + + stream = fopen (plug_in_backtrace_path, "r"); + do + { + /* Just read and output directly the file content. */ + read_len = fread (buffer, 1, sizeof (buffer) - 1, stream); + buffer[read_len] = '\0'; + g_printerr ("%s", buffer); + } + while (read_len); + fclose (stream); + } + + if (_prevExceptionFilter && _prevExceptionFilter != gimp_plugin_sigfatal_handler) + return _prevExceptionFilter (pExceptionInfo); + else + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + +#else +static void +gimp_plugin_sigfatal_handler (gint sig_num) +{ + switch (sig_num) + { + case SIGHUP: + case SIGINT: + case SIGQUIT: + case SIGTERM: + g_printerr ("%s terminated: %s\n", progname, g_strsignal (sig_num)); + break; + + case SIGABRT: + case SIGBUS: + case SIGSEGV: + case SIGFPE: + case SIGPIPE: + default: + g_printerr ("%s: fatal error: %s\n", progname, g_strsignal (sig_num)); + switch (stack_trace_mode) + { + case GIMP_STACK_TRACE_NEVER: + break; + + case GIMP_STACK_TRACE_QUERY: + { + sigset_t sigset; + + sigemptyset (&sigset); + sigprocmask (SIG_SETMASK, &sigset, NULL); + gimp_stack_trace_query (progname); + } + break; + + case GIMP_STACK_TRACE_ALWAYS: + { + sigset_t sigset; + + sigemptyset (&sigset); + sigprocmask (SIG_SETMASK, &sigset, NULL); + gimp_stack_trace_print (progname, stdout, NULL); + } + break; + } + break; + } + + /* Do not end with gimp_quit(). + * We want the plug-in to continue its normal crash course, otherwise + * we won't get the "Plug-in crashed" error in GIMP. + */ + exit (EXIT_FAILURE); +} +#endif + +static gboolean +gimp_plugin_io_error_handler (GIOChannel *channel, + GIOCondition cond, + gpointer data) +{ + g_printerr ("%s: fatal error: GIMP crashed\n", progname); + gimp_quit (); + + /* never reached */ + return TRUE; +} + +static gboolean +gimp_write (GIOChannel *channel, + const guint8 *buf, + gulong count, + gpointer user_data) +{ + gulong bytes; + + while (count > 0) + { + if ((write_buffer_index + count) >= WRITE_BUFFER_SIZE) + { + bytes = WRITE_BUFFER_SIZE - write_buffer_index; + memcpy (&write_buffer[write_buffer_index], buf, bytes); + write_buffer_index += bytes; + if (! gimp_wire_flush (channel, NULL)) + return FALSE; + } + else + { + bytes = count; + memcpy (&write_buffer[write_buffer_index], buf, bytes); + write_buffer_index += bytes; + } + + buf += bytes; + count -= bytes; + } + + return TRUE; +} + +static gboolean +gimp_flush (GIOChannel *channel, + gpointer user_data) +{ + GIOStatus status; + GError *error = NULL; + gsize count; + gsize bytes; + + if (write_buffer_index > 0) + { + count = 0; + while (count != write_buffer_index) + { + do + { + bytes = 0; + status = g_io_channel_write_chars (channel, + &write_buffer[count], + (write_buffer_index - count), + &bytes, + &error); + } + while (status == G_IO_STATUS_AGAIN); + + if (status != G_IO_STATUS_NORMAL) + { + if (error) + { + g_warning ("%s: gimp_flush(): error: %s", + g_get_prgname (), error->message); + g_error_free (error); + } + else + { + g_warning ("%s: gimp_flush(): error", g_get_prgname ()); + } + + return FALSE; + } + + count += bytes; + } + + write_buffer_index = 0; + } + + return TRUE; +} + +static void +gimp_loop (void) +{ + GimpWireMessage msg; + + while (TRUE) + { + if (! gimp_wire_read_msg (_readchannel, &msg, NULL)) + { + gimp_close (); + return; + } + + switch (msg.type) + { + case GP_QUIT: + gimp_wire_destroy (&msg); + gimp_close (); + return; + + case GP_CONFIG: + gimp_config (msg.data); + break; + + case GP_TILE_REQ: + case GP_TILE_ACK: + case GP_TILE_DATA: + g_warning ("unexpected tile message received (should not happen)"); + break; + + case GP_PROC_RUN: + gimp_proc_run (msg.data); + gimp_wire_destroy (&msg); + gimp_close (); + return; + + case GP_PROC_RETURN: + g_warning ("unexpected proc return message received (should not happen)"); + break; + + case GP_TEMP_PROC_RUN: + g_warning ("unexpected temp proc run message received (should not happen"); + break; + + case GP_TEMP_PROC_RETURN: + g_warning ("unexpected temp proc return message received (should not happen"); + break; + + case GP_PROC_INSTALL: + g_warning ("unexpected proc install message received (should not happen)"); + break; + + case GP_HAS_INIT: + g_warning ("unexpected has init message received (should not happen)"); + break; + } + + gimp_wire_destroy (&msg); + } +} + +static void +gimp_config (GPConfig *config) +{ + GFile *file; + gchar *path; + + if (config->version < GIMP_PROTOCOL_VERSION) + { + g_message ("Could not execute plug-in \"%s\"\n(%s)\n" + "because GIMP is using an older version of the " + "plug-in protocol.", + gimp_filename_to_utf8 (g_get_prgname ()), + gimp_filename_to_utf8 (progname)); + gimp_quit (); + } + else if (config->version > GIMP_PROTOCOL_VERSION) + { + g_message ("Could not execute plug-in \"%s\"\n(%s)\n" + "because it uses an obsolete version of the " + "plug-in protocol.", + gimp_filename_to_utf8 (g_get_prgname ()), + gimp_filename_to_utf8 (progname)); + gimp_quit (); + } + + _tile_width = config->tile_width; + _tile_height = config->tile_height; + _shm_ID = config->shm_ID; + _check_size = config->check_size; + _check_type = config->check_type; + _show_tool_tips = config->show_tooltips ? TRUE : FALSE; + _show_help_button = config->show_help_button ? TRUE : FALSE; + _export_profile = config->export_profile ? TRUE : FALSE; + _export_exif = config->export_exif ? TRUE : FALSE; + _export_xmp = config->export_xmp ? TRUE : FALSE; + _export_iptc = config->export_iptc ? TRUE : FALSE; + _min_colors = config->min_colors; + _gdisp_ID = config->gdisp_ID; + _wm_class = g_strdup (config->wm_class); + _display_name = g_strdup (config->display_name); + _monitor_number = config->monitor_number; + _timestamp = config->timestamp; + _icon_theme_dir = g_strdup (config->icon_theme_dir); + + if (config->app_name) + g_set_application_name (config->app_name); + + gimp_cpu_accel_set_use (config->use_cpu_accel); + + file = gimp_file_new_for_config_path (config->swap_path, NULL); + path = g_file_get_path (file); + + g_object_set (gegl_config (), + "tile-cache-size", config->tile_cache_size, + "swap", path, + "swap-compression", config->swap_compression, + "threads", (gint) config->num_processors, + "use-opencl", config->use_opencl, + "application-license", "GPL3", + NULL); + + g_free (path); + g_object_unref (file); + + if (_shm_ID != -1) + { +#if defined(USE_SYSV_SHM) + + /* Use SysV shared memory mechanisms for transferring tile data. */ + + _shm_addr = (guchar *) shmat (_shm_ID, NULL, 0); + + if (_shm_addr == (guchar *) -1) + { + g_error ("shmat() failed: %s\n" ERRMSG_SHM_FAILED, + g_strerror (errno)); + } + +#elif defined(USE_WIN32_SHM) + + /* Use Win32 shared memory mechanisms for transferring tile data. */ + + gchar fileMapName[128]; + + /* From the id, derive the file map name */ + g_snprintf (fileMapName, sizeof (fileMapName), "GIMP%d.SHM", _shm_ID); + + /* Open the file mapping */ + shm_handle = OpenFileMapping (FILE_MAP_ALL_ACCESS, + 0, fileMapName); + if (shm_handle) + { + /* Map the shared memory into our address space for use */ + _shm_addr = (guchar *) MapViewOfFile (shm_handle, + FILE_MAP_ALL_ACCESS, + 0, 0, TILE_MAP_SIZE); + + /* Verify that we mapped our view */ + if (!_shm_addr) + { + g_error ("MapViewOfFile error: %lu... " ERRMSG_SHM_FAILED, + GetLastError ()); + } + } + else + { + g_error ("OpenFileMapping error: %lu... " ERRMSG_SHM_FAILED, + GetLastError ()); + } + +#elif defined(USE_POSIX_SHM) + + /* Use POSIX shared memory mechanisms for transferring tile data. */ + + gchar map_file[32]; + gint shm_fd; + + /* From the id, derive the file map name */ + g_snprintf (map_file, sizeof (map_file), "/gimp-shm-%d", _shm_ID); + + /* Open the file mapping */ + shm_fd = shm_open (map_file, O_RDWR, 0600); + + if (shm_fd != -1) + { + /* Map the shared memory into our address space for use */ + _shm_addr = (guchar *) mmap (NULL, TILE_MAP_SIZE, + PROT_READ | PROT_WRITE, MAP_SHARED, + shm_fd, 0); + + /* Verify that we mapped our view */ + if (_shm_addr == MAP_FAILED) + { + g_error ("mmap() failed: %s\n" ERRMSG_SHM_FAILED, + g_strerror (errno)); + } + + close (shm_fd); + } + else + { + g_error ("shm_open() failed: %s\n" ERRMSG_SHM_FAILED, + g_strerror (errno)); + } + +#endif + } +} + +static void +gimp_proc_run (GPProcRun *proc_run) +{ + if (PLUG_IN_INFO.run_proc) + { + GPProcReturn proc_return; + GimpParam *return_vals; + gint n_return_vals; + + (* PLUG_IN_INFO.run_proc) (proc_run->name, + proc_run->nparams, + (GimpParam *) proc_run->params, + &n_return_vals, &return_vals); + + proc_return.name = proc_run->name; + proc_return.nparams = n_return_vals; + proc_return.params = (GPParam *) return_vals; + + if (! gp_proc_return_write (_writechannel, &proc_return, NULL)) + gimp_quit (); + } +} + +static void +gimp_temp_proc_run (GPProcRun *proc_run) +{ + GimpRunProc run_proc = g_hash_table_lookup (temp_proc_ht, proc_run->name); + + if (run_proc) + { + GPProcReturn proc_return; + GimpParam *return_vals; + gint n_return_vals; + +#ifdef GDK_WINDOWING_QUARTZ + if (proc_run->params && + proc_run->params[0].data.d_int32 == GIMP_RUN_INTERACTIVE) + { + [NSApp activateIgnoringOtherApps: YES]; + } +#endif + + (* run_proc) (proc_run->name, + proc_run->nparams, + (GimpParam *) proc_run->params, + &n_return_vals, &return_vals); + + proc_return.name = proc_run->name; + proc_return.nparams = n_return_vals; + proc_return.params = (GPParam *) return_vals; + + if (! gp_temp_proc_return_write (_writechannel, &proc_return, NULL)) + gimp_quit (); + } +} + +static void +gimp_process_message (GimpWireMessage *msg) +{ + switch (msg->type) + { + case GP_QUIT: + gimp_quit (); + break; + case GP_CONFIG: + gimp_config (msg->data); + break; + case GP_TILE_REQ: + case GP_TILE_ACK: + case GP_TILE_DATA: + g_warning ("unexpected tile message received (should not happen)"); + break; + case GP_PROC_RUN: + g_warning ("unexpected proc run message received (should not happen)"); + break; + case GP_PROC_RETURN: + g_warning ("unexpected proc return message received (should not happen)"); + break; + case GP_TEMP_PROC_RUN: + gimp_temp_proc_run (msg->data); + break; + case GP_TEMP_PROC_RETURN: + g_warning ("unexpected temp proc return message received (should not happen)"); + break; + case GP_PROC_INSTALL: + g_warning ("unexpected proc install message received (should not happen)"); + break; + case GP_HAS_INIT: + g_warning ("unexpected has init message received (should not happen)"); + break; + } +} + +static void +gimp_single_message (void) +{ + GimpWireMessage msg; + + /* Run a temp function */ + if (! gimp_wire_read_msg (_readchannel, &msg, NULL)) + gimp_quit (); + + gimp_process_message (&msg); + + gimp_wire_destroy (&msg); +} + +static gboolean +gimp_extension_read (GIOChannel *channel, + GIOCondition condition, + gpointer data) +{ + gimp_single_message (); + + return TRUE; +} + +static void +gimp_set_pdb_error (const GimpParam *return_vals, + gint n_return_vals) +{ + if (pdb_error_message) + { + g_free (pdb_error_message); + pdb_error_message = NULL; + } + + pdb_error_status = return_vals[0].data.d_status; + + switch (pdb_error_status) + { + case GIMP_PDB_SUCCESS: + case GIMP_PDB_PASS_THROUGH: + break; + + case GIMP_PDB_EXECUTION_ERROR: + case GIMP_PDB_CALLING_ERROR: + case GIMP_PDB_CANCEL: + if (n_return_vals > 1 && return_vals[1].type == GIMP_PDB_STRING) + { + pdb_error_message = g_strdup (return_vals[1].data.d_string); + } + break; + } +} |