[glib] Implement GNotification on OSX
- From: Matthias Clasen <matthiasc src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [glib] Implement GNotification on OSX
- Date: Fri, 5 Jun 2015 18:57:56 +0000 (UTC)
commit 36e093a31a9eb12021e7780b9e322c29763ffa58
Author: Patrick Griffis <tingping tingping se>
Date: Wed Mar 25 15:51:29 2015 -0400
Implement GNotification on OSX
https://bugzilla.gnome.org/show_bug.cgi?id=747146
gio/Makefile.am | 5 +-
gio/gcocoanotificationbackend.c | 278 +++++++++++++++++++++++++++++++++++++++
gio/giomodule.c | 7 +
gio/gnotificationbackend.c | 4 +-
4 files changed, 292 insertions(+), 2 deletions(-)
---
diff --git a/gio/Makefile.am b/gio/Makefile.am
index 757f924..627c63e 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -265,6 +265,9 @@ unix_sources = \
ggtknotificationbackend.c \
$(NULL)
+if OS_COCOA
+unix_sources += gcocoanotificationbackend.c
+endif
giounixincludedir=$(includedir)/gio-unix-2.0/gio
giounixinclude_HEADERS = \
@@ -522,7 +525,7 @@ libgio_2_0_la_LDFLAGS = $(GLIB_LINK_FLAGS) \
if OS_COCOA
# This is dumb. The ObjC source file should be properly named .m
libgio_2_0_la_CFLAGS += -xobjective-c
-libgio_2_0_la_LDFLAGS += -Wl,-framework,Foundation
+libgio_2_0_la_LDFLAGS += -Wl,-framework,Foundation -Wl,-framework,AppKit
endif
libgio_2_0_la_DEPENDENCIES = $(gio_win32_res) $(gio_def) $(platform_deps)
diff --git a/gio/gcocoanotificationbackend.c b/gio/gcocoanotificationbackend.c
new file mode 100644
index 0000000..8a78c99
--- /dev/null
+++ b/gio/gcocoanotificationbackend.c
@@ -0,0 +1,278 @@
+/*
+ * Copyright © 2015 Patrick Griffis
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * 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
+ * Lesser 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 <http://www.gnu.org/licenses/>.
+ *
+ * Authors: Patrick Griffis
+ */
+
+#include "config.h"
+
+#import <Cocoa/Cocoa.h>
+#include "gnotificationbackend.h"
+#include "gapplication.h"
+#include "gaction.h"
+#include "gactiongroup.h"
+#include "giomodule-priv.h"
+#include "gnotification-private.h"
+#include "gthemedicon.h"
+#include "gfileicon.h"
+#include "gfile.h"
+
+#define G_TYPE_COCOA_NOTIFICATION_BACKEND (g_cocoa_notification_backend_get_type ())
+#define G_COCOA_NOTIFICATION_BACKEND(o) (G_TYPE_CHECK_INSTANCE_CAST ((o),
G_TYPE_COCOA_NOTIFICATION_BACKEND, GCocoaNotificationBackend))
+
+typedef struct _GCocoaNotificationBackend GCocoaNotificationBackend;
+typedef GNotificationBackendClass GCocoaNotificationBackendClass;
+struct _GCocoaNotificationBackend
+{
+ GNotificationBackend parent;
+};
+
+GType g_cocoa_notification_backend_get_type (void);
+
+G_DEFINE_TYPE_WITH_CODE (GCocoaNotificationBackend, g_cocoa_notification_backend,
G_TYPE_NOTIFICATION_BACKEND,
+ _g_io_modules_ensure_extension_points_registered ();
+ g_io_extension_point_implement (G_NOTIFICATION_BACKEND_EXTENSION_POINT_NAME, g_define_type_id, "cocoa",
0));
+
+static NSString *
+nsstring_from_cstr (const char *cstr)
+{
+ if (!cstr)
+ return nil;
+
+ return [[NSString alloc] initWithUTF8String:cstr];
+}
+
+static NSImage*
+nsimage_from_gicon (GIcon *icon)
+{
+ if (G_IS_FILE_ICON (icon))
+ {
+ NSImage *image = nil;
+ GFile *file;
+ char *path;
+
+ file = g_file_icon_get_file (G_FILE_ICON (icon));
+ path = g_file_get_path (file);
+ if (path)
+ {
+ NSString *str_path = nsstring_from_cstr (path);
+ image = [[NSImage alloc] initByReferencingFile:str_path];
+
+ [str_path release];
+ g_free (path);
+ }
+ return image;
+ }
+ else
+ {
+ g_warning ("This icon type is not handled by this NotificationBackend");
+ return nil;
+ }
+}
+
+static void
+activate_detailed_action (const char * action)
+{
+ char *name;
+ GVariant *target;
+
+ if (!g_str_has_prefix (action, "app."))
+ {
+ g_warning ("Notification action does not have \"app.\" prefix");
+ return;
+ }
+
+ if (g_action_parse_detailed_name (action, &name, &target, NULL))
+ {
+ g_action_group_activate_action (G_ACTION_GROUP (g_application_get_default()), name + 4, target);
+ g_free (name);
+ if (target)
+ g_variant_unref (target);
+ }
+}
+
+ interface GNotificationCenterDelegate : NSObject<NSUserNotificationCenterDelegate> @end
+ implementation GNotificationCenterDelegate
+
+-(void) userNotificationCenter:(NSUserNotificationCenter*) center
+ didActivateNotification:(NSUserNotification*) notification
+{
+ if ([notification activationType] == NSUserNotificationActivationTypeContentsClicked)
+ {
+ const char *action = [[notification userInfo][@"default"] UTF8String];
+ if (action)
+ activate_detailed_action (action);
+ /* OSX Always activates the front window */
+ }
+ else if ([notification activationType] == NSUserNotificationActivationTypeActionButtonClicked)
+ {
+ const char *action = [[notification userInfo][@"button0"] UTF8String];
+ if (action)
+ activate_detailed_action (action);
+ }
+
+ [center removeDeliveredNotification:notification];
+}
+
+ end
+
+static GNotificationCenterDelegate *cocoa_notification_delegate;
+
+static gboolean
+g_cocoa_notification_backend_is_supported (void)
+{
+ NSBundle *bundle = [NSBundle mainBundle];
+
+ /* This is always actually supported, but without a bundle it does nothing */
+ if (![bundle bundleIdentifier])
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+add_actions_to_notification (NSUserNotification *userNotification,
+ GNotification *notification)
+{
+ guint n_buttons = g_notification_get_n_buttons (notification);
+ char *action = NULL, *label = NULL;
+ GVariant *target = NULL;
+ NSMutableDictionary *user_info = nil;
+
+ if (g_notification_get_default_action (notification, &action, &target))
+ {
+ char *detailed_name = g_action_print_detailed_name (action, target);
+ NSString *action_name = nsstring_from_cstr (detailed_name);
+ user_info = [[NSMutableDictionary alloc] init];
+
+ user_info[@"default"] = action_name;
+
+ [action_name release];
+ g_free (detailed_name);
+ g_clear_pointer (&action, g_free);
+ g_clear_pointer (&target, g_variant_unref);
+ }
+
+ if (n_buttons)
+ {
+ g_notification_get_button (notification, 0, &label, &action, &target);
+ if (label)
+ {
+ NSString *str_label = nsstring_from_cstr (label);
+ char *detailed_name = g_action_print_detailed_name (action, target);
+ NSString *action_name = nsstring_from_cstr (detailed_name);
+
+ if (!user_info)
+ user_info = [[NSMutableDictionary alloc] init];
+
+ user_info[@"button0"] = action_name;
+ userNotification.actionButtonTitle = str_label;
+
+ [str_label release];
+ [action_name release];
+ g_free (label);
+ g_free (action);
+ g_free (detailed_name);
+ g_clear_pointer (&target, g_variant_unref);
+ }
+
+ if (n_buttons > 1)
+ g_warning ("Only a single button is currently supported by this NotificationBackend");
+ }
+
+ userNotification.userInfo = user_info;
+ [user_info release];
+}
+
+static void
+g_cocoa_notification_backend_send_notification (GNotificationBackend *backend,
+ const gchar *cstr_id,
+ GNotification *notification)
+{
+ NSString *str_title = nil, *str_text = nil, *str_id = nil;
+ NSImage *content = nil;
+ const char *cstr;
+ GIcon *icon;
+ NSUserNotification *userNotification;
+ NSUserNotificationCenter *center;
+
+ if ((cstr = g_notification_get_title (notification)))
+ str_title = nsstring_from_cstr (cstr);
+ if ((cstr = g_notification_get_body (notification)))
+ str_text = nsstring_from_cstr (cstr);
+ if (cstr_id != NULL)
+ str_id = nsstring_from_cstr (cstr_id);
+ if ((icon = g_notification_get_icon (notification)))
+ content = nsimage_from_gicon (icon);
+ /* NOTE: There is no priority */
+
+ userNotification = [NSUserNotification new];
+ userNotification.title = str_title;
+ userNotification.informativeText = str_text;
+ userNotification.identifier = str_id;
+ userNotification.contentImage = content;
+ /* NOTE: Buttons only show up if your bundle has NSUserNotificationAlertStyle set to "alerts" */
+ add_actions_to_notification (userNotification, notification);
+
+ if (!cocoa_notification_delegate)
+ cocoa_notification_delegate = [[GNotificationCenterDelegate alloc] init];
+
+ center = [NSUserNotificationCenter defaultUserNotificationCenter];
+ center.delegate = cocoa_notification_delegate;
+ [center deliverNotification:userNotification];
+
+ [str_title release];
+ [str_text release];
+ [str_id release];
+ [content release];
+ [userNotification release];
+}
+
+static void
+g_cocoa_notification_backend_withdraw_notification (GNotificationBackend *backend,
+ const gchar *cstr_id)
+{
+ NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
+ NSArray *notifications = [center deliveredNotifications];
+ NSString *str_id = nsstring_from_cstr (cstr_id);
+
+ for (NSUserNotification *notification in notifications)
+ {
+ if ([notification.identifier compare:str_id] == NSOrderedSame)
+ {
+ [center removeDeliveredNotification:notification];
+ break;
+ }
+ }
+
+ [notifications release];
+ [str_id release];
+}
+
+static void
+g_cocoa_notification_backend_init (GCocoaNotificationBackend *backend)
+{
+}
+
+static void
+g_cocoa_notification_backend_class_init (GCocoaNotificationBackendClass *klass)
+{
+ GNotificationBackendClass *backend_class = G_NOTIFICATION_BACKEND_CLASS (klass);
+
+ backend_class->is_supported = g_cocoa_notification_backend_is_supported;
+ backend_class->send_notification = g_cocoa_notification_backend_send_notification;
+ backend_class->withdraw_notification = g_cocoa_notification_backend_withdraw_notification;
+}
diff --git a/gio/giomodule.c b/gio/giomodule.c
index f559c74..11581b4 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -911,6 +911,10 @@ extern GType g_fdo_notification_backend_get_type (void);
extern GType g_gtk_notification_backend_get_type (void);
#endif
+#ifdef HAVE_COCOA
+extern GType g_cocoa_notification_backend_get_type (void);
+#endif
+
#ifdef G_PLATFORM_WIN32
#include <windows.h>
@@ -1084,6 +1088,9 @@ _g_io_modules_ensure_loaded (void)
g_type_ensure (g_fdo_notification_backend_get_type ());
g_type_ensure (g_gtk_notification_backend_get_type ());
#endif
+#ifdef HAVE_COCOA
+ g_type_ensure (g_cocoa_notification_backend_get_type ());
+#endif
#ifdef G_OS_WIN32
g_type_ensure (_g_winhttp_vfs_get_type ());
#endif
diff --git a/gio/gnotificationbackend.c b/gio/gnotificationbackend.c
index c1e009d..c5d5b5b 100644
--- a/gio/gnotificationbackend.c
+++ b/gio/gnotificationbackend.c
@@ -55,7 +55,9 @@ g_notification_backend_new_default (GApplication *application)
*/
backend->application = application;
- backend->dbus_connection = g_object_ref (g_application_get_dbus_connection (application));
+ backend->dbus_connection = g_application_get_dbus_connection (application);
+ if (backend->dbus_connection)
+ g_object_ref (backend->dbus_connection);
return backend;
}
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]