From 0813a3daffb6ea161eb90a41bbb118ec2b890515 Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Thu, 25 Jul 2024 18:59:31 +0530 Subject: [PATCH] Report if close events are supported in the query response --- docs/desktop-notifications.rst | 14 ++++++++++++++ kitty/notifications.py | 24 +++++++++++++++++------- kitty_tests/notifications.py | 2 +- 3 files changed, 32 insertions(+), 8 deletions(-) diff --git a/docs/desktop-notifications.rst b/docs/desktop-notifications.rst index 558bb5199..e45f47f57 100644 --- a/docs/desktop-notifications.rst +++ b/docs/desktop-notifications.rst @@ -99,6 +99,9 @@ to display it based on what it does understand. Being informed when a notification is closed ------------------------------------------------ +.. versionadded:: 0.36.0 + Notifications of close events were added in kitty version 0.36.0 + If you wish to be informed when a notification is closed, you can specify ``c=1`` when sending the notification. For example:: @@ -113,6 +116,12 @@ If no notification id was specified ``i=0`` will be used. If ``a=report`` is specified and the notification is activated/clicked on then both the activation report and close notification are sent. +.. note:: + Close events are best effort, some platforms such as macOS do not have + events when notifications are closed. Applications can use the + :ref:`notifications_query` to check if close events are supported + by the current terminal emulator. + Closing an existing notification ---------------------------------- @@ -128,6 +137,8 @@ This will close a previous notification with the specified id. If no such notification exists (perhaps because it was already closed or it was activated) then the request is ignored. +.. _notifications_query: + Querying for support ------------------------- @@ -167,6 +178,9 @@ Key Value ``p`` Comma spearated list of supported payload types (i.e. values of the ``p`` key that the terminal implements). These must contain at least ``title`` and ``body``. + +``c`` ``c=1`` if the terminal supports close events, otherwise the ``c`` + must be omitted. ======= ================================================================================ In the future, if this protocol expands, more keys might be added. Clients must diff --git a/kitty/notifications.py b/kitty/notifications.py index 0721e3b60..68673bd8c 100644 --- a/kitty/notifications.py +++ b/kitty/notifications.py @@ -249,6 +249,8 @@ class NotificationCommand: class DesktopIntegration: + supports_close_events: bool = True + def __init__(self, notification_manager: 'NotificationManager'): self.notification_manager = notification_manager self.initialize() @@ -274,9 +276,20 @@ class DesktopIntegration: from .update_check import notification_activated notification_activated() + def query_response(self, identifier: str) -> str: + actions = ','.join(x.value for x in Action) + when = ','.join(x.value for x in OnlyWhen if x.value) + urgency = ','.join(str(x.value) for x in Urgency) + i = f'i={identifier or "0"}:' + p = ','.join(x.value for x in PayloadType if x.value) + c = ':c=1' if self.supports_close_events else '' + return f'99;{i}p=?;a={actions}:o={when}:u={urgency}:p={p}{c}' + class MacOSIntegration(DesktopIntegration): + supports_close_events: bool = False + def initialize(self) -> None: from .fast_data_types import cocoa_set_notification_activated_callback self.id_counter = count(start=1) @@ -543,7 +556,9 @@ class NotificationManager: _, cmd = self.in_progress_notification_commands.popitem(False) self.in_progress_notification_commands_by_client_id.pop(cmd.identifier, None) - def parse_notification_cmd(self, prev_cmd: NotificationCommand, channel_id: int, raw: str) -> Optional[NotificationCommand]: + def parse_notification_cmd( + self, prev_cmd: NotificationCommand, channel_id: int, raw: str + ) -> Optional[NotificationCommand]: metadata, payload = raw.partition(';')[::2] cmd = NotificationCommand() try: @@ -552,12 +567,7 @@ class NotificationManager: self.log('Malformed metadata section in OSC 99: ' + metadata) return None if payload_type is PayloadType.query: - actions = ','.join(x.value for x in Action) - when = ','.join(x.value for x in OnlyWhen if x.value) - urgency = ','.join(str(x.value) for x in Urgency) - i = f'i={cmd.identifier or "0"}:' - p = ','.join(x.value for x in PayloadType if x.value) - self.channel.send(channel_id, f'99;{i}p=?;a={actions}:o={when}:u={urgency}:p={p}') + self.channel.send(channel_id, self.desktop_integration.query_response(cmd.identifier)) return None if payload_type is PayloadType.close: if cmd.identifier: diff --git a/kitty_tests/notifications.py b/kitty_tests/notifications.py index a38fc6975..3c3b35542 100644 --- a/kitty_tests/notifications.py +++ b/kitty_tests/notifications.py @@ -204,7 +204,7 @@ def do_test(self: 'TestNotifications') -> None: # Test querying h('i=xyz:p=?') self.assertFalse(di.notifications) - qr = 'a=focus,report:o=always,unfocused,invisible:u=0,1,2:p=title,body,?,close' + qr = 'a=focus,report:o=always,unfocused,invisible:u=0,1,2:p=title,body,?,close:c=1' self.ae(ch.responses, [f'99;i=xyz:p=?;{qr}']) reset() h('p=?')