version check infrastructure
This commit is contained in:
parent
6a9a7dee55
commit
c7a0626c69
@ -115,6 +115,8 @@ get_dock_menu(id self UNUSED, SEL _cmd UNUSED, NSApplication *sender UNUSED) {
|
|||||||
return dockMenu;
|
return dockMenu;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static PyObject *notification_activated_callback = NULL;
|
||||||
|
|
||||||
@interface NotificationDelegate : NSObject <NSUserNotificationCenterDelegate>
|
@interface NotificationDelegate : NSObject <NSUserNotificationCenterDelegate>
|
||||||
@end
|
@end
|
||||||
|
|
||||||
@ -133,13 +135,19 @@ get_dock_menu(id self UNUSED, SEL _cmd UNUSED, NSApplication *sender UNUSED) {
|
|||||||
- (void) userNotificationCenter:(NSUserNotificationCenter *)center
|
- (void) userNotificationCenter:(NSUserNotificationCenter *)center
|
||||||
didActivateNotification:(NSUserNotification *)notification {
|
didActivateNotification:(NSUserNotification *)notification {
|
||||||
(void)(center); (void)(notification);
|
(void)(center); (void)(notification);
|
||||||
|
if (notification_activated_callback) {
|
||||||
|
PyObject *ret = PyObject_CallFunction(notification_activated_callback, "z",
|
||||||
|
notification.identifier ? [notification.identifier UTF8String] : NULL);
|
||||||
|
if (ret == NULL) PyErr_Print();
|
||||||
|
else Py_DECREF(ret);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@end
|
@end
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
|
cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
|
||||||
char *title = NULL, *subtitle = NULL, *message = NULL, *path_to_image = NULL;
|
char *identifier = NULL, *title = NULL, *subtitle = NULL, *message = NULL, *path_to_image = NULL;
|
||||||
if (!PyArg_ParseTuple(args, "ssz|z", &title, &message, &path_to_image, &subtitle)) return NULL;
|
if (!PyArg_ParseTuple(args, "zssz|z", &identifier, &title, &message, &path_to_image, &subtitle)) return NULL;
|
||||||
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
NSUserNotificationCenter *center = [NSUserNotificationCenter defaultUserNotificationCenter];
|
||||||
if (!center) {PyErr_SetString(PyExc_RuntimeError, "Failed to get the user notification center"); return NULL; }
|
if (!center) {PyErr_SetString(PyExc_RuntimeError, "Failed to get the user notification center"); return NULL; }
|
||||||
if (!center.delegate) center.delegate = [[NotificationDelegate alloc] init];
|
if (!center.delegate) center.delegate = [[NotificationDelegate alloc] init];
|
||||||
@ -151,6 +159,7 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
|
|||||||
img = [[NSImage alloc] initWithContentsOfURL:url];
|
img = [[NSImage alloc] initWithContentsOfURL:url];
|
||||||
[url release]; [p release];
|
[url release]; [p release];
|
||||||
}
|
}
|
||||||
|
n.identifier = identifier ? [NSString stringWithUTF8String:identifier] : nil;
|
||||||
n.title = title ? [NSString stringWithUTF8String:title] : nil;
|
n.title = title ? [NSString stringWithUTF8String:title] : nil;
|
||||||
n.subtitle = subtitle ? [NSString stringWithUTF8String:subtitle] : nil;
|
n.subtitle = subtitle ? [NSString stringWithUTF8String:subtitle] : nil;
|
||||||
n.informativeText = message ? [NSString stringWithUTF8String:message] : nil;
|
n.informativeText = message ? [NSString stringWithUTF8String:message] : nil;
|
||||||
@ -159,6 +168,7 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
|
|||||||
[n setValue:@(false) forKey:@"_identityImageHasBorder"];
|
[n setValue:@(false) forKey:@"_identityImageHasBorder"];
|
||||||
}
|
}
|
||||||
[center deliverNotification:n];
|
[center deliverNotification:n];
|
||||||
|
if (n.identifier) { [n.identifier release]; n.identifier = nil; }
|
||||||
if (n.title) { [n.title release]; n.title = nil; }
|
if (n.title) { [n.title release]; n.title = nil; }
|
||||||
if (n.subtitle) { [n.subtitle release]; n.subtitle = nil; }
|
if (n.subtitle) { [n.subtitle release]; n.subtitle = nil; }
|
||||||
if (n.informativeText) { [n.informativeText release]; n.informativeText = nil; }
|
if (n.informativeText) { [n.informativeText release]; n.informativeText = nil; }
|
||||||
@ -167,6 +177,41 @@ cocoa_send_notification(PyObject *self UNUSED, PyObject *args) {
|
|||||||
Py_RETURN_NONE;
|
Py_RETURN_NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
SIGTERM_handler(int signum UNUSED) {
|
||||||
|
exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
call_timer_callback(PyObject *timer_callback) {
|
||||||
|
PyObject *ret = PyObject_CallObject(timer_callback, NULL);
|
||||||
|
if (ret == NULL) PyErr_Print();
|
||||||
|
else Py_DECREF(ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
cocoa_run_notification_loop(PyObject *self UNUSED, PyObject *args) {
|
||||||
|
PyObject *timer_callback;
|
||||||
|
double timeout;
|
||||||
|
if (!PyArg_ParseTuple(args, "OOd", ¬ification_activated_callback, &timer_callback, &timeout)) return NULL;
|
||||||
|
signal(SIGTERM, SIGTERM_handler);
|
||||||
|
signal(SIGINT, SIGTERM_handler);
|
||||||
|
NSAutoreleasePool *pool = [NSAutoreleasePool new];
|
||||||
|
NSApplication * application = [NSApplication sharedApplication];
|
||||||
|
// prevent icon in dock
|
||||||
|
[application setActivationPolicy:NSApplicationActivationPolicyAccessory];
|
||||||
|
// timer will fire after timeout, so fire it once at the start
|
||||||
|
call_timer_callback(timer_callback);
|
||||||
|
[NSTimer scheduledTimerWithTimeInterval:timeout
|
||||||
|
repeats:YES
|
||||||
|
block:^(NSTimer *timer UNUSED) {
|
||||||
|
call_timer_callback(timer_callback);
|
||||||
|
}];
|
||||||
|
[application run];
|
||||||
|
[pool drain];
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
cocoa_create_global_menu(void) {
|
cocoa_create_global_menu(void) {
|
||||||
NSString* app_name = find_app_name();
|
NSString* app_name = find_app_name();
|
||||||
@ -376,6 +421,7 @@ static PyMethodDef module_methods[] = {
|
|||||||
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
{"cocoa_get_lang", (PyCFunction)cocoa_get_lang, METH_NOARGS, ""},
|
||||||
{"cocoa_set_new_window_trigger", (PyCFunction)cocoa_set_new_window_trigger, METH_VARARGS, ""},
|
{"cocoa_set_new_window_trigger", (PyCFunction)cocoa_set_new_window_trigger, METH_VARARGS, ""},
|
||||||
{"cocoa_send_notification", (PyCFunction)cocoa_send_notification, METH_VARARGS, ""},
|
{"cocoa_send_notification", (PyCFunction)cocoa_send_notification, METH_VARARGS, ""},
|
||||||
|
{"cocoa_run_notification_loop", (PyCFunction)cocoa_run_notification_loop, METH_VARARGS, ""},
|
||||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@ -3,20 +3,76 @@
|
|||||||
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
import time
|
||||||
|
from urllib.request import urlopen
|
||||||
|
|
||||||
from .constants import is_macos, logo_png_file
|
from .constants import cache_dir, is_macos, logo_png_file, version
|
||||||
|
from .utils import open_url
|
||||||
|
|
||||||
|
CHANGELOG_URL = 'https://sw.kovidgoyal.net/kitty/changelog.html'
|
||||||
|
RELEASED_VERSION_URL = 'https://sw.kovidgoyal.net/kitty/current-version.txt'
|
||||||
|
CHECK_INTERVAL = 24 * 60 * 60
|
||||||
|
|
||||||
|
|
||||||
|
def version_notification_log():
|
||||||
|
return os.path.join(cache_dir(), 'new-version-notifications.txt')
|
||||||
|
|
||||||
|
|
||||||
|
def notify_new_version(version):
|
||||||
|
notify('kitty update available!', 'kitty version {} released'.format('.'.join(map(str, version))))
|
||||||
|
|
||||||
|
|
||||||
|
def get_released_version():
|
||||||
|
try:
|
||||||
|
raw = urlopen(RELEASED_VERSION_URL).read().decode('utf-8').strip()
|
||||||
|
except Exception:
|
||||||
|
raw = '0.0.0'
|
||||||
|
return tuple(map(int, raw.split('.')))
|
||||||
|
|
||||||
|
|
||||||
|
notified_versions = set()
|
||||||
|
|
||||||
|
|
||||||
|
def save_notification(version):
|
||||||
|
notified_versions.add(version)
|
||||||
|
version = '.'.join(map(str, version))
|
||||||
|
with open(version_notification_log(), 'a') as f:
|
||||||
|
print(version, file=f)
|
||||||
|
|
||||||
|
|
||||||
|
def already_notified(version):
|
||||||
|
if not hasattr(already_notified, 'read_cache'):
|
||||||
|
already_notified.read_cache = True
|
||||||
|
with open(version_notification_log()) as f:
|
||||||
|
for line in f:
|
||||||
|
notified_versions.add(tuple(map(int, line.strip().split('.'))))
|
||||||
|
return tuple(version) in notified_versions
|
||||||
|
|
||||||
|
|
||||||
if is_macos:
|
if is_macos:
|
||||||
from .fast_data_types import cocoa_send_notification
|
from .fast_data_types import cocoa_send_notification, cocoa_run_notification_loop
|
||||||
|
|
||||||
def notify(title, body, timeout=5000, application='kitty', icon=True):
|
def notify(title, body, timeout=5000, application='kitty', icon=True, identifier=None):
|
||||||
if icon is True:
|
if icon is True:
|
||||||
icon = None
|
icon = None
|
||||||
cocoa_send_notification(title, body, icon)
|
cocoa_send_notification(identifier, title, body, icon)
|
||||||
|
|
||||||
|
def notification_activated(notification_identifier):
|
||||||
|
open_url(CHANGELOG_URL)
|
||||||
|
|
||||||
|
def do_check():
|
||||||
|
new_version = get_released_version()
|
||||||
|
if new_version > version and not already_notified(new_version):
|
||||||
|
save_notification(new_version)
|
||||||
|
notify_new_version(new_version)
|
||||||
|
|
||||||
|
def update_check():
|
||||||
|
cocoa_run_notification_loop(notification_activated, do_check, CHECK_INTERVAL)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
def notify(title, body, timeout=5000, application='kitty', icon=True):
|
def notify(title, body, timeout=5000, application='kitty', icon=True, identifier=None):
|
||||||
cmd = ['notify-send', '-t', str(timeout), '-a', application]
|
cmd = ['notify-send', '-t', str(timeout), '-a', application]
|
||||||
if icon is True:
|
if icon is True:
|
||||||
icon = logo_png_file
|
icon = logo_png_file
|
||||||
@ -24,3 +80,11 @@ else:
|
|||||||
cmd.extend(['-i', icon])
|
cmd.extend(['-i', icon])
|
||||||
subprocess.Popen(
|
subprocess.Popen(
|
||||||
cmd + [title, body], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL)
|
cmd + [title, body], stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL, stdin=subprocess.DEVNULL)
|
||||||
|
|
||||||
|
def update_check():
|
||||||
|
while True:
|
||||||
|
new_version = get_released_version()
|
||||||
|
if new_version > version and not already_notified(new_version):
|
||||||
|
save_notification(new_version)
|
||||||
|
notify_new_version(new_version)
|
||||||
|
time.sleep(CHECK_INTERVAL)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user