summaryrefslogtreecommitdiffstats
path: root/app/gui/gui-unique.c
diff options
context:
space:
mode:
Diffstat (limited to 'app/gui/gui-unique.c')
-rw-r--r--app/gui/gui-unique.c442
1 files changed, 442 insertions, 0 deletions
diff --git a/app/gui/gui-unique.c b/app/gui/gui-unique.c
new file mode 100644
index 0000000..c54a05b
--- /dev/null
+++ b/app/gui/gui-unique.c
@@ -0,0 +1,442 @@
+/* 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 <gegl.h>
+#include <gtk/gtk.h>
+
+#ifdef G_OS_WIN32
+#include <windows.h>
+#endif
+
+#ifdef GDK_WINDOWING_QUARTZ
+#import <AppKit/AppKit.h>
+#include <gtkosxapplication.h>
+#endif
+
+#include "gui/gui-types.h"
+
+#include "core/gimp.h"
+#include "core/gimpcontainer.h"
+
+#include "display/gimpdisplay.h"
+#include "display/gimpdisplayshell.h"
+#include "display/gimpimagewindow.h"
+
+#include "file/file-open.h"
+
+#include "gimpdbusservice.h"
+#include "gui-unique.h"
+
+
+#ifdef G_OS_WIN32
+
+static void gui_unique_win32_init (Gimp *gimp);
+static void gui_unique_win32_exit (void);
+
+static Gimp *unique_gimp = NULL;
+static HWND proxy_window = NULL;
+
+#elif defined (GDK_WINDOWING_QUARTZ)
+
+static void gui_unique_quartz_init (Gimp *gimp);
+static void gui_unique_quartz_exit (void);
+
+@interface GimpAppleEventHandler : NSObject {}
+- (void) handleEvent:(NSAppleEventDescriptor *) inEvent
+ andReplyWith:(NSAppleEventDescriptor *) replyEvent;
+@end
+
+static Gimp *unique_gimp = NULL;
+static GimpAppleEventHandler *event_handler = NULL;
+
+#else
+
+static void gui_dbus_service_init (Gimp *gimp);
+static void gui_dbus_service_exit (void);
+
+static GDBusObjectManagerServer *dbus_manager = NULL;
+static guint dbus_name_id = 0;
+
+#endif
+
+
+void
+gui_unique_init (Gimp *gimp)
+{
+#ifdef G_OS_WIN32
+ gui_unique_win32_init (gimp);
+#elif defined (GDK_WINDOWING_QUARTZ)
+ gui_unique_quartz_init (gimp);
+#else
+ gui_dbus_service_init (gimp);
+#endif
+}
+
+void
+gui_unique_exit (void)
+{
+#ifdef G_OS_WIN32
+ gui_unique_win32_exit ();
+#elif defined (GDK_WINDOWING_QUARTZ)
+ gui_unique_quartz_exit ();
+#else
+ gui_dbus_service_exit ();
+#endif
+}
+
+
+#ifdef G_OS_WIN32
+
+typedef struct
+{
+ GFile *file;
+ gboolean as_new;
+} IdleOpenData;
+
+static IdleOpenData *
+idle_open_data_new (GFile *file,
+ gboolean as_new)
+{
+ IdleOpenData *data = g_slice_new0 (IdleOpenData);
+
+ data->file = g_object_ref (file);
+ data->as_new = as_new;
+
+ return data;
+}
+
+static void
+idle_open_data_free (IdleOpenData *data)
+{
+ g_object_unref (data->file);
+ g_slice_free (IdleOpenData, data);
+}
+
+static gboolean
+gui_unique_win32_idle_open (IdleOpenData *data)
+{
+ /* We want to be called again later in case that GIMP is not fully
+ * started yet.
+ */
+ if (! gimp_is_restored (unique_gimp))
+ return TRUE;
+
+ if (data->file)
+ {
+ file_open_from_command_line (unique_gimp, data->file,
+ data->as_new, NULL, 0);
+ }
+ else
+ {
+ /* raise the first display */
+ GimpObject *display;
+
+ display = gimp_container_get_first_child (unique_gimp->displays);
+
+ gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display)));
+ }
+
+ return FALSE;
+}
+
+static LRESULT CALLBACK
+gui_unique_win32_message_handler (HWND hWnd,
+ UINT uMsg,
+ WPARAM wParam,
+ LPARAM lParam)
+{
+ switch (uMsg)
+ {
+ case WM_COPYDATA:
+ if (unique_gimp)
+ {
+ COPYDATASTRUCT *copydata = (COPYDATASTRUCT *) lParam;
+ GimpObject *display;
+
+ if (copydata->cbData > 0)
+ {
+ GSource *source;
+ GClosure *closure;
+ GFile *file;
+ IdleOpenData *data;
+
+ file = g_file_new_for_uri (copydata->lpData);
+
+ data = idle_open_data_new (file,
+ copydata->dwData != 0);
+
+ g_object_unref (file);
+
+ closure = g_cclosure_new (G_CALLBACK (gui_unique_win32_idle_open),
+ data,
+ (GClosureNotify) idle_open_data_free);
+
+ g_object_watch_closure (G_OBJECT (unique_gimp), closure);
+
+ source = g_idle_source_new ();
+ g_source_set_priority (source, G_PRIORITY_LOW);
+ g_source_set_closure (source, closure);
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+ }
+
+ /* Deiconify the window if minimized. */
+ display = gimp_container_get_first_child (unique_gimp->displays);
+ if (display)
+ gimp_display_shell_present (gimp_display_get_shell (GIMP_DISPLAY (display)));
+ }
+ return TRUE;
+
+ default:
+ return DefWindowProcW (hWnd, uMsg, wParam, lParam);
+ }
+}
+
+static void
+gui_unique_win32_init (Gimp *gimp)
+{
+ WNDCLASSW wc;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (unique_gimp == NULL);
+
+ unique_gimp = gimp;
+
+ /* register window class for proxy window */
+ memset (&wc, 0, sizeof (wc));
+
+ wc.hInstance = GetModuleHandle (NULL);
+ wc.lpfnWndProc = gui_unique_win32_message_handler;
+ wc.lpszClassName = GIMP_UNIQUE_WIN32_WINDOW_CLASS;
+
+ RegisterClassW (&wc);
+
+ proxy_window = CreateWindowExW (0,
+ GIMP_UNIQUE_WIN32_WINDOW_CLASS,
+ GIMP_UNIQUE_WIN32_WINDOW_NAME,
+ WS_POPUP, 0, 0, 1, 1, NULL, NULL, wc.hInstance, NULL);
+}
+
+static void
+gui_unique_win32_exit (void)
+{
+ g_return_if_fail (GIMP_IS_GIMP (unique_gimp));
+
+ unique_gimp = NULL;
+
+ DestroyWindow (proxy_window);
+}
+
+#elif defined (GDK_WINDOWING_QUARTZ)
+
+static gboolean
+gui_unique_quartz_idle_open (GFile *file)
+{
+ /* We want to be called again later in case that GIMP is not fully
+ * started yet.
+ */
+ if (! gimp_is_restored (unique_gimp))
+ return TRUE;
+
+ if (file)
+ {
+ file_open_from_command_line (unique_gimp, file, FALSE, NULL, 0);
+ }
+
+ return FALSE;
+}
+
+static gboolean
+gui_unique_quartz_nsopen_file_callback (GtkosxApplication *osx_app,
+ gchar *path,
+ gpointer user_data)
+{
+ GSource *source;
+ GClosure *closure;
+
+ closure = g_cclosure_new (G_CALLBACK (gui_unique_quartz_idle_open),
+ g_file_new_for_path (path),
+ (GClosureNotify) g_object_unref);
+
+ g_object_watch_closure (G_OBJECT (unique_gimp), closure);
+
+ source = g_idle_source_new ();
+
+ g_source_set_priority (source, G_PRIORITY_LOW);
+ g_source_set_closure (source, closure);
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+
+ return TRUE;
+}
+
+@implementation GimpAppleEventHandler
+- (void) handleEvent: (NSAppleEventDescriptor *) inEvent
+ andReplyWith: (NSAppleEventDescriptor *) replyEvent
+{
+ NSAutoreleasePool *urlpool;
+ NSInteger count;
+ NSInteger i;
+
+ urlpool = [[NSAutoreleasePool alloc] init];
+
+ count = [inEvent numberOfItems];
+
+ for (i = 1; i <= count; i++)
+ {
+ NSURL *url;
+ const gchar *path;
+ GSource *source;
+ GClosure *closure;
+
+ url = [NSURL URLWithString: [[inEvent descriptorAtIndex: i] stringValue]];
+ path = [[url path] UTF8String];
+
+ closure = g_cclosure_new (G_CALLBACK (gui_unique_quartz_idle_open),
+ g_file_new_for_path (path),
+ (GClosureNotify) g_object_unref);
+
+ g_object_watch_closure (G_OBJECT (unique_gimp), closure);
+
+ source = g_idle_source_new ();
+ g_source_set_priority (source, G_PRIORITY_LOW);
+ g_source_set_closure (source, closure);
+ g_source_attach (source, NULL);
+ g_source_unref (source);
+ }
+
+ [urlpool drain];
+}
+@end
+
+static void
+gui_unique_quartz_init (Gimp *gimp)
+{
+ GtkosxApplication *osx_app;
+
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (unique_gimp == NULL);
+
+ osx_app = gtkosx_application_get ();
+
+ unique_gimp = gimp;
+
+ g_signal_connect (osx_app, "NSApplicationOpenFile",
+ G_CALLBACK (gui_unique_quartz_nsopen_file_callback),
+ gimp);
+
+ /* Using the event handler is a hack, it is necessary because
+ * gtkosx_application will drop the file open events if any
+ * event processing is done before gtkosx_application_ready is
+ * called, which we unfortuantly can't avoid doing right now.
+ */
+ event_handler = [[GimpAppleEventHandler alloc] init];
+
+ [[NSAppleEventManager sharedAppleEventManager]
+ setEventHandler: event_handler
+ andSelector: @selector (handleEvent: andReplyWith:)
+ forEventClass: kCoreEventClass
+ andEventID: kAEOpenDocuments];
+}
+
+static void
+gui_unique_quartz_exit (void)
+{
+ g_return_if_fail (GIMP_IS_GIMP (unique_gimp));
+
+ unique_gimp = NULL;
+
+ [[NSAppleEventManager sharedAppleEventManager]
+ removeEventHandlerForEventClass: kCoreEventClass
+ andEventID: kAEOpenDocuments];
+
+ [event_handler release];
+
+ event_handler = NULL;
+}
+
+#else
+
+static void
+gui_dbus_bus_acquired (GDBusConnection *connection,
+ const gchar *name,
+ Gimp *gimp)
+{
+ GDBusObjectSkeleton *object;
+ GObject *service;
+
+ /* this should use GIMP_DBUS_SERVICE_PATH, but that's historically wrong */
+ dbus_manager = g_dbus_object_manager_server_new ("/org/gimp/GIMP");
+
+ object = g_dbus_object_skeleton_new (GIMP_DBUS_INTERFACE_PATH);
+
+ service = gimp_dbus_service_new (gimp);
+ g_dbus_object_skeleton_add_interface (object,
+ G_DBUS_INTERFACE_SKELETON (service));
+ g_object_unref (service);
+
+ g_dbus_object_manager_server_export (dbus_manager, object);
+ g_object_unref (object);
+
+ g_dbus_object_manager_server_set_connection (dbus_manager, connection);
+}
+
+static void
+gui_dbus_name_acquired (GDBusConnection *connection,
+ const gchar *name,
+ Gimp *gimp)
+{
+}
+
+static void
+gui_dbus_name_lost (GDBusConnection *connection,
+ const gchar *name,
+ Gimp *gimp)
+{
+ if (connection == NULL)
+ g_printerr ("%s: connection to the bus cannot be established.\n",
+ G_STRFUNC);
+ else
+ g_printerr ("%s: the name \"%s\" could not be acquired on the bus.\n",
+ G_STRFUNC, name);
+}
+
+static void
+gui_dbus_service_init (Gimp *gimp)
+{
+ g_return_if_fail (GIMP_IS_GIMP (gimp));
+ g_return_if_fail (dbus_name_id == 0);
+
+ dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
+ GIMP_DBUS_SERVICE_NAME,
+ G_BUS_NAME_OWNER_FLAGS_NONE,
+ (GBusAcquiredCallback) gui_dbus_bus_acquired,
+ (GBusNameAcquiredCallback) gui_dbus_name_acquired,
+ (GBusNameLostCallback) gui_dbus_name_lost,
+ gimp, NULL);
+}
+
+static void
+gui_dbus_service_exit (void)
+{
+ g_bus_unown_name (dbus_name_id);
+ g_clear_object (&dbus_manager);
+}
+
+#endif