PIKApp/app/gui/gui-unique.c

414 lines
11 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
*
* 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>
#endif
#include "gui/gui-types.h"
#include "core/pika.h"
#include "core/pikacontainer.h"
#include "display/pikadisplay.h"
#include "display/pikadisplayshell.h"
#include "display/pikaimagewindow.h"
#include "file/file-open.h"
#include "pikadbusservice.h"
#include "gui-unique.h"
#ifdef G_OS_WIN32
static void gui_unique_win32_init (Pika *pika);
static void gui_unique_win32_exit (void);
static Pika *unique_pika = NULL;
static HWND proxy_window = NULL;
#elif defined (GDK_WINDOWING_QUARTZ)
static void gui_unique_quartz_init (Pika *pika);
static void gui_unique_quartz_exit (void);
@interface PikaAppleEventHandler : NSObject {}
- (void) handleEvent:(NSAppleEventDescriptor *) inEvent
andReplyWith:(NSAppleEventDescriptor *) replyEvent;
@end
static Pika *unique_pika = NULL;
static PikaAppleEventHandler *event_handler = NULL;
#else
static void gui_dbus_service_init (Pika *pika);
static void gui_dbus_service_exit (void);
static GDBusObjectManagerServer *dbus_manager = NULL;
static guint dbus_name_id = 0;
#endif
void
gui_unique_init (Pika *pika)
{
#ifdef G_OS_WIN32
gui_unique_win32_init (pika);
#elif defined (GDK_WINDOWING_QUARTZ)
gui_unique_quartz_init (pika);
#else
gui_dbus_service_init (pika);
#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 PIKA is not fully
* started yet.
*/
if (! pika_is_restored (unique_pika))
return TRUE;
if (data->file)
{
file_open_from_command_line (unique_pika, data->file,
data->as_new, NULL);
}
else
{
/* raise the first display */
PikaObject *display;
display = pika_container_get_first_child (unique_pika->displays);
pika_display_shell_present (pika_display_get_shell (PIKA_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_pika)
{
COPYDATASTRUCT *copydata = (COPYDATASTRUCT *) lParam;
PikaObject *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_pika), 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 = pika_container_get_first_child (unique_pika->displays);
if (display)
pika_display_shell_present (pika_display_get_shell (PIKA_DISPLAY (display)));
}
return TRUE;
default:
return DefWindowProcW (hWnd, uMsg, wParam, lParam);
}
}
static void
gui_unique_win32_init (Pika *pika)
{
WNDCLASSW wc;
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (unique_pika == NULL);
unique_pika = pika;
/* register window class for proxy window */
memset (&wc, 0, sizeof (wc));
wc.hInstance = GetModuleHandleW (NULL);
wc.lpfnWndProc = gui_unique_win32_message_handler;
wc.lpszClassName = PIKA_UNIQUE_WIN32_WINDOW_CLASS;
RegisterClassW (&wc);
proxy_window = CreateWindowExW (0,
PIKA_UNIQUE_WIN32_WINDOW_CLASS,
PIKA_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 (PIKA_IS_PIKA (unique_pika));
unique_pika = 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 PIKA is not fully
* started yet.
*/
if (! pika_is_restored (unique_pika))
return TRUE;
if (file)
{
file_open_from_command_line (unique_pika, file, FALSE, NULL);
}
return FALSE;
}
@implementation PikaAppleEventHandler
- (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_pika), 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 (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (unique_pika == NULL);
unique_pika = pika;
/* 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 = [[PikaAppleEventHandler 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 (PIKA_IS_PIKA (unique_pika));
unique_pika = 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,
Pika *pika)
{
GDBusObjectSkeleton *object;
GObject *service;
/* this should use PIKA_DBUS_SERVICE_PATH, but that's historically wrong */
dbus_manager = g_dbus_object_manager_server_new ("/technology.heckin/PIKA");
object = g_dbus_object_skeleton_new (PIKA_DBUS_INTERFACE_PATH);
service = pika_dbus_service_new (pika);
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,
Pika *pika)
{
}
static void
gui_dbus_name_lost (GDBusConnection *connection,
const gchar *name,
Pika *pika)
{
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 (Pika *pika)
{
g_return_if_fail (PIKA_IS_PIKA (pika));
g_return_if_fail (dbus_name_id == 0);
dbus_name_id = g_bus_own_name (G_BUS_TYPE_SESSION,
PIKA_DBUS_SERVICE_NAME,
G_BUS_NAME_OWNER_FLAGS_NONE,
(GBusAcquiredCallback) gui_dbus_bus_acquired,
(GBusNameAcquiredCallback) gui_dbus_name_acquired,
(GBusNameLostCallback) gui_dbus_name_lost,
pika, NULL);
}
static void
gui_dbus_service_exit (void)
{
g_bus_unown_name (dbus_name_id);
g_clear_object (&dbus_manager);
}
#endif