462 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			462 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
|  | /* PIKA - Photo and Image Kooker Application
 | ||
|  |  * a rebranding of The GNU Image Manipulation Program (created with heckimp) | ||
|  |  * A derived work which may be trivial. However, any changes may be (C)2023 by Aldercone Studio | ||
|  |  * | ||
|  |  * Original copyright, applying to most contents (license remains unchanged):  | ||
|  |  * Copyright (C) 1995 Spencer Kimball and Peter Mattis | ||
|  |  * | ||
|  |  * PikaDBusService | ||
|  |  * 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/pika.h"
 | ||
|  | #include "core/pika-batch.h"
 | ||
|  | #include "core/pikacontainer.h"
 | ||
|  | 
 | ||
|  | #include "file/file-open.h"
 | ||
|  | 
 | ||
|  | #include "display/pikadisplay.h"
 | ||
|  | #include "display/pikadisplayshell.h"
 | ||
|  | 
 | ||
|  | #include "pikadbusservice.h"
 | ||
|  | 
 | ||
|  | 
 | ||
|  | typedef struct | ||
|  | { | ||
|  |   GFile    *file; | ||
|  |   gboolean  as_new; | ||
|  | 
 | ||
|  |   gchar    *interpreter; | ||
|  |   gchar    *command; | ||
|  | } IdleData; | ||
|  | 
 | ||
|  | static void       pika_dbus_service_ui_iface_init  (PikaDBusServiceUIIface *iface); | ||
|  | 
 | ||
|  | static void       pika_dbus_service_dispose        (GObject               *object); | ||
|  | static void       pika_dbus_service_finalize       (GObject               *object); | ||
|  | 
 | ||
|  | static gboolean   pika_dbus_service_activate       (PikaDBusServiceUI     *service, | ||
|  |                                                     GDBusMethodInvocation *invocation); | ||
|  | static gboolean   pika_dbus_service_open           (PikaDBusServiceUI     *service, | ||
|  |                                                     GDBusMethodInvocation *invocation, | ||
|  |                                                     const gchar           *uri); | ||
|  | 
 | ||
|  | static gboolean   pika_dbus_service_open_as_new    (PikaDBusServiceUI     *service, | ||
|  |                                                     GDBusMethodInvocation *invocation, | ||
|  |                                                     const gchar           *uri); | ||
|  | 
 | ||
|  | static gboolean   pika_dbus_service_batch_run      (PikaDBusServiceUI     *service, | ||
|  |                                                     GDBusMethodInvocation *invocation, | ||
|  |                                                     const gchar           *batch_interpreter, | ||
|  |                                                     const gchar           *batch_command); | ||
|  | 
 | ||
|  | static void       pika_dbus_service_pika_opened    (Pika                  *pika, | ||
|  |                                                     GFile                 *file, | ||
|  |                                                     PikaDBusService       *service); | ||
|  | 
 | ||
|  | static gboolean   pika_dbus_service_queue_open     (PikaDBusService       *service, | ||
|  |                                                     const gchar           *uri, | ||
|  |                                                     gboolean               as_new); | ||
|  | static gboolean   pika_dbus_service_queue_batch    (PikaDBusService       *service, | ||
|  |                                                     const gchar           *interpreter, | ||
|  |                                                     const gchar           *command); | ||
|  | 
 | ||
|  | static gboolean   pika_dbus_service_process_idle   (PikaDBusService       *service); | ||
|  | static IdleData * pika_dbus_service_open_data_new  (PikaDBusService       *service, | ||
|  |                                                     const gchar           *uri, | ||
|  |                                                     gboolean               as_new); | ||
|  | static IdleData * pika_dbus_service_batch_data_new (PikaDBusService       *service, | ||
|  |                                                     const gchar           *interpreter, | ||
|  |                                                     const gchar           *command); | ||
|  | static void       pika_dbus_service_idle_data_free (IdleData              *data); | ||
|  | 
 | ||
|  | 
 | ||
|  | G_DEFINE_TYPE_WITH_CODE (PikaDBusService, pika_dbus_service, | ||
|  |                          PIKA_DBUS_SERVICE_TYPE_UI_SKELETON, | ||
|  |                          G_IMPLEMENT_INTERFACE (PIKA_DBUS_SERVICE_TYPE_UI, | ||
|  |                                                 pika_dbus_service_ui_iface_init)) | ||
|  | 
 | ||
|  | #define parent_class pika_dbus_service_parent_class
 | ||
|  | 
 | ||
|  | 
 | ||
|  | static void | ||
|  | pika_dbus_service_class_init (PikaDBusServiceClass *klass) | ||
|  | { | ||
|  |   GObjectClass *object_class = G_OBJECT_CLASS (klass); | ||
|  | 
 | ||
|  |   object_class->dispose  = pika_dbus_service_dispose; | ||
|  |   object_class->finalize = pika_dbus_service_finalize; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_dbus_service_init (PikaDBusService *service) | ||
|  | { | ||
|  |   service->queue = g_queue_new (); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_dbus_service_ui_iface_init (PikaDBusServiceUIIface *iface) | ||
|  | { | ||
|  |   iface->handle_activate    = pika_dbus_service_activate; | ||
|  |   iface->handle_open        = pika_dbus_service_open; | ||
|  |   iface->handle_open_as_new = pika_dbus_service_open_as_new; | ||
|  |   iface->handle_batch_run   = pika_dbus_service_batch_run; | ||
|  | } | ||
|  | 
 | ||
|  | GObject * | ||
|  | pika_dbus_service_new (Pika *pika) | ||
|  | { | ||
|  |   PikaDBusService *service; | ||
|  | 
 | ||
|  |   g_return_val_if_fail (PIKA_IS_PIKA (pika), NULL); | ||
|  | 
 | ||
|  |   service = g_object_new (PIKA_TYPE_DBUS_SERVICE, NULL); | ||
|  | 
 | ||
|  |   service->pika = pika; | ||
|  | 
 | ||
|  |   g_signal_connect_object (pika, "image-opened", | ||
|  |                            G_CALLBACK (pika_dbus_service_pika_opened), | ||
|  |                            service, 0); | ||
|  | 
 | ||
|  |   return G_OBJECT (service); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_dbus_service_dispose (GObject *object) | ||
|  | { | ||
|  |   PikaDBusService *service = PIKA_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)) | ||
|  |     { | ||
|  |       pika_dbus_service_idle_data_free (g_queue_pop_head (service->queue)); | ||
|  |     } | ||
|  | 
 | ||
|  |   G_OBJECT_CLASS (parent_class)->dispose (object); | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_dbus_service_finalize (GObject *object) | ||
|  | { | ||
|  |   PikaDBusService *service = PIKA_DBUS_SERVICE (object); | ||
|  | 
 | ||
|  |   if (service->queue) | ||
|  |     { | ||
|  |       g_queue_free (service->queue); | ||
|  |       service->queue = NULL; | ||
|  |     } | ||
|  | 
 | ||
|  |   G_OBJECT_CLASS (parent_class)->finalize (object); | ||
|  | } | ||
|  | 
 | ||
|  | gboolean | ||
|  | pika_dbus_service_activate (PikaDBusServiceUI     *service, | ||
|  |                             GDBusMethodInvocation *invocation) | ||
|  | { | ||
|  |   Pika *pika = PIKA_DBUS_SERVICE (service)->pika; | ||
|  | 
 | ||
|  |   /*  We want to be called again later in case that PIKA is not fully
 | ||
|  |    *  started yet. | ||
|  |    */ | ||
|  |   if (pika_is_restored (pika)) | ||
|  |     { | ||
|  |       PikaObject *display; | ||
|  | 
 | ||
|  |       display = pika_container_get_first_child (pika->displays); | ||
|  | 
 | ||
|  |       if (display) | ||
|  |         pika_display_shell_present (pika_display_get_shell (PIKA_DISPLAY (display))); | ||
|  |     } | ||
|  | 
 | ||
|  |   pika_dbus_service_ui_complete_activate (service, invocation); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | gboolean | ||
|  | pika_dbus_service_open (PikaDBusServiceUI     *service, | ||
|  |                         GDBusMethodInvocation *invocation, | ||
|  |                         const gchar           *uri) | ||
|  | { | ||
|  |   gboolean success; | ||
|  | 
 | ||
|  |   success = pika_dbus_service_queue_open (PIKA_DBUS_SERVICE (service), | ||
|  |                                           uri, FALSE); | ||
|  | 
 | ||
|  |   pika_dbus_service_ui_complete_open (service, invocation, success); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | gboolean | ||
|  | pika_dbus_service_open_as_new (PikaDBusServiceUI     *service, | ||
|  |                                GDBusMethodInvocation *invocation, | ||
|  |                                const gchar           *uri) | ||
|  | { | ||
|  |   gboolean success; | ||
|  | 
 | ||
|  |   success = pika_dbus_service_queue_open (PIKA_DBUS_SERVICE (service), | ||
|  |                                           uri, TRUE); | ||
|  | 
 | ||
|  |   pika_dbus_service_ui_complete_open_as_new (service, invocation, success); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | gboolean | ||
|  | pika_dbus_service_batch_run (PikaDBusServiceUI     *service, | ||
|  |                              GDBusMethodInvocation *invocation, | ||
|  |                              const gchar           *batch_interpreter, | ||
|  |                              const gchar           *batch_command) | ||
|  | { | ||
|  |   gboolean success; | ||
|  | 
 | ||
|  |   success = pika_dbus_service_queue_batch (PIKA_DBUS_SERVICE (service), | ||
|  |                                            batch_interpreter, | ||
|  |                                            batch_command); | ||
|  | 
 | ||
|  |   pika_dbus_service_ui_complete_batch_run (service, invocation, success); | ||
|  | 
 | ||
|  |   return TRUE; | ||
|  | } | ||
|  | 
 | ||
|  | static void | ||
|  | pika_dbus_service_pika_opened (Pika            *pika, | ||
|  |                                GFile           *file, | ||
|  |                                PikaDBusService *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 | ||
|  | pika_dbus_service_queue_open (PikaDBusService *service, | ||
|  |                               const gchar     *uri, | ||
|  |                               gboolean         as_new) | ||
|  | { | ||
|  |   g_queue_push_tail (service->queue, | ||
|  |                      pika_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) pika_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 | ||
|  | pika_dbus_service_queue_batch (PikaDBusService *service, | ||
|  |                                const gchar     *interpreter, | ||
|  |                                const gchar     *command) | ||
|  | { | ||
|  |   g_queue_push_tail (service->queue, | ||
|  |                      pika_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) pika_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 | ||
|  | pika_dbus_service_process_idle (PikaDBusService *service) | ||
|  | { | ||
|  |   IdleData *data; | ||
|  | 
 | ||
|  |   if (! pika_is_restored (service->pika)) | ||
|  |     { | ||
|  |       if (! service->timeout_source) | ||
|  |         { | ||
|  |           /* We are probably starting the program. No need to spam PIKA with
 | ||
|  |            * an idle handler (which might make PIKA 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) pika_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->pika, data->file, data->as_new, | ||
|  |                                      NULL /* FIXME monitor */); | ||
|  | 
 | ||
|  |       if (data->command) | ||
|  |         { | ||
|  |           const gchar *commands[2] = {data->command, 0}; | ||
|  | 
 | ||
|  |           pika_batch_run (service->pika, data->interpreter, | ||
|  |                           commands); | ||
|  |         } | ||
|  | 
 | ||
|  |       pika_dbus_service_idle_data_free (data); | ||
|  | 
 | ||
|  |       if (service->timeout_source) | ||
|  |         { | ||
|  |           /* Now PIKA 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) pika_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 * | ||
|  | pika_dbus_service_open_data_new (PikaDBusService *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 * | ||
|  | pika_dbus_service_batch_data_new (PikaDBusService *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 | ||
|  | pika_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); | ||
|  | } |