From 0ae1f9906f7e79fba190abc19408a28ee7281b06 Mon Sep 17 00:00:00 2001 From: Luflosi Date: Mon, 28 Sep 2020 01:33:40 +0200 Subject: [PATCH] macOS: re-add deprecated notification API The new User Notifications Framework is only available on macOS 10.14 and above, while the old NSUserNotification API is deprecated in macOS 11 (Big Sur) and will probably be removed in the future. This commit compiles a simple test program to see if the Framework is available and then uses either the new or the old API. --- kitty/cocoa_window.m | 78 +++++++++++++++++++++++++++++++++++++++----- setup.py | 9 +++-- 2 files changed, 77 insertions(+), 10 deletions(-) diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index 9b81b276a..bd4f0deb4 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -9,7 +9,9 @@ #include "state.h" #include "monotonic.h" #include +#if !defined(KITTY_USE_DEPRECATED_MACOS_NOTIFICATION_API) #include +#endif #include // Needed for _NSGetProgname @@ -135,6 +137,68 @@ get_dock_menu(id self UNUSED, SEL _cmd UNUSED, NSApplication *sender UNUSED) { static PyObject *notification_activated_callback = NULL; +static PyObject* +set_notification_activated_callback(PyObject *self UNUSED, PyObject *callback) { + if (notification_activated_callback) Py_DECREF(notification_activated_callback); + notification_activated_callback = callback; + Py_INCREF(callback); + Py_RETURN_NONE; +} + +#if defined(KITTY_USE_DEPRECATED_MACOS_NOTIFICATION_API) + +@interface NotificationDelegate : NSObject +@end + +@implementation NotificationDelegate + - (void)userNotificationCenter:(NSUserNotificationCenter *)center + didDeliverNotification:(NSUserNotification *)notification { + (void)(center); (void)(notification); + } + + - (BOOL) userNotificationCenter:(NSUserNotificationCenter *)center + shouldPresentNotification:(NSUserNotification *)notification { + (void)(center); (void)(notification); + return YES; + } + + - (void) userNotificationCenter:(NSUserNotificationCenter *)center + didActivateNotification:(NSUserNotification *)notification { + (void)(center); (void)(notification); + if (notification_activated_callback) { + PyObject *ret = PyObject_CallFunction(notification_activated_callback, "z", + notification.userInfo[@"user_id"] ? [notification.userInfo[@"user_id"] UTF8String] : NULL); + if (ret == NULL) PyErr_Print(); + else Py_DECREF(ret); + } + } +@end + +static PyObject* +cocoa_send_notification(PyObject *self UNUSED, PyObject *args) { + char *identifier = NULL, *title = NULL, *informativeText = NULL, *subtitle = NULL; + if (!PyArg_ParseTuple(args, "zsz|z", &identifier, &title, &informativeText, &subtitle)) return NULL; + NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter]; + if (!center) {PyErr_SetString(PyExc_RuntimeError, "Failed to get the user notification center"); return NULL; } + if (!center.delegate) center.delegate = [[NotificationDelegate alloc] init]; + NSUserNotification *n = [NSUserNotification new]; +#define SET(x) { \ + if (x) { \ + NSString *t = @(x); \ + n.x = t; \ + [t release]; \ + }} + SET(title); SET(subtitle); SET(informativeText); +#undef SET + if (identifier) { + n.userInfo = @{@"user_id": @(identifier)}; + } + [center deliverNotification:n]; + Py_RETURN_NONE; +} + +#else + @interface NotificationDelegate : NSObject @end @@ -213,14 +277,6 @@ drain_pending_notifications(BOOL granted) { } } -PyObject* -set_notification_activated_callback(PyObject *self UNUSED, PyObject *callback) { - if (notification_activated_callback) Py_DECREF(notification_activated_callback); - notification_activated_callback = callback; - Py_INCREF(callback); - Py_RETURN_NONE; -} - static PyObject* cocoa_send_notification(PyObject *self UNUSED, PyObject *args) { char *identifier = NULL, *title = NULL, *body = NULL, *subtitle = NULL; @@ -244,6 +300,8 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) { Py_RETURN_NONE; } +#endif + @interface ServiceProvider : NSObject @end @@ -539,10 +597,14 @@ cleanup() { if (dockMenu) [dockMenu release]; dockMenu = nil; Py_CLEAR(notification_activated_callback); + + #if !defined(KITTY_USE_DEPRECATED_MACOS_NOTIFICATION_API) drain_pending_notifications(NO); free(notification_queue.notifications); notification_queue.notifications = NULL; notification_queue.capacity = 0; + #endif + } // autoreleasepool } diff --git a/setup.py b/setup.py index 3aaedd36c..bb15a098d 100755 --- a/setup.py +++ b/setup.py @@ -213,7 +213,8 @@ def get_sanitize_args(cc: str, ccver: Tuple[int, int]) -> List[str]: def test_compile(cc: str, *cflags: str, src: Optional[str] = None) -> bool: src = src or 'int main(void) { return 0; }' - p = subprocess.Popen([cc] + list(cflags) + ['-x', 'c', '-o', os.devnull, '-'], stdin=subprocess.PIPE) + p = subprocess.Popen([cc] + list(cflags) + ['-x', 'c', '-o', os.devnull, '-'], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.PIPE) stdin = p.stdin assert stdin is not None try: @@ -336,8 +337,12 @@ def kitty_env() -> Env: if is_macos: platform_libs = [ '-framework', 'CoreText', '-framework', 'CoreGraphics', - '-framework', 'UserNotifications' ] + user_notifications_framework = first_successful_compile(ans.cc, '-framework UserNotifications') + if user_notifications_framework != '': + platform_libs.extend(shlex.split(user_notifications_framework)) + else: + cppflags.append('-DKITTY_USE_DEPRECATED_MACOS_NOTIFICATION_API') # Apple deprecated OpenGL in Mojave (10.14) silence the endless # warnings about it cppflags.append('-DGL_SILENCE_DEPRECATION')