diff --git a/docs/desktop-notifications.rst b/docs/desktop-notifications.rst index bd139f4c7..3a5baa0fc 100644 --- a/docs/desktop-notifications.rst +++ b/docs/desktop-notifications.rst @@ -102,11 +102,15 @@ Querying for support An application can query the terminal emulator for support of this protocol, by sending the following escape code:: - 99 ; ? ; + 99 ; i= : p=? ; A conforming terminal must respond with an escape code of the form:: - 99 ; ? ; key=value : key=value + 99 ; i= : p=? ; key=value : key=value + +The identifier must be some globally unique identifier string like an UUID. It +is present to support terminal multiplexers, so that they know which window to +redirect the query response too. Here, the ``key=value`` parts specify details about what the terminal implementation supports. Currently, the following keys are defined: @@ -150,8 +154,8 @@ Key Value Default Description ``i`` ``[a-zA-Z0-9-_+.]`` ``0`` Identifier for the notification -``p`` One of ``title`` or ``title`` Whether the payload is the notification title or body. If a - ``body``. notification has no title, the body will be used as title. +``p`` One of ``title``, ``title`` Whether the payload is the notification title or body or query. If a + ``body`` or ``?`` notification has no title, the body will be used as title. ``o`` One of ``always``, ``always`` When to honor the notification request. ``unfocused`` means when the window ``unfocused`` or the notification is sent on does not have keyboard focus. ``invisible`` diff --git a/kitty/notify.py b/kitty/notify.py index da201ce6b..f344fe663 100644 --- a/kitty/notify.py +++ b/kitty/notify.py @@ -140,11 +140,6 @@ def __init__(self, response_string: str): def parse_osc_99(raw: str) -> NotificationCommand: cmd = NotificationCommand() metadata, payload = raw.partition(';')[::2] - if metadata == '?': - actions = ','.join(x.name for x in Action) - when = ','.join(x.name for x in OnlyWhen if x.value) - urgency = ','.join(str(x.value) for x in Urgency) - raise QueryResponse(f'99;?;a={actions}:o={when}:u={urgency}') payload_is_encoded = False payload_type = 'title' if metadata: @@ -181,6 +176,15 @@ def parse_osc_99(raw: str) -> NotificationCommand: elif k == 'u': with suppress(Exception): cmd.urgency = Urgency(int(v)) + if payload_type == '?': + actions = ','.join(x.name for x in Action) + when = ','.join(x.name for x in OnlyWhen if x.value) + urgency = ','.join(str(x.value) for x in Urgency) + i = '' + if cmd.identifier: + i = f'i={sanitize_id(cmd.identifier)}:' + raise QueryResponse(f'99;{i}p=?;a={actions}:o={when}:u={urgency}') + if payload_type not in ('body', 'title'): log_error(f'Malformed OSC 99: unknown payload type: {payload_type}') return NotificationCommand() diff --git a/kitty_tests/parser.py b/kitty_tests/parser.py index b23a6fd0b..5d53bb346 100644 --- a/kitty_tests/parser.py +++ b/kitty_tests/parser.py @@ -588,9 +588,9 @@ def activated(identifier, window_id, focus, report): notification_activated(notifications[-1][-2], activated) self.ae(activations, [('0', 1, True, False)]) reset() - h('?') + h('i=xyz:p=?') self.assertFalse(notifications) - self.ae(query_responses, ['99;?;a=focus,report:o=always,unfocused,invisible:u=0,1,2']) + self.ae(query_responses, ['99;i=xyz:p=?;a=focus,report:o=always,unfocused,invisible:u=0,1,2']) def test_dcs_codes(self): s = self.create_screen()