Skip to content

Commit

Permalink
desktop notification protocol: Allow applications sending notificatio…
Browse files Browse the repository at this point in the history
…ns to specify that the notification should only be displayed if the window is currently unfocused

Fixes #6755
  • Loading branch information
kovidgoyal committed Oct 25, 2023
1 parent 8c83284 commit 1937420
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 4 deletions.
2 changes: 2 additions & 0 deletions docs/changelog.rst
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ Detailed list of changes

- macOS: Fix a regression in the previous release that caused kitten @ ls to not report the environment variables for the default shell (:iss:`6749`)

- desktop notification protocol: Allow applications sending notifications to specify that the notification should only be displayed if the window is currently unfocused (:iss:`6755`)

0.30.1 [2023-10-05]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Expand Down
13 changes: 10 additions & 3 deletions docs/desktop-notifications.rst
Original file line number Diff line number Diff line change
Expand Up @@ -94,9 +94,9 @@ to display it based on what it does understand.
revisions.


======= ==================== ========= =================
======= ==================== ========== =================
Key Value Default Description
======= ==================== ========= =================
======= ==================== ========== =================
``a`` Comma separated list ``focus`` What action to perform when the
of ``report``, notification is clicked
``focus``, with
Expand All @@ -113,7 +113,14 @@ Key Value Default Description

``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.
======= ==================== ========= =================

``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``
``invisible`` means the window both is unfocused
and not visible to the user, for example, because it is in an inactive tab or
its OS window is not currently active.
``always`` is the default and always honors the request.
======= ==================== ========== =================


.. note::
Expand Down
27 changes: 26 additions & 1 deletion kitty/notify.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@
import re
from base64 import standard_b64decode
from collections import OrderedDict
from contextlib import suppress
from enum import Enum
from itertools import count
from typing import Callable, Dict, Optional

from .constants import is_macos, logo_png_file
from .fast_data_types import get_boss
from .fast_data_types import current_focused_os_window_id, get_boss
from .types import run_once
from .utils import get_custom_window_icon, log_error

Expand Down Expand Up @@ -67,13 +69,20 @@ def notify_implementation(title: str, body: str, identifier: str) -> None:
notify(title, body, identifier=identifier)


class OnlyWhen(Enum):
always = 'always'
unfocused = 'unfocused'
invisible = 'invisible'


class NotificationCommand:

done: bool = True
identifier: str = '0'
title: str = ''
body: str = ''
actions: str = ''
only_when: OnlyWhen = OnlyWhen.always

def __repr__(self) -> str:
return f'NotificationCommand(identifier={self.identifier!r}, title={self.title!r}, body={self.body!r}, actions={self.actions!r}, done={self.done!r})'
Expand Down Expand Up @@ -125,6 +134,9 @@ def parse_osc_99(raw: str) -> NotificationCommand:
cmd.done = v != '0'
elif k == 'a':
cmd.actions += f',{v}'
elif k == 'o':
with suppress(ValueError):
cmd.only_when = OnlyWhen(v)
if payload_type not in ('body', 'title'):
log_error(f'Malformed OSC 99: unknown payload type: {payload_type}')
return NotificationCommand()
Expand Down Expand Up @@ -208,6 +220,19 @@ def reset_registry() -> None:
def notify_with_command(cmd: NotificationCommand, window_id: int, notify_implementation: NotifyImplementation = notify_implementation) -> None:
title = cmd.title or cmd.body
body = cmd.body if cmd.title else ''
if not title:
return
if cmd.only_when is not OnlyWhen.always:
w = get_boss().window_id_map.get(window_id)
if w is None:
return
boss = get_boss()
window_has_keyboard_focus = w.is_active and w.os_window_id == current_focused_os_window_id()
if window_has_keyboard_focus:
return
if cmd.only_when is OnlyWhen.invisible:
if w.os_window_id == current_focused_os_window_id() and w.tabref() is boss.active_tab and w.is_visible_in_layout:
return # window is in the active OS window and the active tab and is visible in the tab layout
if title:
identifier = f'i{next(id_counter)}'
notify_implementation(title, body, identifier)
Expand Down

0 comments on commit 1937420

Please sign in to comment.