app: initial work on moving to GtkApplication.

Reviewer's message (Jehan): This was a work-in-progress by Niels, which
we only keep in this state because Lukas worked over it. I have rebased
and fix-amended many broken part of this commit, because various things
had been changed in these areas of code since this commit was initially
written.
This commit is contained in:
Niels De Graef 2021-12-19 18:09:28 +01:00 committed by Jehan
parent 8bbde7db25
commit 27fe67814e
13 changed files with 417 additions and 169 deletions

View File

@ -57,6 +57,8 @@ libapp_sources = \
app.h \ app.h \
errors.c \ errors.c \
errors.h \ errors.h \
gimpapp.c \
gimpapp.h \
language.c \ language.c \
language.h \ language.h \
sanity.c \ sanity.c \

302
app/app.c
View File

@ -67,6 +67,7 @@
#include "app.h" #include "app.h"
#include "errors.h" #include "errors.h"
#include "gimpapp.h"
#include "language.h" #include "language.h"
#include "sanity.h" #include "sanity.h"
#include "gimp-debug.h" #include "gimp-debug.h"
@ -84,7 +85,7 @@ static void app_restore_after_callback (Gimp *gimp,
GimpInitStatusFunc status_callback); GimpInitStatusFunc status_callback);
static gboolean app_exit_after_callback (Gimp *gimp, static gboolean app_exit_after_callback (Gimp *gimp,
gboolean kill_it, gboolean kill_it,
GMainLoop **loop); GimpApp **app);
#if 0 #if 0
/* left here as documentation how to do compat enums */ /* left here as documentation how to do compat enums */
@ -162,6 +163,142 @@ app_exit (gint status)
exit (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 gint
app_run (const gchar *full_prog_name, app_run (const gchar *full_prog_name,
const gchar **filenames, const gchar **filenames,
@ -187,16 +324,11 @@ app_run (const gchar *full_prog_name,
GimpPDBCompatMode pdb_compat_mode, GimpPDBCompatMode pdb_compat_mode,
const gchar *backtrace_file) const gchar *backtrace_file)
{ {
GimpInitStatusFunc update_status_func = NULL; Gimp *gimp = NULL;
Gimp *gimp; GApplication *app = NULL;
GMainLoop *loop;
GMainLoop *run_loop;
GFile *default_folder = NULL; GFile *default_folder = NULL;
GFile *gimpdir; GFile *gimpdir = NULL;
const gchar *abort_message; const gchar *abort_message = NULL;
const gchar *current_language;
gchar *prev_language = NULL;
GError *font_error = NULL;
gint retval = EXIT_SUCCESS; gint retval = EXIT_SUCCESS;
if (filenames && filenames[0] && ! filenames[1] && if (filenames && filenames[0] && ! filenames[1] &&
@ -237,13 +369,13 @@ app_run (const gchar *full_prog_name,
stack_trace_mode, stack_trace_mode,
pdb_compat_mode); pdb_compat_mode);
if (default_folder) g_clear_object (&default_folder);
g_object_unref (default_folder);
app = gimp_app_new (gimp, no_splash, as_new, batch_interpreter, batch_commands);
gimp_cpu_accel_set_use (use_cpu_accel); 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); gimpdir = gimp_directory_file (NULL);
if (g_file_query_file_type (gimpdir, G_FILE_QUERY_INFO_NONE, 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), G_CALLBACK (app_restore_after_callback),
NULL); 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_signal_connect_after (gimp, "exit",
G_CALLBACK (app_exit_after_callback), G_CALLBACK (app_exit_after_callback),
&run_loop); &run_loop);
@ -372,94 +456,12 @@ app_run (const gchar *full_prog_name,
GError *error = NULL; GError *error = NULL;
GimpPDBStatusType status; GimpPDBStatusType status;
file = g_file_new_for_path (iter->data); g_application_run (app, 0, NULL);
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);
if (gimp->be_verbose) if (gimp->be_verbose)
g_print ("EXIT: %s\n", G_STRFUNC); g_print ("EXIT: %s\n", G_STRFUNC);
g_main_loop_unref (loop); g_clear_object (&app);
gimp_gegl_exit (gimp); gimp_gegl_exit (gimp);
@ -505,7 +507,7 @@ app_restore_after_callback (Gimp *gimp,
static gboolean static gboolean
app_exit_after_callback (Gimp *gimp, app_exit_after_callback (Gimp *gimp,
gboolean kill_it, gboolean kill_it,
GMainLoop **loop) GimpApp **app)
{ {
if (gimp->be_verbose) if (gimp->be_verbose)
g_print ("EXIT: %s\n", G_STRFUNC); g_print ("EXIT: %s\n", G_STRFUNC);
@ -521,10 +523,8 @@ app_exit_after_callback (Gimp *gimp,
#ifdef GIMP_UNSTABLE #ifdef GIMP_UNSTABLE
if (g_main_loop_is_running (*loop)) g_application_quit (G_APPLICATION (*app));
g_main_loop_quit (*loop); *app = NULL;
*loop = NULL;
#else #else

View File

@ -1114,6 +1114,7 @@ gimp_image_window_new (Gimp *gimp,
"gimp", gimp, "gimp", gimp,
"dialog-factory", dialog_factory, "dialog-factory", dialog_factory,
"initial-monitor", monitor, "initial-monitor", monitor,
"application", g_application_get_default (),
/* The window position will be overridden by the /* The window position will be overridden by the
* dialog factory, it is only really used on first * dialog factory, it is only really used on first
* startup. * startup.

191
app/gimpapp.c Normal file
View File

@ -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 <nielsdegraef@gmail.com>
*
* 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>
#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;
}

46
app/gimpapp.h Normal file
View File

@ -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 <nielsdegraef@gmail.com>
*
* 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/>.
*/
#ifndef __GIMP_APP_H__
#define __GIMP_APP_H__
#include <gio/gio.h>
#include <gtk/gtk.h>
#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__ */

View File

@ -225,6 +225,7 @@ gui_abort (const gchar *abort_message)
GimpInitStatusFunc GimpInitStatusFunc
gui_init (Gimp *gimp, gui_init (Gimp *gimp,
gboolean no_splash, gboolean no_splash,
GApplication *app,
const gchar *test_base_dir) const gchar *test_base_dir)
{ {
GimpInitStatusFunc status_callback = NULL; GimpInitStatusFunc status_callback = NULL;
@ -232,6 +233,7 @@ gui_init (Gimp *gimp,
g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); 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 (the_gui_gimp == NULL, NULL);
g_return_val_if_fail (G_IS_APPLICATION (app) || app == NULL, NULL);
abort_message = gui_sanity_check (); abort_message = gui_sanity_check ();
if (abort_message) if (abort_message)
@ -284,7 +286,7 @@ gui_init (Gimp *gimp,
if (! no_splash) if (! no_splash)
{ {
splash_create (gimp, gimp->be_verbose, initial_monitor); splash_create (gimp, gimp->be_verbose, initial_monitor, app);
status_callback = splash_update; status_callback = splash_update;
} }

View File

@ -24,6 +24,7 @@ void gui_abort (const gchar *abort_message);
GimpInitStatusFunc gui_init (Gimp *gimp, GimpInitStatusFunc gui_init (Gimp *gimp,
gboolean no_splash, gboolean no_splash,
GApplication *app,
const gchar *test_base_dir); const gchar *test_base_dir);
gboolean gui_recover (gint n_recoveries); gboolean gui_recover (gint n_recoveries);

View File

@ -111,7 +111,8 @@ static void splash_timer_elapsed (void);
void void
splash_create (Gimp *gimp, splash_create (Gimp *gimp,
gboolean be_verbose, gboolean be_verbose,
GdkMonitor *monitor) GdkMonitor *monitor,
GApplication *app)
{ {
GtkWidget *frame; GtkWidget *frame;
GtkWidget *vbox; GtkWidget *vbox;
@ -123,6 +124,7 @@ splash_create (Gimp *gimp,
g_return_if_fail (splash == NULL); g_return_if_fail (splash == NULL);
g_return_if_fail (GDK_IS_MONITOR (monitor)); g_return_if_fail (GDK_IS_MONITOR (monitor));
g_return_if_fail (G_IS_APPLICATION (app) || app == NULL);
gdk_monitor_get_workarea (monitor, &workarea); gdk_monitor_get_workarea (monitor, &workarea);
@ -162,13 +164,14 @@ splash_create (Gimp *gimp,
splash = g_slice_new0 (GimpSplash); splash = g_slice_new0 (GimpSplash);
splash->window = splash->window =
g_object_new (GTK_TYPE_WINDOW, g_object_new (GTK_TYPE_APPLICATION_WINDOW,
"type", GTK_WINDOW_TOPLEVEL, "type", GTK_WINDOW_TOPLEVEL,
"type-hint", GDK_WINDOW_TYPE_HINT_SPLASHSCREEN, "type-hint", GDK_WINDOW_TYPE_HINT_SPLASHSCREEN,
"title", _("GIMP Startup"), "title", _("GIMP Startup"),
"role", "gimp-startup", "role", "gimp-startup",
"window-position", GTK_WIN_POS_CENTER, "window-position", GTK_WIN_POS_CENTER,
"resizable", FALSE, "resizable", FALSE,
"application", app,
NULL); NULL);
/* Don't remove this call, it's necessary to remove decorations on Windows /* Don't remove this call, it's necessary to remove decorations on Windows

View File

@ -21,7 +21,8 @@
void splash_create (Gimp *gimp, void splash_create (Gimp *gimp,
gboolean be_verbose, gboolean be_verbose,
GdkMonitor *mointor); GdkMonitor *mointor,
GApplication *app);
void splash_destroy (void); void splash_destroy (void);
void splash_update (const gchar *label1, void splash_update (const gchar *label1,

View File

@ -32,6 +32,7 @@ app_debug_files = files(
libapp_sources = [ libapp_sources = [
'app.c', 'app.c',
'errors.c', 'errors.c',
'gimpapp.c',
'gimp-debug.c', 'gimp-debug.c',
'gimp-log.c', 'gimp-log.c',
'gimp-update.c', 'gimp-update.c',

View File

@ -138,7 +138,7 @@ gimp_init_for_gui_testing_internal (gboolean show_gui,
gimp_set_show_gui (gimp, show_gui); gimp_set_show_gui (gimp, show_gui);
gimp_load_config (gimp, gimprc, NULL); gimp_load_config (gimp, gimprc, NULL);
gimp_gegl_init (gimp); 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_init_icon_theme_for_testing ();
gimp_initialize (gimp, gimp_status_func_dummy); gimp_initialize (gimp, gimp_status_func_dummy);
gimp_restore (gimp, gimp_status_func_dummy, NULL); gimp_restore (gimp, gimp_status_func_dummy, NULL);

View File

@ -59,7 +59,7 @@ static gboolean gimp_window_key_press_event (GtkWidget *widget,
GdkEventKey *kevent); 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 #define parent_class gimp_window_parent_class

View File

@ -34,14 +34,14 @@ typedef struct _GimpWindowPrivate GimpWindowPrivate;
struct _GimpWindow struct _GimpWindow
{ {
GtkWindow parent_instance; GtkApplicationWindow parent_instance;
GimpWindowPrivate *private; GimpWindowPrivate *private;
}; };
struct _GimpWindowClass struct _GimpWindowClass
{ {
GtkWindowClass parent_class; GtkApplicationWindowClass parent_class;
void (* monitor_changed) (GimpWindow *window, void (* monitor_changed) (GimpWindow *window,
GdkMonitor *monitor); GdkMonitor *monitor);