diff options
Diffstat (limited to 'app/gui/gimpdbusservice.c')
-rw-r--r-- | app/gui/gimpdbusservice.c | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/app/gui/gimpdbusservice.c b/app/gui/gimpdbusservice.c new file mode 100644 index 0000000..462d7d4 --- /dev/null +++ b/app/gui/gimpdbusservice.c @@ -0,0 +1,457 @@ +/* GIMP - The GNU Image Manipulation Program + * Copyright (C) 1995 Spencer Kimball and Peter Mattis + * + * GimpDBusService + * Copyright (C) 2007, 2008 Sven Neumann <sven@gimp.org> + * Copyright (C) 2013 Michael Natterer <mitch@gimp.org> + * + * 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 <gegl.h> +#include <gtk/gtk.h> + +#include "gui-types.h" + +#include "core/gimp.h" +#include "core/gimp-batch.h" +#include "core/gimpcontainer.h" + +#include "file/file-open.h" + +#include "display/gimpdisplay.h" +#include "display/gimpdisplayshell.h" + +#include "gimpdbusservice.h" + + +typedef struct +{ + GFile *file; + gboolean as_new; + + gchar *interpreter; + gchar *command; +} IdleData; + +static void gimp_dbus_service_ui_iface_init (GimpDBusServiceUIIface *iface); + +static void gimp_dbus_service_dispose (GObject *object); +static void gimp_dbus_service_finalize (GObject *object); + +static gboolean gimp_dbus_service_activate (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation); +static gboolean gimp_dbus_service_open (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *uri); + +static gboolean gimp_dbus_service_open_as_new (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *uri); + +static gboolean gimp_dbus_service_batch_run (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *batch_interpreter, + const gchar *batch_command); + +static void gimp_dbus_service_gimp_opened (Gimp *gimp, + GFile *file, + GimpDBusService *service); + +static gboolean gimp_dbus_service_queue_open (GimpDBusService *service, + const gchar *uri, + gboolean as_new); +static gboolean gimp_dbus_service_queue_batch (GimpDBusService *service, + const gchar *interpreter, + const gchar *command); + +static gboolean gimp_dbus_service_process_idle (GimpDBusService *service); +static IdleData * gimp_dbus_service_open_data_new (GimpDBusService *service, + const gchar *uri, + gboolean as_new); +static IdleData * gimp_dbus_service_batch_data_new (GimpDBusService *service, + const gchar *interpreter, + const gchar *command); +static void gimp_dbus_service_idle_data_free (IdleData *data); + + +G_DEFINE_TYPE_WITH_CODE (GimpDBusService, gimp_dbus_service, + GIMP_DBUS_SERVICE_TYPE_UI_SKELETON, + G_IMPLEMENT_INTERFACE (GIMP_DBUS_SERVICE_TYPE_UI, + gimp_dbus_service_ui_iface_init)) + +#define parent_class gimp_dbus_service_parent_class + + +static void +gimp_dbus_service_class_init (GimpDBusServiceClass *klass) +{ + GObjectClass *object_class = G_OBJECT_CLASS (klass); + + object_class->dispose = gimp_dbus_service_dispose; + object_class->finalize = gimp_dbus_service_finalize; +} + +static void +gimp_dbus_service_init (GimpDBusService *service) +{ + service->queue = g_queue_new (); +} + +static void +gimp_dbus_service_ui_iface_init (GimpDBusServiceUIIface *iface) +{ + iface->handle_activate = gimp_dbus_service_activate; + iface->handle_open = gimp_dbus_service_open; + iface->handle_open_as_new = gimp_dbus_service_open_as_new; + iface->handle_batch_run = gimp_dbus_service_batch_run; +} + +GObject * +gimp_dbus_service_new (Gimp *gimp) +{ + GimpDBusService *service; + + g_return_val_if_fail (GIMP_IS_GIMP (gimp), NULL); + + service = g_object_new (GIMP_TYPE_DBUS_SERVICE, NULL); + + service->gimp = gimp; + + g_signal_connect_object (gimp, "image-opened", + G_CALLBACK (gimp_dbus_service_gimp_opened), + service, 0); + + return G_OBJECT (service); +} + +static void +gimp_dbus_service_dispose (GObject *object) +{ + GimpDBusService *service = GIMP_DBUS_SERVICE (object); + + if (service->source) + { + g_source_remove (g_source_get_id (service->source)); + service->source = NULL; + service->timeout_source = FALSE; + } + + while (! g_queue_is_empty (service->queue)) + { + gimp_dbus_service_idle_data_free (g_queue_pop_head (service->queue)); + } + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static void +gimp_dbus_service_finalize (GObject *object) +{ + GimpDBusService *service = GIMP_DBUS_SERVICE (object); + + if (service->queue) + { + g_queue_free (service->queue); + service->queue = NULL; + } + + G_OBJECT_CLASS (parent_class)->finalize (object); +} + +gboolean +gimp_dbus_service_activate (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation) +{ + Gimp *gimp = GIMP_DBUS_SERVICE (service)->gimp; + + /* We want to be called again later in case that GIMP is not fully + * started yet. + */ + if (gimp_is_restored (gimp)) + { + GimpObject *display; + + display = gimp_container_get_first_child (gimp->displays); + + if (display) + gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display))); + } + + gimp_dbus_service_ui_complete_activate (service, invocation); + + return TRUE; +} + +gboolean +gimp_dbus_service_open (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *uri) +{ + gboolean success; + + success = gimp_dbus_service_queue_open (GIMP_DBUS_SERVICE (service), + uri, FALSE); + + gimp_dbus_service_ui_complete_open (service, invocation, success); + + return TRUE; +} + +gboolean +gimp_dbus_service_open_as_new (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *uri) +{ + gboolean success; + + success = gimp_dbus_service_queue_open (GIMP_DBUS_SERVICE (service), + uri, TRUE); + + gimp_dbus_service_ui_complete_open_as_new (service, invocation, success); + + return TRUE; +} + +gboolean +gimp_dbus_service_batch_run (GimpDBusServiceUI *service, + GDBusMethodInvocation *invocation, + const gchar *batch_interpreter, + const gchar *batch_command) +{ + gboolean success; + + success = gimp_dbus_service_queue_batch (GIMP_DBUS_SERVICE (service), + batch_interpreter, + batch_command); + + gimp_dbus_service_ui_complete_batch_run (service, invocation, success); + + return TRUE; +} + +static void +gimp_dbus_service_gimp_opened (Gimp *gimp, + GFile *file, + GimpDBusService *service) +{ + gchar *uri = g_file_get_uri (file); + + g_signal_emit_by_name (service, "opened", uri); + + g_free (uri); +} + +/* + * Adds a request to open a file to the end of the queue and + * starts an idle source if it is not already running. + */ +static gboolean +gimp_dbus_service_queue_open (GimpDBusService *service, + const gchar *uri, + gboolean as_new) +{ + g_queue_push_tail (service->queue, + gimp_dbus_service_open_data_new (service, uri, as_new)); + + if (! service->source) + { + service->source = g_idle_source_new (); + service->timeout_source = FALSE; + + g_source_set_priority (service->source, G_PRIORITY_LOW); + g_source_set_callback (service->source, + (GSourceFunc) gimp_dbus_service_process_idle, + service, + NULL); + g_source_attach (service->source, NULL); + g_source_unref (service->source); + } + + /* The call always succeeds as it is handled in one way or another. + * Even presenting an error message is considered success ;-) + */ + return TRUE; +} + +/* + * Adds a request to run a batch command to the end of the queue and + * starts an idle source if it is not already running. + */ +static gboolean +gimp_dbus_service_queue_batch (GimpDBusService *service, + const gchar *interpreter, + const gchar *command) +{ + g_queue_push_tail (service->queue, + gimp_dbus_service_batch_data_new (service, + interpreter, + command)); + + if (! service->source) + { + service->source = g_idle_source_new (); + service->timeout_source = FALSE; + + g_source_set_priority (service->source, G_PRIORITY_LOW); + g_source_set_callback (service->source, + (GSourceFunc) gimp_dbus_service_process_idle, + service, + NULL); + g_source_attach (service->source, NULL); + g_source_unref (service->source); + } + + /* The call always succeeds as it is handled in one way or another. + * Even presenting an error message is considered success ;-) + */ + return TRUE; +} + +/* + * Idle callback that removes the first request from the queue and + * handles it. If there are no more requests, the idle source is + * removed. + */ +static gboolean +gimp_dbus_service_process_idle (GimpDBusService *service) +{ + IdleData *data; + + if (! service->gimp->initialized || ! service->gimp->restored) + { + if (! service->timeout_source) + { + /* We are probably starting the program. No need to spam GIMP with + * an idle handler (which might make GIMP slower to start even + * with low priority). + * Instead let's add a timeout of half a second. + */ + service->source = g_timeout_source_new (500); + service->timeout_source = TRUE; + + g_source_set_priority (service->source, G_PRIORITY_LOW); + g_source_set_callback (service->source, + (GSourceFunc) gimp_dbus_service_process_idle, + service, + NULL); + g_source_attach (service->source, NULL); + g_source_unref (service->source); + + return G_SOURCE_REMOVE; + } + else + { + return G_SOURCE_CONTINUE; + } + } + + /* Process data as a FIFO. */ + data = g_queue_pop_head (service->queue); + + if (data) + { + if (data->file) + file_open_from_command_line (service->gimp, data->file, data->as_new, + NULL, /* FIXME monitor */ + 0 /* FIXME monitor */); + if (data->command) + { + const gchar *commands[2] = {data->command, 0}; + + gimp_batch_run (service->gimp, data->interpreter, + commands); + } + + gimp_dbus_service_idle_data_free (data); + + if (service->timeout_source) + { + /* Now GIMP is fully functional and can respond quickly to + * DBus calls. Switch to a usual idle source. + */ + service->source = g_idle_source_new (); + service->timeout_source = FALSE; + + g_source_set_priority (service->source, G_PRIORITY_LOW); + g_source_set_callback (service->source, + (GSourceFunc) gimp_dbus_service_process_idle, + service, + NULL); + g_source_attach (service->source, NULL); + g_source_unref (service->source); + + return G_SOURCE_REMOVE; + } + else + { + return G_SOURCE_CONTINUE; + } + } + + service->source = NULL; + + return G_SOURCE_REMOVE; +} + +static IdleData * +gimp_dbus_service_open_data_new (GimpDBusService *service, + const gchar *uri, + gboolean as_new) +{ + IdleData *data = g_slice_new (IdleData); + + data->file = g_file_new_for_uri (uri); + data->as_new = as_new; + data->interpreter = NULL; + data->command = NULL; + + return data; +} + +static IdleData * +gimp_dbus_service_batch_data_new (GimpDBusService *service, + const gchar *interpreter, + const gchar *command) +{ + IdleData *data = g_slice_new (IdleData); + + data->file = NULL; + data->as_new = FALSE; + + data->command = g_strdup (command); + + if (g_strcmp0 (interpreter, "") == 0) + data->interpreter = NULL; + else + data->interpreter = g_strdup (interpreter); + + return data; +} + +static void +gimp_dbus_service_idle_data_free (IdleData *data) +{ + if (data->file) + g_object_unref (data->file); + + if (data->command) + g_free (data->command); + if (data->interpreter) + g_free (data->interpreter); + + g_slice_free (IdleData, data); +} |