mirror of
https://gitlab.gnome.org/GNOME/glib.git
synced 2024-11-10 03:16:17 +01:00
Implement GNotification on OSX
https://bugzilla.gnome.org/show_bug.cgi?id=747146
This commit is contained in:
parent
89058e8a9b
commit
36e093a31a
@ -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)
|
||||
|
278
gio/gcocoanotificationbackend.c
Normal file
278
gio/gcocoanotificationbackend.c
Normal file
@ -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;
|
||||
}
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user