summaryrefslogtreecommitdiffstats
path: root/app/app.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/app.c')
-rw-r--r--app/app.c523
1 files changed, 523 insertions, 0 deletions
diff --git a/app/app.c b/app/app.c
new file mode 100644
index 0000000..3bb210b
--- /dev/null
+++ b/app/app.c
@@ -0,0 +1,523 @@
+/* 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 <https://www.gnu.org/licenses/>.
+ */
+
+#include "config.h"
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <locale.h>
+
+#ifdef HAVE_SYS_PARAM_H
+#include <sys/param.h>
+#endif
+
+#ifdef HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+
+#include <glib/gstdio.h>
+#include <gio/gio.h>
+#include <gegl.h>
+
+#include <gdk-pixbuf/gdk-pixbuf.h>
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#include <winnls.h>
+#endif
+
+#undef GIMP_DISABLE_DEPRECATED /* for compat enums */
+#include "libgimpbase/gimpbase.h"
+#define GIMP_DISABLE_DEPRECATED
+#include "libgimpconfig/gimpconfig.h"
+
+#include "core/core-types.h"
+
+#include "config/gimplangrc.h"
+#include "config/gimprc.h"
+
+#include "gegl/gimp-gegl.h"
+
+#include "core/gimp.h"
+#include "core/gimp-batch.h"
+#include "core/gimp-user-install.h"
+#include "core/gimpimage.h"
+
+#include "file/file-open.h"
+
+#ifndef GIMP_CONSOLE_COMPILATION
+#include "dialogs/user-install-dialog.h"
+
+#include "gui/gui.h"
+#endif
+
+#include "app.h"
+#include "errors.h"
+#include "language.h"
+#include "sanity.h"
+#include "gimp-debug.h"
+
+#include "gimp-intl.h"
+#include "gimp-update.h"
+
+
+/* local prototypes */
+
+static void app_init_update_noop (const gchar *text1,
+ const gchar *text2,
+ gdouble percentage);
+static void app_restore_after_callback (Gimp *gimp,
+ GimpInitStatusFunc status_callback);
+static gboolean app_exit_after_callback (Gimp *gimp,
+ gboolean kill_it,
+ GMainLoop **loop);
+
+GType gimp_convert_dither_type_compat_get_type (void); /* compat cruft */
+GType gimp_layer_mode_effects_get_type (void); /* compat cruft */
+
+
+/* local variables */
+
+static GObject *initial_screen = NULL;
+static gint initial_monitor = 0;
+
+
+/* public functions */
+
+void
+app_libs_init (GOptionContext *context,
+ gboolean no_interface)
+{
+ GQuark quark;
+
+ /* disable OpenCL before GEGL is even initialized; this way we only
+ * enable if wanted in gimprc, instead of always enabling, and then
+ * disabling again if wanted in gimprc
+ */
+ g_object_set (gegl_config (),
+ "use-opencl", FALSE,
+ "application-license", "GPL3",
+ NULL);
+
+ g_option_context_add_group (context, gegl_get_option_group ());
+
+#ifndef GIMP_CONSOLE_COMPILATION
+ if (! no_interface)
+ {
+ gui_libs_init (context);
+ }
+#endif
+
+ /* keep compat enum code in sync with pdb/enumcode.pl */
+ quark = g_quark_from_static_string ("gimp-compat-enum");
+
+ g_type_set_qdata (GIMP_TYPE_CONVERT_DITHER_TYPE, quark,
+ (gpointer) gimp_convert_dither_type_compat_get_type ());
+ g_type_set_qdata (GIMP_TYPE_LAYER_MODE, quark,
+ (gpointer) gimp_layer_mode_effects_get_type ());
+}
+
+void
+app_abort (gboolean no_interface,
+ const gchar *abort_message)
+{
+#ifndef GIMP_CONSOLE_COMPILATION
+ if (no_interface)
+#endif
+ {
+ g_print ("%s\n\n", abort_message);
+ }
+#ifndef GIMP_CONSOLE_COMPILATION
+ else
+ {
+ gui_abort (abort_message);
+ }
+#endif
+
+ app_exit (EXIT_FAILURE);
+}
+
+void
+app_exit (gint status)
+{
+ exit (status);
+}
+
+void
+app_run (const gchar *full_prog_name,
+ const gchar **filenames,
+ GFile *alternate_system_gimprc,
+ GFile *alternate_gimprc,
+ const gchar *session_name,
+ const gchar *batch_interpreter,
+ const gchar **batch_commands,
+ gboolean as_new,
+ gboolean no_interface,
+ gboolean no_data,
+ gboolean no_fonts,
+ gboolean no_splash,
+ gboolean be_verbose,
+ gboolean use_shm,
+ gboolean use_cpu_accel,
+ gboolean console_messages,
+ gboolean use_debug_handler,
+ gboolean show_playground,
+ gboolean show_debug_menu,
+ GimpStackTraceMode stack_trace_mode,
+ GimpPDBCompatMode pdb_compat_mode,
+ const gchar *backtrace_file)
+{
+ GimpInitStatusFunc update_status_func = NULL;
+ Gimp *gimp;
+ GMainLoop *loop;
+ GMainLoop *run_loop;
+ GFile *default_folder = NULL;
+ GFile *gimpdir;
+ const gchar *abort_message;
+ GimpLangRc *temprc;
+ gchar *language = NULL;
+ GError *font_error = NULL;
+
+ if (filenames && filenames[0] && ! filenames[1] &&
+ g_file_test (filenames[0], G_FILE_TEST_IS_DIR))
+ {
+ if (g_path_is_absolute (filenames[0]))
+ {
+ default_folder = g_file_new_for_path (filenames[0]);
+ }
+ else
+ {
+ gchar *absolute = g_build_path (G_DIR_SEPARATOR_S,
+ g_get_current_dir (),
+ filenames[0],
+ NULL);
+ default_folder = g_file_new_for_path (absolute);
+ g_free (absolute);
+ }
+
+ filenames = NULL;
+ }
+
+ /* 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). Therefore we do a first pass on "gimprc" file for the sole
+ * purpose of getting the settings language, so that we can initialize
+ * it before anything else.
+ */
+ temprc = gimp_lang_rc_new (alternate_system_gimprc,
+ alternate_gimprc,
+ be_verbose);
+ language = gimp_lang_rc_get_language (temprc);
+ g_object_unref (temprc);
+
+ /* change the locale if a language if specified */
+ language_init (language);
+ if (language)
+ g_free (language);
+
+ /* Create an instance of the "Gimp" object which is the root of the
+ * core object system
+ */
+ gimp = gimp_new (full_prog_name,
+ session_name,
+ default_folder,
+ be_verbose,
+ no_data,
+ no_fonts,
+ no_interface,
+ use_shm,
+ use_cpu_accel,
+ console_messages,
+ show_playground,
+ show_debug_menu,
+ stack_trace_mode,
+ pdb_compat_mode);
+
+ if (default_folder)
+ g_object_unref (default_folder);
+
+ gimp_cpu_accel_set_use (use_cpu_accel);
+
+ /* Check if the user's gimp_directory exists
+ */
+ gimpdir = gimp_directory_file (NULL);
+
+ if (g_file_query_file_type (gimpdir, G_FILE_QUERY_INFO_NONE, NULL) !=
+ G_FILE_TYPE_DIRECTORY)
+ {
+ GimpUserInstall *install = gimp_user_install_new (G_OBJECT (gimp),
+ be_verbose);
+
+#ifdef GIMP_CONSOLE_COMPILATION
+ gimp_user_install_run (install);
+#else
+ if (! (no_interface ?
+ gimp_user_install_run (install) :
+ user_install_dialog_run (install)))
+ exit (EXIT_FAILURE);
+#endif
+
+ gimp_user_install_free (install);
+ }
+
+ g_object_unref (gimpdir);
+
+ gimp_load_config (gimp, alternate_system_gimprc, alternate_gimprc);
+
+ /* Initialize the error handling after creating/migrating the config
+ * directory because it will create some folders for backup and crash
+ * logs in advance. Therefore running this before
+ * gimp_user_install_new() would break migration as well as initial
+ * folder creations.
+ *
+ * It also needs to be run after gimp_load_config() because error
+ * handling is based on Preferences. It means that early loading code
+ * is not handled by our debug code, but that's not a big deal.
+ */
+ errors_init (gimp, full_prog_name, use_debug_handler,
+ stack_trace_mode, backtrace_file);
+
+ /* run the late-stage sanity check. it's important that this check is run
+ * after the call to language_init() (see comment in sanity_check_late().)
+ */
+ abort_message = sanity_check_late ();
+ if (abort_message)
+ app_abort (no_interface, abort_message);
+
+ /* initialize lowlevel stuff */
+ gimp_gegl_init (gimp);
+
+ /* Connect our restore_after callback before gui_init() connects
+ * theirs, so ours runs first and can grab the initial monitor
+ * before the GUI's restore_after callback resets it.
+ */
+ g_signal_connect_after (gimp, "restore",
+ G_CALLBACK (app_restore_after_callback),
+ NULL);
+
+#ifndef GIMP_CONSOLE_COMPILATION
+ if (! no_interface)
+ update_status_func = gui_init (gimp, no_splash);
+#endif
+
+ if (! update_status_func)
+ update_status_func = app_init_update_noop;
+
+ /* Create all members of the global Gimp instance which need an already
+ * parsed gimprc, e.g. the data factories
+ */
+ gimp_initialize (gimp, update_status_func);
+
+ /* Load all data files
+ */
+ gimp_restore (gimp, update_status_func, &font_error);
+
+ /* enable autosave late so we don't autosave when the
+ * monitor resolution is set in gui_init()
+ */
+ gimp_rc_set_autosave (GIMP_RC (gimp->edit_config), TRUE);
+
+ /* check for updates *after* enabling config autosave, so that the timestamp
+ * is saved
+ */
+ gimp_update_auto_check (gimp->edit_config);
+
+ loop = run_loop = g_main_loop_new (NULL, FALSE);
+
+ g_signal_connect_after (gimp, "exit",
+ G_CALLBACK (app_exit_after_callback),
+ &run_loop);
+
+#ifndef GIMP_CONSOLE_COMPILATION
+ if (run_loop && ! no_interface)
+ {
+ /* Before opening images from command line, check for salvaged images
+ * and query interactively to know if we should recover or discard
+ * them.
+ */
+ GList *recovered_files;
+ GList *iter;
+
+ recovered_files = errors_recovered ();
+ if (recovered_files &&
+ gui_recover (g_list_length (recovered_files)))
+ {
+ for (iter = recovered_files; iter; iter = iter->next)
+ {
+ GFile *file;
+ GimpImage *image;
+ GError *error = NULL;
+ GimpPDBStatusType status;
+
+ file = g_file_new_for_path (iter->data);
+ image = file_open_with_display (gimp,
+ gimp_get_user_context (gimp),
+ NULL,
+ file, as_new,
+ initial_screen,
+ initial_monitor,
+ &status, &error);
+ if (image)
+ {
+ /* Break ties with the backup directory. */
+ gimp_image_set_file (image, NULL);
+ /* One of the rare exceptions where we should call
+ * gimp_image_dirty() directly instead of creating
+ * an undo. We want the image to be dirty from
+ * scratch, without anything to undo.
+ */
+ gimp_image_dirty (image, GIMP_DIRTY_IMAGE);
+ }
+ else
+ {
+ g_error_free (error);
+ }
+
+ g_object_unref (file);
+ }
+ }
+ /* Delete backup XCF images. */
+ for (iter = recovered_files; iter; iter = iter->next)
+ {
+ g_unlink (iter->data);
+ }
+ g_list_free_full (recovered_files, g_free);
+ }
+#endif
+
+ /* Load the images given on the command-line. */
+ if (filenames)
+ {
+ gint i;
+
+ for (i = 0; filenames[i] != NULL; i++)
+ {
+ if (run_loop)
+ {
+ GFile *file = g_file_new_for_commandline_arg (filenames[i]);
+
+ file_open_from_command_line (gimp, file, as_new,
+ initial_screen,
+ initial_monitor);
+
+ g_object_unref (file);
+ }
+ }
+ }
+
+ /* The software is now fully loaded and ready to be used and get
+ * external input.
+ */
+ gimp->initialized = TRUE;
+
+ if (font_error)
+ {
+ gimp_message_literal (gimp, NULL,
+ GIMP_MESSAGE_INFO,
+ font_error->message);
+ g_error_free (font_error);
+ }
+
+ if (run_loop)
+ gimp_batch_run (gimp, batch_interpreter, batch_commands);
+
+ if (run_loop)
+ {
+ gimp_threads_leave (gimp);
+ g_main_loop_run (loop);
+ gimp_threads_enter (gimp);
+ }
+
+ if (gimp->be_verbose)
+ g_print ("EXIT: %s\n", G_STRFUNC);
+
+ g_main_loop_unref (loop);
+
+ gimp_gegl_exit (gimp);
+
+ errors_exit ();
+
+ g_object_unref (gimp);
+
+ gimp_debug_instances ();
+
+ gegl_exit ();
+}
+
+
+/* private functions */
+
+static void
+app_init_update_noop (const gchar *text1,
+ const gchar *text2,
+ gdouble percentage)
+{
+ /* deliberately do nothing */
+}
+
+static void
+app_restore_after_callback (Gimp *gimp,
+ GimpInitStatusFunc status_callback)
+{
+ /* Getting the display name for a -1 display returns the initial
+ * monitor during startup. Need to call this from a restore_after
+ * callback, because before restore(), the GUI can't return anything,
+ * after after restore() the initial monitor gets reset.
+ */
+ g_free (gimp_get_display_name (gimp, -1, &initial_screen, &initial_monitor));
+}
+
+static gboolean
+app_exit_after_callback (Gimp *gimp,
+ gboolean kill_it,
+ GMainLoop **loop)
+{
+ if (gimp->be_verbose)
+ g_print ("EXIT: %s\n", G_STRFUNC);
+
+ /*
+ * In stable releases, we simply call exit() here. This speeds up
+ * the process of quitting GIMP and also works around the problem
+ * that plug-ins might still be running.
+ *
+ * In unstable releases, we shut down GIMP properly in an attempt
+ * to catch possible problems in our finalizers.
+ */
+
+#ifndef GIMP_RELEASE
+
+ if (g_main_loop_is_running (*loop))
+ g_main_loop_quit (*loop);
+
+ *loop = NULL;
+
+#else
+
+ gimp_gegl_exit (gimp);
+
+ gegl_exit ();
+
+ exit (EXIT_SUCCESS);
+
+#endif
+
+ return FALSE;
+}