diff --git a/app/Makefile.am b/app/Makefile.am index 5874608111..07bae5532e 100644 --- a/app/Makefile.am +++ b/app/Makefile.am @@ -57,6 +57,8 @@ libapp_sources = \ app.h \ errors.c \ errors.h \ + gimpapp.c \ + gimpapp.h \ language.c \ language.h \ sanity.c \ diff --git a/app/app.c b/app/app.c index 5d40b7f7d2..8d6ac3f2fc 100644 --- a/app/app.c +++ b/app/app.c @@ -67,6 +67,7 @@ #include "app.h" #include "errors.h" +#include "gimpapp.h" #include "language.h" #include "sanity.h" #include "gimp-debug.h" @@ -84,7 +85,7 @@ static void app_restore_after_callback (Gimp *gimp, GimpInitStatusFunc status_callback); static gboolean app_exit_after_callback (Gimp *gimp, gboolean kill_it, - GMainLoop **loop); + GimpApp **app); #if 0 /* left here as documentation how to do compat enums */ @@ -162,6 +163,142 @@ app_exit (gint status) exit (status); } +static void +app_activate_callback (GApplication *gapp, + gpointer user_data) +{ + GimpApp *app = GIMP_APP (gapp); + Gimp *gimp = NULL; + GimpInitStatusFunc update_status_func = NULL; + + gimp = gimp_app_get_gimp (app); + +#ifndef GIMP_CONSOLE_COMPILATION + if (! gimp->no_interface) + update_status_func = gui_init (gimp, gimp_app_get_no_splash (app), gapp, NULL); +#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, NULL); + + /* 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, gimp); + + /* Set this after gimp_update_auto_check(). This will be used for the + * next run. + */ + g_object_set (gimp->edit_config, + "config-version", GIMP_VERSION, + NULL); + +#ifndef GIMP_CONSOLE_COMPILATION + if (! gimp->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, + gimp_app_get_as_new (app), + 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 + + //XXX +#if 0 + /* Load the images given on the command-line. */ + if (filenames) + { + gint i; + + for (i = 0; filenames[i] != NULL; i++) + { + if (app) + { + GFile *file = g_file_new_for_commandline_arg (filenames[i]); + + file_open_from_command_line (gimp, file, + gimp_app_get_as_new (app), + initial_monitor); + + g_object_unref (file); + } + } + } +#endif + + /* The software is now fully loaded and ready to be used and get + * external input. + */ + gimp->initialized = TRUE; + + if (app) + { + gimp_batch_run (gimp, + gimp_app_get_batch_interpreter (app), + gimp_app_get_batch_commands (app)); + } +} + gint app_run (const gchar *full_prog_name, const gchar **filenames, @@ -187,17 +324,12 @@ app_run (const gchar *full_prog_name, 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; - const gchar *current_language; - gchar *prev_language = NULL; - GError *font_error = NULL; - gint retval = EXIT_SUCCESS; + Gimp *gimp = NULL; + GApplication *app = NULL; + GFile *default_folder = NULL; + GFile *gimpdir = NULL; + const gchar *abort_message = NULL; + gint retval = EXIT_SUCCESS; if (filenames && filenames[0] && ! filenames[1] && g_file_test (filenames[0], G_FILE_TEST_IS_DIR)) @@ -237,13 +369,13 @@ app_run (const gchar *full_prog_name, stack_trace_mode, pdb_compat_mode); - if (default_folder) - g_object_unref (default_folder); + g_clear_object (&default_folder); + + app = gimp_app_new (gimp, no_splash, as_new, batch_interpreter, batch_commands); gimp_cpu_accel_set_use (use_cpu_accel); - /* Check if the user's gimp_directory exists - */ + /* 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) != @@ -299,54 +431,6 @@ app_run (const gchar *full_prog_name, G_CALLBACK (app_restore_after_callback), NULL); -#ifndef GIMP_CONSOLE_COMPILATION - if (! no_interface) - update_status_func = gui_init (gimp, no_splash, NULL); -#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); - - g_object_get (gimp->edit_config, - "prev-language", &prev_language, - NULL); - /* Language was already initialized. I call this again only to get the - * actual language information. - */ - current_language = language_init (NULL); - gimp->query_all = (prev_language == NULL || - g_strcmp0 (prev_language, current_language) != 0); - g_free (prev_language); - - /* 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, gimp); - - /* Setting properties to be used for the next run. */ - g_object_set (gimp->edit_config, - /* Set this after gimp_update_auto_check(). */ - "config-version", GIMP_VERSION, - /* Set this after gimp_restore(). */ - "prev-language", current_language, - NULL); - - loop = run_loop = g_main_loop_new (NULL, FALSE); - g_signal_connect_after (gimp, "exit", G_CALLBACK (app_exit_after_callback), &run_loop); @@ -372,94 +456,12 @@ app_run (const gchar *full_prog_name, 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_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_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) - retval = gimp_batch_run (gimp, batch_interpreter, batch_commands); - - if (quit) - /* Emit the "exit" signal, but also properly free all images still - * opened. - */ - gimp_exit (gimp, TRUE); - else - /* Only take into account the batch commands' success when we - * return immediately. - */ - retval = EXIT_SUCCESS; - - if (run_loop) - g_main_loop_run (loop); + g_application_run (app, 0, NULL); if (gimp->be_verbose) g_print ("EXIT: %s\n", G_STRFUNC); - g_main_loop_unref (loop); + g_clear_object (&app); gimp_gegl_exit (gimp); @@ -505,7 +507,7 @@ app_restore_after_callback (Gimp *gimp, static gboolean app_exit_after_callback (Gimp *gimp, gboolean kill_it, - GMainLoop **loop) + GimpApp **app) { if (gimp->be_verbose) g_print ("EXIT: %s\n", G_STRFUNC); @@ -521,10 +523,8 @@ app_exit_after_callback (Gimp *gimp, #ifdef GIMP_UNSTABLE - if (g_main_loop_is_running (*loop)) - g_main_loop_quit (*loop); - - *loop = NULL; + g_application_quit (G_APPLICATION (*app)); + *app = NULL; #else diff --git a/app/display/gimpimagewindow.c b/app/display/gimpimagewindow.c index 1499c27eed..d407cbf471 100644 --- a/app/display/gimpimagewindow.c +++ b/app/display/gimpimagewindow.c @@ -1114,6 +1114,7 @@ gimp_image_window_new (Gimp *gimp, "gimp", gimp, "dialog-factory", dialog_factory, "initial-monitor", monitor, + "application", g_application_get_default (), /* The window position will be overridden by the * dialog factory, it is only really used on first * startup. diff --git a/app/gimpapp.c b/app/gimpapp.c new file mode 100644 index 0000000000..7b69bc7a4f --- /dev/null +++ b/app/gimpapp.c @@ -0,0 +1,191 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * gimpapp.c + * Copyright (C) 2021 Niels De Graef + * + * 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 + * . + */ + +#include + +#include "gimpapp.h" + +#include "libgimpbase/gimpbase.h" + + +enum +{ + PROP_0, + PROP_GIMP, + N_PROPS +}; + +struct _GimpApp +{ + GtkApplication parent_instance; + + Gimp *gimp; + + gboolean no_splash; + gboolean as_new; + const gchar *batch_interpreter; + const gchar **batch_commands; +}; + + +static void gimp_app_finalize (GObject *object); +static void gimp_app_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec); +static void gimp_app_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec); + + +G_DEFINE_TYPE (GimpApp, gimp_app, GTK_TYPE_APPLICATION) + +static GParamSpec *props[N_PROPS] = { NULL, }; + + +static void +gimp_app_class_init (GimpAppClass *klass) +{ + GObjectClass *gobj_class = G_OBJECT_CLASS (klass); + + gobj_class->get_property = gimp_app_get_property; + gobj_class->set_property = gimp_app_set_property; + gobj_class->finalize = gimp_app_finalize; + + props[PROP_GIMP] = + g_param_spec_object ("gimp", "GIMP", "GIMP root object", + GIMP_TYPE_GIMP, + GIMP_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY); + + g_object_class_install_properties (gobj_class, N_PROPS, props); +} + +static void +gimp_app_init (GimpApp *self) +{ +} + +static void +gimp_app_finalize (GObject *object) +{ + GimpApp *self = GIMP_APP (object); + + g_clear_object (&self->gimp); + + G_OBJECT_CLASS (gimp_app_parent_class)->finalize (object); +} + +static void +gimp_app_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + GimpApp *self = GIMP_APP (object); + + switch (property_id) + { + case PROP_GIMP: + g_value_set_object (value, self->gimp); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +static void +gimp_app_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + GimpApp *self = GIMP_APP (object); + + switch (property_id) + { + case PROP_GIMP: + self->gimp = g_value_dup_object (value); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + break; + } +} + +/* public functions */ + +GApplication * +gimp_app_new (Gimp *gimp, + gboolean no_splash, + gboolean as_new, + const char *batch_interpreter, + const char **batch_commands) +{ + GimpApp *app; + + app = g_object_new (GIMP_TYPE_APP, + "gimp", gimp, + NULL); + + /* We shouldn't have to pass these externally, so I didn't bother making + * GObject properties for them. In the end, they should just be parsed by + * the GApplication code */ + app->no_splash = no_splash; + app->as_new = as_new; + app->batch_interpreter = batch_interpreter; + app->batch_commands = batch_commands; + + return G_APPLICATION (app); +} + +Gimp * +gimp_app_get_gimp (GimpApp *self) +{ + g_return_val_if_fail (GIMP_IS_APP (self), NULL); + return self->gimp; +} + +gboolean +gimp_app_get_no_splash (GimpApp *self) +{ + g_return_val_if_fail (GIMP_IS_APP (self), FALSE); + return self->no_splash; +} + +gboolean +gimp_app_get_as_new (GimpApp *self) +{ + g_return_val_if_fail (GIMP_IS_APP (self), FALSE); + return self->as_new; +} + +const char * +gimp_app_get_batch_interpreter (GimpApp *self) +{ + g_return_val_if_fail (GIMP_IS_APP (self), NULL); + return self->batch_interpreter; +} + +const char ** +gimp_app_get_batch_commands (GimpApp *self) +{ + g_return_val_if_fail (GIMP_IS_APP (self), NULL); + return self->batch_commands; +} diff --git a/app/gimpapp.h b/app/gimpapp.h new file mode 100644 index 0000000000..1a8a1c3989 --- /dev/null +++ b/app/gimpapp.h @@ -0,0 +1,46 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995-1997 Peter Mattis and Spencer Kimball + * + * gimpapp.h + * Copyright (C) 2021 Niels De Graef + * + * 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 + * . + */ + +#ifndef __GIMP_APP_H__ +#define __GIMP_APP_H__ + +#include +#include + +#include "core/core-types.h" +#include "core/gimp.h" + +#define GIMP_TYPE_APP (gimp_app_get_type ()) +G_DECLARE_FINAL_TYPE (GimpApp, gimp_app, GIMP, APP, GtkApplication) + + +GApplication * gimp_app_new (Gimp *gimp, + gboolean no_splash, + gboolean as_new, + const char *batch_interpreter, + const char **batch_commands); + +Gimp * gimp_app_get_gimp (GimpApp *self); + +gboolean gimp_app_get_no_splash (GimpApp *self); + +gboolean gimp_app_get_as_new (GimpApp *self); + +const char * gimp_app_get_batch_interpreter (GimpApp *self); + +const char ** gimp_app_get_batch_commands (GimpApp *self); + +#endif /* __GIMP_APP_H__ */ diff --git a/app/gui/gui.c b/app/gui/gui.c index 647ea73e3f..3dcafd4d60 100644 --- a/app/gui/gui.c +++ b/app/gui/gui.c @@ -223,15 +223,17 @@ gui_abort (const gchar *abort_message) * unit testing calls. */ GimpInitStatusFunc -gui_init (Gimp *gimp, - gboolean no_splash, - const gchar *test_base_dir) +gui_init (Gimp *gimp, + gboolean no_splash, + GApplication *app, + const gchar *test_base_dir) { GimpInitStatusFunc status_callback = NULL; gchar *abort_message; g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); g_return_val_if_fail (the_gui_gimp == NULL, NULL); + g_return_val_if_fail (G_IS_APPLICATION (app) || app == NULL, NULL); abort_message = gui_sanity_check (); if (abort_message) @@ -284,7 +286,7 @@ gui_init (Gimp *gimp, if (! no_splash) { - splash_create (gimp, gimp->be_verbose, initial_monitor); + splash_create (gimp, gimp->be_verbose, initial_monitor, app); status_callback = splash_update; } diff --git a/app/gui/gui.h b/app/gui/gui.h index a5cf0fecc4..af88b08613 100644 --- a/app/gui/gui.h +++ b/app/gui/gui.h @@ -24,6 +24,7 @@ void gui_abort (const gchar *abort_message); GimpInitStatusFunc gui_init (Gimp *gimp, gboolean no_splash, + GApplication *app, const gchar *test_base_dir); gboolean gui_recover (gint n_recoveries); diff --git a/app/gui/splash.c b/app/gui/splash.c index 8df1d8039a..3ce1bc3d6c 100644 --- a/app/gui/splash.c +++ b/app/gui/splash.c @@ -109,9 +109,10 @@ static void splash_timer_elapsed (void); /* public functions */ void -splash_create (Gimp *gimp, - gboolean be_verbose, - GdkMonitor *monitor) +splash_create (Gimp *gimp, + gboolean be_verbose, + GdkMonitor *monitor, + GApplication *app) { GtkWidget *frame; GtkWidget *vbox; @@ -123,6 +124,7 @@ splash_create (Gimp *gimp, g_return_if_fail (splash == NULL); g_return_if_fail (GDK_IS_MONITOR (monitor)); + g_return_if_fail (G_IS_APPLICATION (app) || app == NULL); gdk_monitor_get_workarea (monitor, &workarea); @@ -162,13 +164,14 @@ splash_create (Gimp *gimp, splash = g_slice_new0 (GimpSplash); splash->window = - g_object_new (GTK_TYPE_WINDOW, + g_object_new (GTK_TYPE_APPLICATION_WINDOW, "type", GTK_WINDOW_TOPLEVEL, "type-hint", GDK_WINDOW_TYPE_HINT_SPLASHSCREEN, "title", _("GIMP Startup"), "role", "gimp-startup", "window-position", GTK_WIN_POS_CENTER, "resizable", FALSE, + "application", app, NULL); /* Don't remove this call, it's necessary to remove decorations on Windows diff --git a/app/gui/splash.h b/app/gui/splash.h index e3d86da9fb..c6fb9fdfb3 100644 --- a/app/gui/splash.h +++ b/app/gui/splash.h @@ -19,9 +19,10 @@ #define __SPLASH_H__ -void splash_create (Gimp *gimp, - gboolean be_verbose, - GdkMonitor *mointor); +void splash_create (Gimp *gimp, + gboolean be_verbose, + GdkMonitor *mointor, + GApplication *app); void splash_destroy (void); void splash_update (const gchar *label1, diff --git a/app/meson.build b/app/meson.build index cf2b1184d7..9783b164e5 100644 --- a/app/meson.build +++ b/app/meson.build @@ -32,6 +32,7 @@ app_debug_files = files( libapp_sources = [ 'app.c', 'errors.c', + 'gimpapp.c', 'gimp-debug.c', 'gimp-log.c', 'gimp-update.c', diff --git a/app/tests.c b/app/tests.c index c1b26c4ff9..3777057631 100644 --- a/app/tests.c +++ b/app/tests.c @@ -138,7 +138,7 @@ gimp_init_for_gui_testing_internal (gboolean show_gui, gimp_set_show_gui (gimp, show_gui); gimp_load_config (gimp, gimprc, NULL); gimp_gegl_init (gimp); - gui_init (gimp, TRUE, g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR")); + gui_init (gimp, TRUE, NULL, g_getenv ("GIMP_TESTING_ABS_TOP_SRCDIR")); gimp_init_icon_theme_for_testing (); gimp_initialize (gimp, gimp_status_func_dummy); gimp_restore (gimp, gimp_status_func_dummy, NULL); diff --git a/app/widgets/gimpwindow.c b/app/widgets/gimpwindow.c index d9bc728c9e..866bb1f92a 100644 --- a/app/widgets/gimpwindow.c +++ b/app/widgets/gimpwindow.c @@ -59,7 +59,7 @@ static gboolean gimp_window_key_press_event (GtkWidget *widget, GdkEventKey *kevent); -G_DEFINE_TYPE_WITH_PRIVATE (GimpWindow, gimp_window, GTK_TYPE_WINDOW) +G_DEFINE_TYPE_WITH_PRIVATE (GimpWindow, gimp_window, GTK_TYPE_APPLICATION_WINDOW) #define parent_class gimp_window_parent_class diff --git a/app/widgets/gimpwindow.h b/app/widgets/gimpwindow.h index 9ee3af2cb1..be372a138d 100644 --- a/app/widgets/gimpwindow.h +++ b/app/widgets/gimpwindow.h @@ -34,14 +34,14 @@ typedef struct _GimpWindowPrivate GimpWindowPrivate; struct _GimpWindow { - GtkWindow parent_instance; + GtkApplicationWindow parent_instance; - GimpWindowPrivate *private; + GimpWindowPrivate *private; }; struct _GimpWindowClass { - GtkWindowClass parent_class; + GtkApplicationWindowClass parent_class; void (* monitor_changed) (GimpWindow *window, GdkMonitor *monitor);