/* 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 <glib.h>
#include <glib/gstdio.h>

#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(G_OS_WIN32) || defined(G_WITH_CYGWIN)
#  ifdef STRICT
#  undef STRICT
#  endif
#  define STRICT

#  include <windows.h>
#  include <tlhelp32.h>
#  undef RGB
#endif

#ifdef GDK_WINDOWING_QUARTZ
#include <Cocoa/Cocoa.h>
#endif

#include <locale.h>

#include "gimp.h"

#include "libgimpbase/gimpbase-private.h"
#include "libgimpbase/gimpprotocol.h"
#include "libgimpbase/gimpwire.h"

#include "gimp-debug.h"
#include "gimp-private.h"
#include "gimp-shm.h"
#include "gimpgpparams.h"
#include "gimppdb-private.h"
#include "gimpplugin-private.h"

#include "libgimp-intl.h"


static void   gimp_close        (void);


#ifdef G_OS_WIN32

#ifdef HAVE_EXCHNDL
static LONG WINAPI gimp_plugin_sigfatal_handler (PEXCEPTION_POINTERS pExceptionInfo);

static LPTOP_LEVEL_EXCEPTION_FILTER  _prevExceptionFilter    = NULL;
static gchar                         *plug_in_backtrace_path = NULL;
#endif

#else /* ! G_OS_WIN32 */

static void        gimp_plugin_sigfatal_handler (gint sig_num);

#endif /* G_OS_WIN32 */


static GimpPlugIn         *PLUG_IN               = NULL;
static GimpPDB            *PDB                   = NULL;

static gint                _tile_width           = -1;
static gint                _tile_height          = -1;
static gboolean            _show_help_button     = TRUE;
static gboolean            _export_color_profile = FALSE;
static gboolean            _export_comment       = FALSE;
static gboolean            _export_exif          = FALSE;
static gboolean            _export_xmp           = FALSE;
static gboolean            _export_iptc          = FALSE;
static gboolean            _export_thumbnail     = TRUE;
static gboolean            _update_metadata      = TRUE;
static gint32              _num_processors       = 1;
static GimpCheckSize       _check_size           = GIMP_CHECK_SIZE_MEDIUM_CHECKS;
static GimpCheckType       _check_type           = GIMP_CHECK_TYPE_GRAY_CHECKS;
static GeglColor          *_check_custom_color1  = NULL;
static GeglColor          *_check_custom_color2  = NULL;
static gint                _default_display_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 GimpStackTraceMode  stack_trace_mode      = GIMP_STACK_TRACE_NEVER;


/**
 * gimp_main:
 * @plug_in_type: the type of the #GimpPlugIn subclass of the plug-in
 * @argc:         the number of arguments
 * @argv:         (array length=argc): the arguments
 *
 * The main plug-in function that must be called with the plug-in's
 * [class@Gimp.PlugIn] subclass #GType and the 'argc' and 'argv' that are passed
 * to the platform's `main()`.
 *
 * For instance, in Python, you will want to end your plug-in with this
 * call:
 *
 * ```py
 * Gimp.main(MyPlugIn.__gtype__, sys.argv)
 * ```
 *
 * For C plug-ins, use instead the [func@Gimp.MAIN] macro
 *
 * Returns: an exit status as defined by the C library,
 *          on success `EXIT_SUCCESS`.
 *
 * Since: 3.0
 **/
gint
gimp_main (GType  plug_in_type,
           gint   argc,
           gchar *argv[])
{
  enum
  {
    ARG_PROGNAME,
    ARG_GIMP,
    ARG_PROTOCOL_VERSION,
    ARG_READ_FD,
    ARG_WRITE_FD,
    ARG_MODE,
    ARG_STACK_TRACE_MODE,

    N_ARGS
  };

  GIOChannel *read_channel;
  GIOChannel *write_channel;
  gchar      *basename;
  gint        protocol_version;

#ifdef G_OS_WIN32

  gint i, j, k;

  /* Make plugins output available on console */
  if (AttachConsole (ATTACH_PARENT_PROCESS) != 0 && ! g_getenv ("TERM") && ! g_getenv ("SHELL"))
    {
      /* 'r' is needed to prevent interleaving and '+' to support colors */
      freopen ("CONOUT$", "r+", stdout);
      freopen ("CONOUT$", "r+", stderr);
      _flushall ();
    }

  /* Reduce risks */
  SetDllDirectoryW (L"");

  /* 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;

    w_bin_dir = NULL;
    install_dir = gimp_installation_directory ();
    bin_dir = g_build_filename (install_dir, "bin", NULL);

    w_bin_dir = g_utf8_to_utf16 (bin_dir, -1, NULL, NULL, NULL);
    if (w_bin_dir)
      {
        SetDllDirectoryW (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;
#ifdef ENABLE_RELOCATABLE_RESOURCES
    gchar   *plugin_dir = NULL;
    WCHAR    plugin_dir_utf16[MAX_PATH];
    gchar   *plugin_exe;
    size_t   codeview_path_len;
    gchar   *codeview_path;
#endif
    gchar   *filename;
    gchar   *dir;
    wchar_t *plug_in_backtrace_path_utf16;

#ifdef ENABLE_RELOCATABLE_RESOURCES
    /* FIXME: https://github.com/jrfonseca/drmingw/issues/91 */
    if (GetModuleFileNameW (NULL, plugin_dir_utf16, MAX_PATH) != 0)
      {
        plugin_exe = g_utf16_to_utf8 ((const guint16 *)plugin_dir_utf16, -1,
                                      NULL, NULL, NULL);
        plugin_dir = g_path_get_dirname (plugin_exe);
        g_free (plugin_exe);
      }
    if (plugin_dir && (!g_getenv ("_NT_SYMBOL_PATH") || !strstr(g_getenv ("_NT_SYMBOL_PATH"), plugin_dir)))
      {
        codeview_path_len = strlen (g_getenv ("_NT_SYMBOL_PATH") ? g_getenv ("_NT_SYMBOL_PATH") : "") + strlen (plugin_dir) + 2;
        codeview_path = g_try_malloc (codeview_path_len);
        if (codeview_path == NULL)
          {
            g_warning ("Failed to allocate memory");
          }
        if (g_getenv ("_NT_SYMBOL_PATH"))
          g_snprintf (codeview_path, codeview_path_len, "%s;%s", plugin_dir, g_getenv ("_NT_SYMBOL_PATH"));
        else
          g_snprintf (codeview_path, codeview_path_len, "%s", plugin_dir);
        g_setenv ("_NT_SYMBOL_PATH", codeview_path, TRUE);
        g_free (codeview_path);
      }
    g_free (plugin_dir);
#endif

    /* 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 ();

    plug_in_backtrace_path_utf16 = g_utf8_to_utf16 (plug_in_backtrace_path,
                                                    -1, NULL, NULL, NULL);
    if (plug_in_backtrace_path_utf16)
      {
        ExcHndlSetLogFileNameW (plug_in_backtrace_path_utf16);
        g_free (plug_in_backtrace_path_utf16);
      }
  }
#endif /* HAVE_EXCHNDL */

#ifndef _WIN64
  {
    typedef BOOL (WINAPI *t_SetProcessDEPPolicy) (DWORD dwFlags);
    t_SetProcessDEPPolicy p_SetProcessDEPPolicy;

    p_SetProcessDEPPolicy = (t_SetProcessDEPPolicy) GetProcAddress (GetModuleHandleW (L"kernel32.dll"),
                                                                    "SetProcessDEPPolicy");
    if (p_SetProcessDEPPolicy)
      (*p_SetProcessDEPPolicy) (PROCESS_DEP_ENABLE|PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
  }
#endif /* _WIN64 */

  /* Group all our windows together on the taskbar */
  {
    typedef HRESULT (WINAPI *t_SetCurrentProcessExplicitAppUserModelID) (PCWSTR lpPathName);
    t_SetCurrentProcessExplicitAppUserModelID p_SetCurrentProcessExplicitAppUserModelID;

    p_SetCurrentProcessExplicitAppUserModelID = (t_SetCurrentProcessExplicitAppUserModelID) GetProcAddress (GetModuleHandleW (L"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_OS_WIN32 */

#ifdef GDK_WINDOWING_QUARTZ
  /* Sets activation policy to prevent plugins from appearing as separate apps
   * in Dock.
   * Makes plugins behave as helper processes of GIMP on macOS.
   */
  [NSApp setActivationPolicy:NSApplicationActivationPolicyAccessory];
#endif

  g_assert (plug_in_type != G_TYPE_NONE);

  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 EXIT_FAILURE;
    }

  gimp_env_init (TRUE);

  progname = argv[ARG_PROGNAME];

  basename = g_path_get_basename (progname);

  g_set_prgname (basename);

  protocol_version = atoi (argv[ARG_PROTOCOL_VERSION]);

  if (protocol_version < GIMP_PROTOCOL_VERSION)
    {
      g_printerr ("Could not execute plug-in \"%s\"\n(%s)\n"
                  "because GIMP is using an older version of the "
                  "plug-in protocol.\n",
                  gimp_filename_to_utf8 (g_get_prgname ()),
                  gimp_filename_to_utf8 (progname));
      return EXIT_FAILURE;
    }
  else if (protocol_version > GIMP_PROTOCOL_VERSION)
    {
      g_printerr ("Could not execute plug-in \"%s\"\n(%s)\n"
                  "because it uses an obsolete version of the "
                  "plug-in protocol.\n",
                  gimp_filename_to_utf8 (g_get_prgname ()),
                  gimp_filename_to_utf8 (progname));
      return EXIT_FAILURE;
    }

  _gimp_debug_init (basename);

  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 /* ! G_OS_WIN32 */

#ifdef G_OS_WIN32
  read_channel  = g_io_channel_win32_new_fd (atoi (argv[ARG_READ_FD]));
  write_channel = g_io_channel_win32_new_fd (atoi (argv[ARG_WRITE_FD]));
#else
  read_channel  = g_io_channel_unix_new (atoi (argv[ARG_READ_FD]));
  write_channel = g_io_channel_unix_new (atoi (argv[ARG_WRITE_FD]));
#endif

  g_io_channel_set_encoding (read_channel, NULL, NULL);
  g_io_channel_set_encoding (write_channel, NULL, NULL);

  g_io_channel_set_buffered (read_channel, FALSE);
  g_io_channel_set_buffered (write_channel, FALSE);

  g_io_channel_set_close_on_unref (read_channel, TRUE);
  g_io_channel_set_close_on_unref (write_channel, TRUE);

  /*  initialize GTypes, they need to be known to g_type_from_name()  */
  {
    GType init_types[] =
    {
      G_TYPE_INT,                  G_TYPE_PARAM_INT,
      G_TYPE_UCHAR,                G_TYPE_PARAM_UCHAR,

      G_TYPE_STRING,               G_TYPE_PARAM_STRING,
      G_TYPE_STRV,                 G_TYPE_PARAM_BOXED,

      G_TYPE_BYTES,                G_TYPE_PARAM_BOXED,

      GIMP_TYPE_ARRAY,             GIMP_TYPE_PARAM_ARRAY,
      GIMP_TYPE_INT32_ARRAY,       GIMP_TYPE_PARAM_INT32_ARRAY,
      GIMP_TYPE_DOUBLE_ARRAY,      GIMP_TYPE_PARAM_DOUBLE_ARRAY,
      GIMP_TYPE_VALUE_ARRAY,       GIMP_TYPE_PARAM_VALUE_ARRAY,
      GIMP_TYPE_CORE_OBJECT_ARRAY, GIMP_TYPE_PARAM_CORE_OBJECT_ARRAY,

      GIMP_TYPE_DISPLAY,           GIMP_TYPE_PARAM_DISPLAY,
      GIMP_TYPE_IMAGE,             GIMP_TYPE_PARAM_IMAGE,
      GIMP_TYPE_ITEM,              GIMP_TYPE_PARAM_ITEM,
      GIMP_TYPE_DRAWABLE,          GIMP_TYPE_PARAM_DRAWABLE,
      GIMP_TYPE_LAYER,             GIMP_TYPE_PARAM_LAYER,
      GIMP_TYPE_TEXT_LAYER,        GIMP_TYPE_PARAM_TEXT_LAYER,
      GIMP_TYPE_VECTOR_LAYER,      GIMP_TYPE_PARAM_VECTOR_LAYER,
      GIMP_TYPE_LINK_LAYER,        GIMP_TYPE_PARAM_LINK_LAYER,
      GIMP_TYPE_GROUP_LAYER,       GIMP_TYPE_PARAM_GROUP_LAYER,
      GIMP_TYPE_CHANNEL,           GIMP_TYPE_PARAM_CHANNEL,
      GIMP_TYPE_LAYER_MASK,        GIMP_TYPE_PARAM_LAYER_MASK,
      GIMP_TYPE_SELECTION,         GIMP_TYPE_PARAM_SELECTION,
      GIMP_TYPE_PATH,              GIMP_TYPE_PARAM_PATH,
      GIMP_TYPE_DRAWABLE_FILTER,   GIMP_TYPE_PARAM_DRAWABLE_FILTER,

      GIMP_TYPE_BRUSH,             GIMP_TYPE_PARAM_BRUSH,
      GIMP_TYPE_FONT,              GIMP_TYPE_PARAM_FONT,
      GIMP_TYPE_GRADIENT,          GIMP_TYPE_PARAM_GRADIENT,
      GIMP_TYPE_PALETTE,           GIMP_TYPE_PARAM_PALETTE,
      GIMP_TYPE_PATTERN,           GIMP_TYPE_PARAM_PATTERN,

      GIMP_TYPE_UNIT,              GIMP_TYPE_PARAM_UNIT,
    };

    gint i;

    for (i = 0; i < G_N_ELEMENTS (init_types); i++)
      {
        GType type = init_types[i];

        if (G_TYPE_IS_CLASSED (type))
          g_type_class_ref (type);
      }

    gimp_enums_init ();
  }

  /*  initialize units  */
  {
    GimpUnitVtable vtable = { 0 };

    vtable.get_deletion_flag = _gimp_unit_get_deletion_flag;
    vtable.set_deletion_flag = _gimp_unit_set_deletion_flag;
    vtable.get_data          = _gimp_unit_get_data;

    gimp_base_init (&vtable);
  }

  /*  initialize i18n support  */
  setlocale (LC_ALL, "");

  gimp_bind_text_domain (GETTEXT_PACKAGE"-libgimp", gimp_locale_directory ());
#ifdef HAVE_BIND_TEXTDOMAIN_CODESET
  bind_textdomain_codeset (GETTEXT_PACKAGE"-libgimp", "UTF-8");
#endif

  _gimp_debug_configure (stack_trace_mode);

  PLUG_IN = g_object_new (plug_in_type,
                          "program-name",  progname,
                          "read-channel",  read_channel,
                          "write-channel", write_channel,
                          NULL);

  g_assert (GIMP_IS_PLUG_IN (PLUG_IN));

  if (strcmp (argv[ARG_MODE], "-query") == 0)
    {
      if (_gimp_get_debug_flags () & GIMP_DEBUG_QUERY)
        _gimp_debug_stop ();

      _gimp_plug_in_query (PLUG_IN);

      gimp_close ();

      return EXIT_SUCCESS;
    }

  if (strcmp (argv[ARG_MODE], "-init") == 0)
    {
      if (_gimp_get_debug_flags () & GIMP_DEBUG_INIT)
        _gimp_debug_stop ();

      _gimp_plug_in_init (PLUG_IN);

      gimp_close ();

      return EXIT_SUCCESS;
    }

  if (_gimp_get_debug_flags () & GIMP_DEBUG_RUN)
    _gimp_debug_stop ();
  else if (_gimp_get_debug_flags () & GIMP_DEBUG_PID)
    g_log (G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "Here I am!");

  _gimp_plug_in_run (PLUG_IN);

  gimp_close ();
  g_io_channel_unref (read_channel);
  g_io_channel_unref (write_channel);
  g_clear_object (&_check_custom_color1);
  g_clear_object (&_check_custom_color2);

  return EXIT_SUCCESS;
}

/**
 * gimp_get_plug_in:
 *
 * This function returns the plug-in's #GimpPlugIn instance, which is
 * a a singleton that can exist exactly once per running plug-in.
 *
 * Returns: (transfer none) (nullable): The plug-in's #GimpPlugIn singleton.
 *
 * Since: 3.0
 **/
GimpPlugIn *
gimp_get_plug_in (void)
{
  return PLUG_IN;
}

/**
 * gimp_get_pdb:
 *
 * This function returns the plug-in's #GimpPDB instance, which is a
 * singleton that can exist exactly once per running plug-in.
 *
 * Returns: (transfer none) (nullable): The plug-in's #GimpPDB singleton.
 *
 * Since: 3.0
 **/
GimpPDB *
gimp_get_pdb (void)
{
  if (! PDB)
    PDB = _gimp_pdb_new (PLUG_IN);

  return PDB;
}

/**
 * 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_tile_width:
 *
 * Returns the tile width GIMP is using.
 *
 * This is a constant value given at plug-in configuration time.
 *
 * Returns: 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.
 *
 * Returns: the tile_height
 **/
guint
gimp_tile_height (void)
{
  return _tile_height;
}

/**
 * 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.
 *
 * Returns: 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.
 *
 * Returns: TRUE if preferences are set to export the color profile.
 *
 * Since: 2.10.4
 **/
gboolean
gimp_export_color_profile (void)
{
  return _export_color_profile;
}

/**
 * gimp_export_comment:
 *
 * Returns whether file plug-ins should default to exporting the
 * image's comment.
 *
 * Returns: TRUE if preferences are set to export the comment.
 *
 * Since: 3.0
 **/
gboolean
gimp_export_comment (void)
{
  return _export_comment;
}

/**
 * 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).
 *
 * Returns: 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).
 *
 * Returns: 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).
 *
 * Returns: TRUE if preferences are set to export IPTC.
 *
 * Since: 2.10
 **/
gboolean
gimp_export_iptc (void)
{
  return _export_iptc;
}

/**
 * gimp_export_thumbnail:
 *
 * Returns whether file plug-ins should default to exporting the
 * image's comment.
 *
 * Returns: TRUE if preferences are set to export the thumbnail.
 *
 * Since: 3.0
 **/
gboolean
gimp_export_thumbnail (void)
{
  return _export_thumbnail;
}

/**
 * gimp_update_metadata:
 *
 * Returns whether file plug-ins should update the
 * image's metadata.
 *
 * Note that metadata that reflects the image characteristics
 * will still be updated even if this is set to FALSE. This only
 * concerns metadata changes that are nonessential, like setting
 * GIMP in Exif.Image.Software, synchronizing the comment with its
 * equivalent metadata tags, etc.
 *
 * Returns: TRUE if preferences are set to update the metadata.
 *
 * Since: 3.1
 **/
gboolean
gimp_update_metadata (void)
{
  return _update_metadata;
}

/**
 * gimp_get_num_processors:
 *
 * Returns the number of threads set explicitly by the user in the
 * preferences. This information can be used by plug-ins wishing to
 * follow user settings for multi-threaded implementations.
 *
 * Returns: the preferred number of threads to use.
 *
 * Since: 3.0
 **/
gint32
gimp_get_num_processors (void)
{
  return _num_processors;
}

/**
 * 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.
 *
 * Returns: 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.
 *
 * Returns: the check_type value
 *
 * Since: 2.2
 **/
GimpCheckType
gimp_check_type (void)
{
  return _check_type;
}

/**
 * gimp_check_custom_color1:
 *
 * Returns the first checkerboard custom color that can
 * be used in previews.
 *
 * This is a constant value given at plug-in configuration time.
 *
 * Return value: the _check_custom_color1 value
 *
 * Since: 3.0
 **/
const GeglColor *
gimp_check_custom_color1 (void)
{
  return (const GeglColor *) _check_custom_color1;
}

/**
 * gimp_check_custom_color2:
 *
 * Returns the second checkerboard custom color that can
 * be used in previews.
 *
 * This is a constant value given at plug-in configuration time.
 *
 * Return value: the _check_custom_color2 value
 *
 * Since: 3.0
 **/
const GeglColor *
gimp_check_custom_color2 (void)
{
  return (const GeglColor *) _check_custom_color2;
}

/**
 * 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.
 *
 * Returns: (transfer none): the default display ID
 *          The object belongs to libgimp and you should not free it.
 **/
GimpDisplay *
gimp_default_display (void)
{
  return gimp_display_get_by_id (_default_display_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.
 *
 * Returns: 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.
 *
 * Returns: 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.
 *
 * Returns: 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.
 *
 * Returns: timestamp for plug-in window
 *
 * Since: 2.6
 **/
guint32
gimp_user_time (void)
{
  return _timestamp;
}

/**
 * gimp_icon_theme_dir:
 *
 * Returns the directory of the current icon theme.
 *
 * This is a constant value given at plug-in configuration time.
 *
 * Returns: 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.
 *
 * Returns: the executable name
 **/
const gchar *
gimp_get_progname (void)
{
  return progname;
}


/*  private functions  */

static void
gimp_close (void)
{
  if (_gimp_get_debug_flags () & GIMP_DEBUG_QUIT)
    _gimp_debug_stop ();

  gimp_base_exit ();

  _gimp_plug_in_quit (PLUG_IN);

  if (PDB)
    g_object_run_dispose (G_OBJECT (PDB));
  g_clear_object (&PDB);

  g_object_run_dispose (G_OBJECT (PLUG_IN));
  g_clear_object (&PLUG_IN);
}




#ifdef G_OS_WIN32

#ifdef HAVE_EXCHNDL
static LONG WINAPI
gimp_plugin_sigfatal_handler (PEXCEPTION_POINTERS pExceptionInfo)
{
  g_printerr ("Plugin signal handler: %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 = g_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 /* HAVE_EXCHNDL */

#else /* ! G_OS_WIN32 */

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 /* G_OS_WIN32 */

void
_gimp_config (GPConfig *config)
{
  GFile        *file;
  gchar        *path;
  gsize         bpp;
  const guint8 *pixel;
  const guint8 *icc;
  gsize         icc_length;
  const Babl   *format = NULL;
  const Babl   *space  = NULL;

  _tile_width           = config->tile_width;
  _tile_height          = config->tile_height;
  _check_size           = config->check_size;
  _check_type           = config->check_type;

  _show_help_button     = config->show_help_button ? TRUE : FALSE;
  _export_color_profile = config->export_color_profile   ? TRUE : FALSE;
  _export_exif          = config->export_exif      ? TRUE : FALSE;
  _export_xmp           = config->export_xmp       ? TRUE : FALSE;
  _export_iptc          = config->export_iptc      ? TRUE : FALSE;
  _update_metadata      = config->update_metadata  ? TRUE : FALSE;
  _export_comment       = config->export_comment;
  _num_processors       = config->num_processors;
  _default_display_id   = config->default_display_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_clear_object (&_check_custom_color1);
  _check_custom_color1 = gegl_color_new (NULL);
  pixel = g_bytes_get_data (config->check_custom_color1, &bpp);
  icc   = g_bytes_get_data (config->check_custom_icc1, &icc_length);
  space = babl_space_from_icc ((const char *) icc, (int) icc_length,
                               BABL_ICC_INTENT_RELATIVE_COLORIMETRIC,
                               NULL);
  format = babl_format_with_space (config->check_custom_encoding1, space);
  if (bpp != babl_format_get_bytes_per_pixel (format))
    {
      g_warning ("%s: checker board color 1's format expects %d bpp but %"
                 G_GSIZE_FORMAT " bytes were passed.",
                 G_STRFUNC, babl_format_get_bytes_per_pixel (format), bpp);
      gegl_color_set_pixel (_check_custom_color1,
                            babl_format ("R'G'B'A double"),
                            GIMP_CHECKS_CUSTOM_COLOR1);
    }
  else
    {
      gegl_color_set_pixel (_check_custom_color1, format, pixel);
    }

  g_clear_object (&_check_custom_color2);
  _check_custom_color2 = gegl_color_new (NULL);
  pixel = g_bytes_get_data (config->check_custom_color2, &bpp);
  icc   = g_bytes_get_data (config->check_custom_icc2, &icc_length);
  space = babl_space_from_icc ((const char *) icc, (int) icc_length,
                               BABL_ICC_INTENT_RELATIVE_COLORIMETRIC,
                               NULL);
  format = babl_format_with_space (config->check_custom_encoding2, space);
  if (bpp != babl_format_get_bytes_per_pixel (format))
    {
      g_warning ("%s: checker board color 2's format expects %d bpp but %"
                 G_GSIZE_FORMAT " bytes were passed.",
                 G_STRFUNC, babl_format_get_bytes_per_pixel (format), bpp);
      gegl_color_set_pixel (_check_custom_color2,
                            babl_format ("R'G'B'A double"),
                            GIMP_CHECKS_CUSTOM_COLOR2);
    }
  else
    {
      gegl_color_set_pixel (_check_custom_color2, format, pixel);
    }

  g_free (path);
  g_object_unref (file);

  _gimp_shm_open (config->shm_id);
}
