diff --git a/kitty/cocoa_window.m b/kitty/cocoa_window.m index 0d4118cc4..4d9f448bc 100644 --- a/kitty/cocoa_window.m +++ b/kitty/cocoa_window.m @@ -115,6 +115,58 @@ get_dock_menu(id self UNUSED, SEL _cmd UNUSED, NSApplication *sender UNUSED) { return dockMenu; } +@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); + } +@end + +static PyObject* +cocoa_send_notification(PyObject *self UNUSED, PyObject *args) { + char *title = NULL, *subtitle = NULL, *message = NULL, *path_to_image = NULL; + if (!PyArg_ParseTuple(args, "ssz|z", &title, &message, &path_to_image, &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]; + NSImage *img = nil; + if (path_to_image) { + NSString *p = [NSString stringWithUTF8String:path_to_image]; + NSURL *url = [NSURL fileURLWithPath:p]; + img = [[NSImage alloc] initWithContentsOfURL:url]; + [url release]; [p release]; + } + n.title = title ? [NSString stringWithUTF8String:title] : nil; + n.subtitle = subtitle ? [NSString stringWithUTF8String:subtitle] : nil; + n.informativeText = message ? [NSString stringWithUTF8String:message] : nil; + if (img) { + [n setValue:img forKey:@"_identityImage"]; + [n setValue:@(false) forKey:@"_identityImageHasBorder"]; + } + [center deliverNotification:n]; + if (n.title) { [n.title release]; n.title = nil; } + if (n.subtitle) { [n.subtitle release]; n.subtitle = nil; } + if (n.informativeText) { [n.informativeText release]; n.informativeText = nil; } + if (img) [img release]; + [n release]; + Py_RETURN_NONE; +} + void cocoa_create_global_menu(void) { NSString* app_name = find_app_name(); @@ -323,6 +375,7 @@ cleanup() { static PyMethodDef module_methods[] = { {"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""}, {"cocoa_set_new_window_trigger", (PyCFunction)cocoa_set_new_window_trigger, METH_VARARGS, ""}, + {"cocoa_send_notification", (PyCFunction)cocoa_send_notification, METH_VARARGS, ""}, {NULL, NULL, 0, NULL} /* Sentinel */ }; diff --git a/kitty/constants.py b/kitty/constants.py index 7f586996a..25da7adca 100644 --- a/kitty/constants.py +++ b/kitty/constants.py @@ -116,6 +116,7 @@ def wakeup(): base_dir = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) terminfo_dir = os.path.join(base_dir, 'terminfo') logo_data_file = os.path.join(base_dir, 'logo', 'kitty.rgba') +logo_png_file = os.path.join(base_dir, 'logo', 'kitty.png') beam_cursor_data_file = os.path.join(base_dir, 'logo', 'beam-cursor.png') try: shell_path = pwd.getpwuid(os.geteuid()).pw_shell or '/bin/sh' diff --git a/kitty/notify.py b/kitty/notify.py new file mode 100644 index 000000000..8f889fc5f --- /dev/null +++ b/kitty/notify.py @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# vim:fileencoding=utf-8 +# License: GPLv3 Copyright: 2019, Kovid Goyal + + +import subprocess + +from .constants import is_macos, logo_png_file + + +if is_macos: + from .fast_data_types import cocoa_send_notification + + def notify(title, body, timeout=5000, application='kitty', icon=True): + if icon is True: + icon = None + cocoa_send_notification(title, body, icon) +else: + def notify(title, body, timeout=5000, application='kitty', icon=True): + cmd = ['notify-send', '-t', str(timeout), '-a', application] + if icon is True: + icon = logo_png_file + if icon: + cmd.extend(['-i', icon]) + subprocess.Popen( + cmd + [title, body], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL) diff --git a/setup.py b/setup.py index f1f0ce546..d377cd727 100755 --- a/setup.py +++ b/setup.py @@ -636,6 +636,7 @@ def package(args, for_bundle=False, sh_launcher=False): raise SystemExit('tic failed to output the compiled kitty terminfo file') shutil.copy2('__main__.py', libdir) shutil.copy2('logo/kitty.rgba', os.path.join(libdir, 'logo')) + shutil.copy2('logo/kitty.png', os.path.join(libdir, 'logo')) shutil.copy2('logo/beam-cursor.png', os.path.join(libdir, 'logo')) shutil.copy2('logo/beam-cursor@2x.png', os.path.join(libdir, 'logo'))