/* GIMP - The GNU Image Manipulation Program * Copyright (C) 1995 Spencer Kimball and Peter Mattis * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ #include "config.h" #include #include #include #ifdef HAVE_UNISTD_H #include #endif #ifdef __GLIBC__ #include #endif #include #include #include #ifdef G_OS_WIN32 #include /* get_osfhandle */ #endif /* G_OS_WIN32 */ #if defined(ENABLE_RELOCATABLE_RESOURCES) && defined(__APPLE__) #include /* dirname */ #include #endif /* __APPLE__ */ #ifndef GIMP_CONSOLE_COMPILATION #include #ifdef GDK_WINDOWING_X11 #include #endif #else #include #endif #include #include "libgimpbase/gimpbase.h" #include "libgimpbase/gimpbase-private.h" #include "pdb/pdb-types.h" #include "config/gimpearlyrc.h" #include "config/gimpconfig-dump.h" #include "core/gimp.h" #include "core/gimpbacktrace.h" #include "pdb/gimppdb.h" #include "pdb/gimpprocedure.h" #include "pdb/internal-procs.h" #include "about.h" #include "app.h" #include "language.h" #include "sanity.h" #include "signals.h" #include "unique.h" #ifdef G_OS_WIN32 #include #include #endif #include "gimp-log.h" #include "gimp-intl.h" #include "gimp-version.h" static gboolean gimp_option_fatal_warnings (const gchar *option_name, const gchar *value, gpointer data, GError **error); static gboolean gimp_option_stack_trace_mode (const gchar *option_name, const gchar *value, gpointer data, GError **error); static gboolean gimp_option_pdb_compat_mode (const gchar *option_name, const gchar *value, gpointer data, GError **error); static gboolean gimp_option_dump_gimprc (const gchar *option_name, const gchar *value, gpointer data, GError **error); static gboolean gimp_option_dump_pdb_procedures_deprecated (const gchar *option_name, const gchar *value, gpointer data, GError **error); static void gimp_show_version_and_exit (void) G_GNUC_NORETURN; static void gimp_show_license_and_exit (void) G_GNUC_NORETURN; static void gimp_init_i18n (void); static void gimp_init_malloc (void); #if defined (G_OS_WIN32) && !defined (GIMP_CONSOLE_COMPILATION) static void gimp_attach_console_window (void); #else #define gimp_attach_console_window() /* as nothing */ #endif static const gchar *system_gimprc = NULL; static const gchar *user_gimprc = NULL; static const gchar *session_name = NULL; static const gchar *batch_interpreter = NULL; static const gchar **batch_commands = NULL; static const gchar **filenames = NULL; static gboolean quit = FALSE; static gboolean as_new = FALSE; static gboolean no_interface = FALSE; static gboolean no_data = FALSE; static gboolean no_fonts = FALSE; static gboolean no_splash = FALSE; static gboolean be_verbose = FALSE; static gboolean new_instance = FALSE; #if defined (USE_SYSV_SHM) || defined (USE_POSIX_SHM) || defined (G_OS_WIN32) static gboolean use_shm = TRUE; #else static gboolean use_shm = FALSE; #endif static gboolean use_cpu_accel = TRUE; static gboolean console_messages = FALSE; static gboolean use_debug_handler = FALSE; #if defined (GIMP_UNSTABLE) || ! defined (GIMP_RELEASE) static gboolean show_playground = TRUE; static gboolean show_debug_menu = TRUE; static GimpStackTraceMode stack_trace_mode = GIMP_STACK_TRACE_QUERY; static GimpPDBCompatMode pdb_compat_mode = GIMP_PDB_COMPAT_WARN; #else static gboolean show_playground = FALSE; static gboolean show_debug_menu = FALSE; static GimpStackTraceMode stack_trace_mode = GIMP_STACK_TRACE_NEVER; static GimpPDBCompatMode pdb_compat_mode = GIMP_PDB_COMPAT_ON; #endif static const GOptionEntry main_entries[] = { { "version", 'v', G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (GOptionArgFunc) gimp_show_version_and_exit, N_("Show version information and exit"), NULL }, { "license", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, (GOptionArgFunc) gimp_show_license_and_exit, N_("Show license information and exit"), NULL }, { "verbose", 0, 0, G_OPTION_ARG_NONE, &be_verbose, N_("Be more verbose"), NULL }, { "new-instance", 'n', 0, G_OPTION_ARG_NONE, &new_instance, N_("Start a new GIMP instance"), NULL }, { "as-new", 'a', 0, G_OPTION_ARG_NONE, &as_new, N_("Open images as new"), NULL }, { "no-interface", 'i', #ifdef GIMP_CONSOLE_COMPILATION /* The CLI option is not removed for gimp-console because it allows * to set the same options on either the GUI or the console * binaries, as a no-brainer. We in fact also use this in build * scripts which may make use of either one of the binaries to * construct some images. * Yet it's unnecessary to show this option in the CLI tool usage as * it's basically a no-op. */ G_OPTION_FLAG_HIDDEN, #else 0, #endif G_OPTION_ARG_NONE, &no_interface, N_("Run without a user interface"), NULL }, { "no-data", 'd', 0, G_OPTION_ARG_NONE, &no_data, N_("Do not load brushes, gradients, patterns, ..."), NULL }, { "no-fonts", 'f', 0, G_OPTION_ARG_NONE, &no_fonts, N_("Do not load any fonts"), NULL }, { "no-splash", 's', 0, G_OPTION_ARG_NONE, &no_splash, N_("Do not show a splash screen"), NULL }, { "no-shm", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &use_shm, N_("Do not use shared memory between GIMP and plug-ins"), NULL }, { "no-cpu-accel", 0, G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &use_cpu_accel, N_("Do not use special CPU acceleration functions"), NULL }, { "session", 0, 0, G_OPTION_ARG_FILENAME, &session_name, N_("Use an alternate sessionrc file"), "" }, { "gimprc", 'g', 0, G_OPTION_ARG_FILENAME, &user_gimprc, N_("Use an alternate user gimprc file"), "" }, { "system-gimprc", 0, 0, G_OPTION_ARG_FILENAME, &system_gimprc, N_("Use an alternate system gimprc file"), "" }, { "batch", 'b', 0, G_OPTION_ARG_STRING_ARRAY, &batch_commands, N_("Batch command to run (can be used multiple times)"), "" }, { "batch-interpreter", 0, 0, G_OPTION_ARG_STRING, &batch_interpreter, N_("The procedure to process batch commands with"), "" }, { "quit", 0, 0, G_OPTION_ARG_NONE, &quit, N_("Quit immediately after performing requested actions"), NULL }, { "console-messages", 'c', 0, G_OPTION_ARG_NONE, &console_messages, N_("Send messages to console instead of using a dialog"), NULL }, { "pdb-compat-mode", 0, 0, G_OPTION_ARG_CALLBACK, gimp_option_pdb_compat_mode, /* don't translate the mode names (off|on|warn) */ N_("PDB compatibility mode (off|on|warn)"), "" }, { "stack-trace-mode", 0, 0, G_OPTION_ARG_CALLBACK, gimp_option_stack_trace_mode, /* don't translate the mode names (never|query|always) */ N_("Debug in case of a crash (never|query|always)"), "" }, { "debug-handlers", 0, 0, G_OPTION_ARG_NONE, &use_debug_handler, N_("Enable non-fatal debugging signal handlers"), NULL }, { "g-fatal-warnings", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, gimp_option_fatal_warnings, N_("Make all warnings fatal"), NULL }, { "dump-gimprc", 0, G_OPTION_FLAG_NO_ARG, G_OPTION_ARG_CALLBACK, gimp_option_dump_gimprc, N_("Output a gimprc file with default settings"), NULL }, { "dump-gimprc-system", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, gimp_option_dump_gimprc, NULL, NULL }, { "dump-gimprc-manpage", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, gimp_option_dump_gimprc, NULL, NULL }, { "dump-pdb-procedures-deprecated", 0, G_OPTION_FLAG_NO_ARG | G_OPTION_FLAG_HIDDEN, G_OPTION_ARG_CALLBACK, gimp_option_dump_pdb_procedures_deprecated, N_("Output a sorted list of deprecated procedures in the PDB"), NULL }, { "show-playground", 0, 0, G_OPTION_ARG_NONE, &show_playground, N_("Show a preferences page with experimental features"), NULL }, { "show-debug-menu", 0, 0, G_OPTION_ARG_NONE, &show_debug_menu, N_("Show an image submenu with debug actions"), NULL }, { G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_FILENAME_ARRAY, &filenames, NULL, NULL }, { NULL } }; #if defined(ENABLE_RELOCATABLE_RESOURCES) && defined(__APPLE__) static void gimp_macos_setenv (const char * progname) { /* helper to set environment variables for GIMP to be relocatable. * Due to the changes on macOS 10.15, it is not recommended to set it in * a shell wrapper inside Contents/MacOS (like AppImage's AppRun) anymore. * LSEnvironment on Info.plist, limited to bundle scope, would not be enough. * That way, we make sure our python is called instead of system one etc */ gchar *resolved_path; /* on some OSX installations open file limit is 256 and GIMP needs more */ struct rlimit limit; limit.rlim_cur = 10000; limit.rlim_max = 10000; setrlimit (RLIMIT_NOFILE, &limit); resolved_path = g_canonicalize_filename (progname, NULL); if (resolved_path && ! g_getenv ("GIMP_NO_WRAPPER")) { gchar *path; gchar *tmp; gchar *bin_dir; gchar *lib_dir; gchar *share_dir; gchar *etc_dir; size_t path_len; struct stat sb; bin_dir = g_path_get_dirname (resolved_path); tmp = g_strdup_printf ("%s/lib", gimp_installation_directory()); lib_dir = g_canonicalize_filename (tmp, NULL); g_free (tmp); tmp = g_strdup_printf ("%s/share", gimp_installation_directory()); share_dir = g_canonicalize_filename (tmp, NULL); g_free (tmp); tmp = g_strdup_printf ("%s/etc", gimp_installation_directory()); etc_dir = g_canonicalize_filename (tmp, NULL); g_free (tmp); /* Detect if we are running from bundle or from prefix */ if (g_str_has_suffix (bin_dir, "MacOS")) { g_print ("GIMP is started as MacOS application\n"); } else { tmp = g_strdup_printf ("%s/share", gimp_installation_directory()); share_dir = g_canonicalize_filename (tmp, NULL); g_free (tmp); if (share_dir && !stat (share_dir, &sb) && S_ISDIR (sb.st_mode)) { g_free (share_dir); g_print ("GIMP is started in the build directory\n"); tmp = g_strdup_printf ("%s", gimp_installation_directory()); /* running in build dir */ share_dir = g_canonicalize_filename (tmp, NULL); g_free (tmp); } else { g_free (share_dir); return; } } /* Minimum runtime paths */ path_len = strlen (g_getenv ("PATH") ? g_getenv ("PATH") : "") + strlen (bin_dir) + 2; path = g_try_malloc (path_len); if (path == NULL) { g_warning ("Failed to allocate memory"); app_exit (EXIT_FAILURE); } if (g_getenv ("PATH")) g_snprintf (path, path_len, "%s:%s", bin_dir, g_getenv ("PATH")); else g_snprintf (path, path_len, "%s", bin_dir); g_free (bin_dir); g_setenv ("PATH", path, TRUE); g_free (path); if (g_getenv ("XDG_DATA_DIRS")) tmp = g_strdup_printf ("%s:%s", share_dir, g_getenv ("XDG_DATA_DIRS")); else tmp = g_strdup_printf ("%s", share_dir); g_setenv ("XDG_DATA_DIRS", tmp, TRUE); g_free (tmp); if (g_getenv ("HOME") != NULL) { tmp = g_strdup_printf ("%s/Library/Application Support/GIMP/%s/cache", g_getenv ("HOME"), GIMP_APP_VERSION); g_setenv ("XDG_CACHE_HOME", tmp, TRUE); g_free (tmp); } /* Bare minimum to run GTK apps */ tmp = g_strdup_printf ("%s/gio/modules", lib_dir); g_setenv ("GIO_MODULE_DIR", tmp, TRUE); g_free (tmp); tmp = g_strdup_printf ("%s/gdk-pixbuf-2.0/2.10.0/loaders.cache", lib_dir); g_setenv ("GDK_PIXBUF_MODULE_FILE", tmp, TRUE); g_free (tmp); tmp = g_strdup_printf ("%s/gtk-3.0/3.0.0", lib_dir); g_setenv ("GTK_PATH", tmp, TRUE); g_free (tmp); tmp = g_strdup_printf ("%s/gtk-3.0/3.0.0/immodules.cache", lib_dir); g_setenv ("GTK_IM_MODULE_FILE", tmp, TRUE); g_free (tmp); tmp = g_strdup ("quartz"); g_setenv ("GTK_IM_MODULE", tmp, TRUE); g_free (tmp); /* Other needed runtime paths (related to features) */ tmp = g_strdup_printf ("%s/fonts", etc_dir); g_setenv ("FONTCONFIG_PATH", tmp, TRUE); g_free (tmp); tmp = g_strdup_printf ("%s/libthai", share_dir); g_setenv ("LIBTHAI_DICTDIR", tmp, TRUE); g_free (tmp); tmp = g_strdup_printf ("%s/ghostscript/Resource/Init", share_dir); g_setenv ("GS_LIB", tmp, TRUE); g_free (tmp); tmp = g_strdup_printf ("%s/libwmf/fonts", share_dir); g_setenv ("WMF_FONTDIR", tmp, TRUE); g_free (tmp); tmp = g_strdup_printf ("%s/girepository-1.0", lib_dir); g_setenv ("GI_TYPELIB_PATH", tmp, TRUE); g_free (tmp); tmp = g_strdup_printf ("%s/Frameworks/Python.framework/Versions/%s", gimp_installation_directory(), PYTHON_VERSION); if (tmp && !stat (tmp, &sb) && S_ISDIR (sb.st_mode)) { g_setenv ("PYTHONHOME", tmp, TRUE); } g_free (tmp); g_free (lib_dir); g_free (share_dir); g_free (etc_dir); } g_free (resolved_path); } #endif /* gimp_early_configuration () is executed as soon as we can read * the "gimprc" files, but before any library initialization takes * place */ static void gimp_early_configuration (void) { GFile *system_gimprc_file = NULL; GFile *user_gimprc_file = NULL; GimpEarlyRc *earlyrc; gchar *language; if (system_gimprc) system_gimprc_file = g_file_new_for_commandline_arg (system_gimprc); if (user_gimprc) user_gimprc_file = g_file_new_for_commandline_arg (user_gimprc); /* GimpEarlyRc is responsible for reading "gimprc" files for the * sole purpose of getting some configuration data that is needed * in the early initialization phase */ earlyrc = gimp_early_rc_new (system_gimprc_file, user_gimprc_file, be_verbose); /* Language needs to be determined first, before any GimpContext is * instantiated (which happens when the Gimp object is created) * because its properties need to be properly localized in the * settings language (if different from system language). Otherwise we * end up with pieces of GUI always using the system language (cf. bug * 787457) */ language = gimp_early_rc_get_language (earlyrc); /* change the locale if a language if specified */ language_init (language, NULL); if (language) g_free (language); #if defined (G_OS_WIN32) && !defined (GIMP_CONSOLE_COMPILATION) #if GTK_MAJOR_VERSION > 3 #warning For GTK4 and above use the proper backend-specific API instead of the GDK_WIN32_TABLET_INPUT_API environment variable #endif /* Set a GdkWin32-specific environment variable to specify * the desired pen / touch input API to use on Windows */ if (gtk_get_major_version () == 3 && (gtk_get_minor_version () > 24 || (gtk_get_minor_version () == 24 && gtk_get_micro_version () >= 30))) { GimpWin32PointerInputAPI api = gimp_early_rc_get_win32_pointer_input_api (earlyrc); switch (api) { case GIMP_WIN32_POINTER_INPUT_API_WINTAB: g_setenv ("GDK_WIN32_TABLET_INPUT_API", "wintab", TRUE); break; case GIMP_WIN32_POINTER_INPUT_API_WINDOWS_INK: g_setenv ("GDK_WIN32_TABLET_INPUT_API", "winpointer", TRUE); break; } } #endif g_object_unref (earlyrc); g_clear_object (&system_gimprc_file); g_clear_object (&user_gimprc_file); } static gboolean gimp_options_group_parse_hook (GOptionContext *context, GOptionGroup *group, gpointer data, GError **error) { /* early initialization from data stored in "gimprc" files */ gimp_early_configuration (); return TRUE; } int main (int argc, char **argv) { GOptionContext *context; const gchar *abort_message; GFile *system_gimprc_file = NULL; GFile *user_gimprc_file = NULL; GOptionGroup *gimp_group = NULL; gchar *backtrace_file = NULL; GError *error = NULL; #ifdef G_OS_WIN32 gchar *utf8_name; wchar_t *name; #endif gint retval; gint i; #if defined(ENABLE_RELOCATABLE_RESOURCES) && defined(__APPLE__) gint newargc; #endif gimp_attach_console_window (); #if defined(ENABLE_RELOCATABLE_RESOURCES) && defined(__APPLE__) /* remove MacOS session identifier from the command line args */ newargc = 0; for (gint i = 0; i < argc; i++) { if (!g_str_has_prefix (argv[i], "-psn_")) { argv[newargc] = argv[i]; newargc++; } } if (argc > newargc) { argv[newargc] = NULL; /* glib expects NULL terminated array */ argc = newargc; } gimp_macos_setenv (argv[0]); #endif #if defined (__GNUC__) && defined (_WIN64) /* mingw-w64, at least the unstable build from late July 2008, * starts subsystem:windows programs in main(), but passes them * bogus argc and argv. __argc and __argv are OK, though, so just * use them. */ argc = __argc; argv = __argv; #endif /* Initialize GimpBacktrace early on. In particular, we want the * Windows backend to catch the SET_THREAD_NAME exceptions of newly * created threads. */ gimp_backtrace_init (); /* Start signal handlers early. */ gimp_init_signal_handlers (&backtrace_file); #ifdef G_OS_WIN32 /* Make Inno aware of gimp process avoiding broken install/unninstall */ utf8_name = g_strdup_printf ("GIMP-%s", GIMP_MUTEX_VERSION); name = g_utf8_to_utf16 (utf8_name, -1, NULL, NULL, NULL); CreateMutexW (NULL, FALSE, name); g_free (utf8_name); g_free (name); /* Enable Anti-Aliasing*/ g_setenv ("PANGOCAIRO_BACKEND", "fc", TRUE); /* Reduce risks */ SetDllDirectoryW (L""); { const gchar *install_dir; gchar *bin_dir; LPWSTR w_bin_dir; #ifdef ENABLE_RELOCATABLE_RESOURCES size_t path_len; gchar *path; #endif /* On Windows, set DLL search path to $INSTALLDIR/bin so that .exe * plug-ins processes in the plug-ins directory can find libgimp and * file library DLLs. */ 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); } #ifdef ENABLE_RELOCATABLE_RESOURCES /* We also set external PATH variable to other processes just in case * (see #14716 and many other issues labeled as 'DLL Hell'). */ path_len = strlen (g_getenv ("PATH") ? g_getenv ("PATH") : "") + strlen (bin_dir) + 2; path = g_try_malloc (path_len); if (path == NULL) { g_warning ("Failed to allocate memory"); app_exit (EXIT_FAILURE); } if (g_getenv ("PATH")) g_snprintf (path, path_len, "%s;%s", bin_dir, g_getenv ("PATH")); else g_snprintf (path, path_len, "%s", bin_dir); g_setenv ("PATH", path, TRUE); g_free (path); #endif g_free (bin_dir); } #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 /* 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"); } #endif gimp_init_malloc (); gimp_env_init (FALSE); gimp_log_init (); gimp_init_i18n (); g_set_application_name (GIMP_NAME); #ifdef G_OS_WIN32 argv = g_win32_get_command_line (); #else argv = g_strdupv (argv); #endif g_set_prgname (GIMP_DESKTOP_NAME); /* Check argv[] for "--verbose" first */ for (i = 1; i < argc; i++) { const gchar *arg = argv[i]; if (arg[0] != '-') continue; if ((strcmp (arg, "--verbose") == 0) || (strcmp (arg, "-v") == 0)) { be_verbose = TRUE; } } /* Check argv[] for "--no-interface" before trying to initialize gtk+. */ for (i = 1; i < argc; i++) { const gchar *arg = argv[i]; if (arg[0] != '-') continue; if ((strcmp (arg, "--no-interface") == 0) || (strcmp (arg, "-i") == 0)) { no_interface = TRUE; } else if ((strcmp (arg, "--version") == 0) || (strcmp (arg, "-v") == 0)) { gimp_show_version_and_exit (); } #if defined (G_OS_WIN32) && !defined (GIMP_CONSOLE_COMPILATION) else if ((strcmp (arg, "--help") == 0) || (strcmp (arg, "-?") == 0) || (strncmp (arg, "--help-", 7) == 0)) { gimp_attach_console_window (); } #endif } #ifdef GIMP_CONSOLE_COMPILATION no_interface = TRUE; #endif context = g_option_context_new (_("[FILE|URI...]")); g_option_context_set_summary (context, GIMP_NAME); g_option_context_add_main_entries (context, main_entries, GETTEXT_PACKAGE); /* The GIMP option group is just an empty option group, created for * the sole purpose of running a post-parse hook before any other of * dependent libraries are run. This makes it possible to apply * options from configuration data obtained from "gimprc" files, * before other libraries have a chance to run some of their * initialization code. */ gimp_group = g_option_group_new ("gimp", "", "", NULL, NULL); g_option_group_set_parse_hooks (gimp_group, NULL, gimp_options_group_parse_hook); g_option_context_add_group (context, gimp_group); app_libs_init (context, no_interface); if (! g_option_context_parse_strv (context, &argv, &error)) { if (error) { gimp_attach_console_window (); g_print ("%s\n", error->message); g_error_free (error); } else { g_print ("%s\n", _("GIMP could not initialize the graphical user interface.\n" "Make sure a proper setup for your display environment " "exists.")); } app_exit (EXIT_FAILURE); } #if GLIB_CHECK_VERSION(2,72,0) /* g_set_prgname() can only be called several times since 2.72.0. */ #ifndef GIMP_CONSOLE_COMPILATION if (! no_interface) { GKeyFile *flatpak_keyfile; g_return_val_if_fail (gdk_display_get_default () != NULL, EXIT_FAILURE); flatpak_keyfile = g_key_file_new (); if ( #ifdef GDK_WINDOWING_X11 ! GDK_IS_X11_DISPLAY (gdk_display_get_default ()) && #endif g_key_file_load_from_file (flatpak_keyfile, "/.flatpak-info", G_KEY_FILE_NONE, NULL)) { /* Flatpak renames the desktop file. The .flatpak-info file * tells us the right desktop name we must associate our process * to, especially as we have flatpaks with different IDs. * * This logic should not apply on X11 which will instead * apparently use the StartupWMClass set in the desktop file and * expect it to be the same as the name set by g_set_prgname(). * * Cf. #13183 and #14233. */ gchar *flatpak_name = g_key_file_get_string (flatpak_keyfile, "Application", "name", NULL); if (flatpak_name != NULL) { g_set_prgname (flatpak_name); g_free (flatpak_name); } /* The else case should never happen unless we are in some kind * of broken flatpak environment or somehow in a non-flatpak * environment with a .flatpak-info file at the root, which * seems improbable. Fail silently. */ } g_key_file_free (flatpak_keyfile); } #endif #endif if (no_interface) new_instance = TRUE; #ifndef GIMP_CONSOLE_COMPILATION if (! new_instance && gimp_unique_open (filenames, as_new)) { int success = EXIT_SUCCESS; if (be_verbose) g_print ("%s\n", _("Another GIMP instance is already running.")); if (batch_commands && ! gimp_unique_batch_run (batch_interpreter, batch_commands)) success = EXIT_FAILURE; gdk_notify_startup_complete (); return success; } #endif abort_message = sanity_check_early (); if (abort_message) app_abort (no_interface, abort_message); if (system_gimprc) system_gimprc_file = g_file_new_for_commandline_arg (system_gimprc); if (user_gimprc) user_gimprc_file = g_file_new_for_commandline_arg (user_gimprc); retval = app_run (argv[0], filenames, system_gimprc_file, user_gimprc_file, session_name, batch_interpreter, batch_commands, quit, as_new, no_interface, no_data, no_fonts, no_splash, be_verbose, use_shm, use_cpu_accel, console_messages, use_debug_handler, show_playground, show_debug_menu, stack_trace_mode, pdb_compat_mode, backtrace_file); gimp_env_exit (FALSE); g_free (backtrace_file); g_clear_object (&system_gimprc_file); g_clear_object (&user_gimprc_file); g_strfreev (argv); g_option_context_free (context); return retval; } #ifdef G_OS_WIN32 /* Provide WinMain in case we build GIMP as a subsystem:windows * application. Well, we do. When built with mingw, though, user code * execution still starts in main() in that case. So WinMain() gets * used on MSVC builds only. */ int WINAPI WinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { return main (__argc, __argv); } #ifndef GIMP_CONSOLE_COMPILATION static void wait_console_window (void) { g_print (_ ("(Type any character to close this window)\n")); } static void gimp_attach_console_window (void) { /* If run on non-native shell, do nothing */ if (g_getenv ("TERM") || g_getenv ("SHELL")) { g_printerr ("Non-native shell detected, GIMP may " "behave unexpectedly on Unix shells in Windows.\n"); return; } /* If run on native shell, attach to it */ if (AttachConsole (ATTACH_PARENT_PROCESS) != 0) { /* 'r' is needed to prevent interleaving and '+' to support colors */ #ifndef _UCRT freopen ("CONOUT$", "r+", stdout); freopen ("CONOUT$", "r+", stderr); #else FILE* fileOut; FILE* fileErr; errno_t errOut = freopen_s (&fileOut, "CONOUT$", "r+", stdout); errno_t errErr = freopen_s (&fileErr, "CONOUT$", "r+", stderr); if (errOut != 0 || errErr != 0) g_warning ("Failed to redirect streams to CONOUT$. stdout: %d, stderr: %d", errOut, errErr); #endif _flushall (); { /* CTRL+C handling */ HANDLE hIn = GetStdHandle (STD_INPUT_HANDLE); DWORD mode; GetConsoleMode (hIn, &mode); mode |= ENABLE_PROCESSED_INPUT; SetConsoleMode (hIn, mode); } atexit (wait_console_window); } } #endif #endif /* G_OS_WIN32 */ static gboolean gimp_option_fatal_warnings (const gchar *option_name, const gchar *value, gpointer data, GError **error) { 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); return TRUE; } static gboolean gimp_option_stack_trace_mode (const gchar *option_name, const gchar *value, gpointer data, GError **error) { if (strcmp (value, "never") == 0) stack_trace_mode = GIMP_STACK_TRACE_NEVER; else if (strcmp (value, "query") == 0) stack_trace_mode = GIMP_STACK_TRACE_QUERY; else if (strcmp (value, "always") == 0) stack_trace_mode = GIMP_STACK_TRACE_ALWAYS; else return FALSE; return TRUE; } static gboolean gimp_option_pdb_compat_mode (const gchar *option_name, const gchar *value, gpointer data, GError **error) { if (! strcmp (value, "off")) pdb_compat_mode = GIMP_PDB_COMPAT_OFF; else if (! strcmp (value, "on")) pdb_compat_mode = GIMP_PDB_COMPAT_ON; else if (! strcmp (value, "warn")) pdb_compat_mode = GIMP_PDB_COMPAT_WARN; else return FALSE; return TRUE; } static gboolean gimp_option_dump_gimprc (const gchar *option_name, const gchar *value, gpointer data, GError **error) { GimpConfigDumpFormat format = GIMP_CONFIG_DUMP_NONE; gimp_attach_console_window (); if (strcmp (option_name, "--dump-gimprc") == 0) format = GIMP_CONFIG_DUMP_GIMPRC; if (strcmp (option_name, "--dump-gimprc-system") == 0) format = GIMP_CONFIG_DUMP_GIMPRC_SYSTEM; else if (strcmp (option_name, "--dump-gimprc-manpage") == 0) format = GIMP_CONFIG_DUMP_GIMPRC_MANPAGE; if (format) { Gimp *gimp; gboolean success; babl_init (); gimp = g_object_new (GIMP_TYPE_GIMP, NULL); gimp_load_config (gimp, NULL, NULL); success = gimp_config_dump (G_OBJECT (gimp), format); g_object_unref (gimp); app_exit (success ? EXIT_SUCCESS : EXIT_FAILURE); } return FALSE; } static gboolean gimp_option_dump_pdb_procedures_deprecated (const gchar *option_name, const gchar *value, gpointer data, GError **error) { Gimp *gimp; GList *deprecated_procs; GList *iter; babl_init (); gimp = g_object_new (GIMP_TYPE_GIMP, NULL); gimp_load_config (gimp, NULL, NULL); /* Make sure to turn on compatibility mode so deprecated procedures * are included */ gimp->pdb_compat_mode = GIMP_PDB_COMPAT_ON; /* Initialize the list of procedures */ internal_procs_init (gimp->pdb); /* Get deprecated procedures */ deprecated_procs = gimp_pdb_get_deprecated_procedures (gimp->pdb); for (iter = deprecated_procs; iter; iter = g_list_next (iter)) { GimpProcedure *procedure = GIMP_PROCEDURE (iter->data); g_print ("%s\n", gimp_object_get_name (procedure)); } g_list_free (deprecated_procs); g_object_unref (gimp); app_exit (EXIT_SUCCESS); return FALSE; } static void gimp_show_version_and_exit (void) { gimp_attach_console_window (); gimp_version_show (be_verbose); app_exit (EXIT_SUCCESS); } static void gimp_show_license_and_exit (void) { gimp_attach_console_window (); gimp_version_show (be_verbose); g_print ("\n%s\n\n", GIMP_LICENSE); app_exit (EXIT_SUCCESS); } static void gimp_init_malloc (void) { #ifdef GIMP_GLIB_MEM_PROFILER g_mem_set_vtable (glib_mem_profiler_table); g_atexit (g_mem_profile); #endif #ifdef __GLIBC__ /* Tweak memory allocation so that memory allocated in chunks >= 4k * (64x64 pixel 1bpp tile) gets returned to the system when free()'d. * * The default value for M_MMAP_THRESHOLD in glibc-2.3 is 128k. * This is said to be an empirically derived value that works well * in most systems. Lowering it to 4k is thus probably not the ideal * solution. * * An alternative to tuning this parameter would be to use * malloc_trim(), for example after releasing a large tile-manager. */ #if 0 mallopt (M_MMAP_THRESHOLD, TILE_WIDTH * TILE_HEIGHT); #endif #endif } static void gimp_init_i18n (void) { /* We may change the locale later if the user specifies a language * in the gimprc file. Here we are just initializing the locale * according to the environment variables and set up the paths to * the message catalogs. */ 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_bind_text_domain (GETTEXT_PACKAGE, gimp_locale_directory ()); #ifdef HAVE_BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8"); #endif #ifdef HAVE_ISO_CODES /* This iso-codes domain is the only external text domain our core * code is using. Let's make usre it's properly initialized. */ #ifdef ENABLE_RELOCATABLE_RESOURCES gimp_bind_text_domain ("iso_639_3", gimp_locale_directory ()); #else gimp_bind_text_domain ("iso_639_3", ISO_CODES_LOCALEDIR); #endif #ifdef HAVE_BIND_TEXTDOMAIN_CODESET bind_textdomain_codeset ("iso_639_3", "UTF-8"); #endif #endif textdomain (GETTEXT_PACKAGE); }