mirror of
https://github.com/kovidgoyal/kitty
synced 2026-07-21 16:05:02 +02:00
Allow sending notification when a command finishes
When the option `notify_on_command_finish` is set, and a command takes more than `notify_on_command_finish_min_duration` seconds to execute, then when it finishes, send a desktop notification. The value of `notify_on_command_finish` can be: never | unfocused | invisible | always the latter three are interpreted the same way as in the `o=` option of the desktop notification protocol.
This commit is contained in:
@@ -3149,6 +3149,39 @@ first valid match, in the order specified, is sourced.
|
||||
'''
|
||||
)
|
||||
|
||||
opt('notify_on_cmd_finish', 'never',
|
||||
choices=('never', 'unfocused', 'invisible', 'always'),
|
||||
long_text='''
|
||||
Whether to send a desktop notification when a long-running command finishes
|
||||
(needs :opt:`shell_integration`).
|
||||
The possible values are:
|
||||
|
||||
:code:`never`
|
||||
Never send a notification.
|
||||
|
||||
:code:`unfocused`
|
||||
Only send a notification when the window does not have keyboard focus.
|
||||
|
||||
:code:`invisible`
|
||||
Only send a notification when 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.
|
||||
|
||||
:code:`always`
|
||||
Always send a notification, even if the window is focused.
|
||||
'''
|
||||
)
|
||||
|
||||
opt('notify_on_cmd_finish_min_duration', '5.0',
|
||||
option_type="float",
|
||||
long_text='''
|
||||
Only send a notification if a command takes more than specified seconds of time.
|
||||
It's not recommended to set a value too small, as you probably don't want a
|
||||
notification when a command launches a new window and exit immediately (e.g. the
|
||||
`code` command from vscode).
|
||||
'''
|
||||
)
|
||||
|
||||
opt('term', 'xterm-kitty',
|
||||
long_text='''
|
||||
The value of the :envvar:`TERM` environment variable to set. Changing this can
|
||||
|
||||
11
kitty/options/parse.py
generated
11
kitty/options/parse.py
generated
@@ -1120,6 +1120,17 @@ class Parser:
|
||||
for k, v in narrow_symbols(val):
|
||||
ans["narrow_symbols"][k] = v
|
||||
|
||||
def notify_on_cmd_finish(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
val = val.lower()
|
||||
if val not in self.choices_for_notify_on_cmd_finish:
|
||||
raise ValueError(f"The value {val} is not a valid choice for notify_on_cmd_finish")
|
||||
ans["notify_on_cmd_finish"] = val
|
||||
|
||||
choices_for_notify_on_cmd_finish = frozenset(('never', 'unfocused', 'invisible', 'always'))
|
||||
|
||||
def notify_on_cmd_finish_min_duration(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['notify_on_cmd_finish_min_duration'] = float(val)
|
||||
|
||||
def open_url_with(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['open_url_with'] = to_cmdline(val)
|
||||
|
||||
|
||||
5
kitty/options/types.py
generated
5
kitty/options/types.py
generated
@@ -20,6 +20,7 @@ choices_for_default_pointer_shape = typing.Literal['arrow', 'beam', 'text', 'poi
|
||||
choices_for_linux_display_server = typing.Literal['auto', 'wayland', 'x11']
|
||||
choices_for_macos_colorspace = typing.Literal['srgb', 'default', 'displayp3']
|
||||
choices_for_macos_show_window_title_in = typing.Literal['all', 'menubar', 'none', 'window']
|
||||
choices_for_notify_on_cmd_finish = typing.Literal['never', 'unfocused', 'invisible', 'always']
|
||||
choices_for_placement_strategy = typing.Literal['center', 'top-left']
|
||||
choices_for_pointer_shape_when_dragging = choices_for_default_pointer_shape
|
||||
choices_for_pointer_shape_when_grabbed = choices_for_default_pointer_shape
|
||||
@@ -386,6 +387,8 @@ option_names = ( # {{{
|
||||
'mouse_hide_wait',
|
||||
'mouse_map',
|
||||
'narrow_symbols',
|
||||
'notify_on_cmd_finish',
|
||||
'notify_on_cmd_finish_min_duration',
|
||||
'open_url_with',
|
||||
'paste_actions',
|
||||
'placement_strategy',
|
||||
@@ -545,6 +548,8 @@ class Options:
|
||||
mark3_background: Color = Color(242, 116, 188)
|
||||
mark3_foreground: Color = Color(0, 0, 0)
|
||||
mouse_hide_wait: float = 0.0 if is_macos else 3.0
|
||||
notify_on_cmd_finish: choices_for_notify_on_cmd_finish = 'never'
|
||||
notify_on_cmd_finish_min_duration: float = 5.0
|
||||
open_url_with: typing.List[str] = ['default']
|
||||
paste_actions: typing.FrozenSet[str] = frozenset({'confirm', 'quote-urls-at-prompt'})
|
||||
placement_strategy: choices_for_placement_strategy = 'center'
|
||||
|
||||
@@ -2216,9 +2216,12 @@ shell_prompt_marking(Screen *self, PyObject *data) {
|
||||
}
|
||||
if (PyErr_Occurred()) PyErr_Print();
|
||||
self->linebuf->line_attrs[self->cursor->y].prompt_kind = pk;
|
||||
if (pk == PROMPT_START)
|
||||
CALLBACK("cmd_output_marking", "i", 0);
|
||||
} break;
|
||||
case 'C':
|
||||
self->linebuf->line_attrs[self->cursor->y].prompt_kind = OUTPUT_START;
|
||||
CALLBACK("cmd_output_marking", "i", 1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -83,7 +83,9 @@ from .fast_data_types import (
|
||||
from .keys import keyboard_mode_name, mod_mask
|
||||
from .notify import (
|
||||
NotificationCommand,
|
||||
OnlyWhen,
|
||||
handle_notification_cmd,
|
||||
notify_with_command,
|
||||
sanitize_identifier_pat,
|
||||
)
|
||||
from .options.types import Options
|
||||
@@ -536,6 +538,7 @@ class Window:
|
||||
self.current_mouse_event_button = 0
|
||||
self.current_clipboard_read_ask: Optional[bool] = None
|
||||
self.prev_osc99_cmd = NotificationCommand()
|
||||
self.last_cmd_output_start_time = 0
|
||||
self.actions_on_close: List[Callable[['Window'], None]] = []
|
||||
self.actions_on_focus_change: List[Callable[['Window', bool], None]] = []
|
||||
self.actions_on_removal: List[Callable[['Window'], None]] = []
|
||||
@@ -1315,6 +1318,27 @@ class Window:
|
||||
else:
|
||||
if self.child_title:
|
||||
self.title_stack.append(self.child_title)
|
||||
|
||||
def cmd_output_marking(self, is_start: int) -> None:
|
||||
if is_start:
|
||||
self.last_cmd_output_start_time = monotonic()
|
||||
else:
|
||||
if self.last_cmd_output_start_time > 0:
|
||||
last_cmd_output_duration = monotonic() - self.last_cmd_output_start_time
|
||||
self.last_cmd_output_start_time = 0
|
||||
|
||||
opts = get_options()
|
||||
mode = opts.notify_on_cmd_finish
|
||||
if mode == 'never':
|
||||
return
|
||||
|
||||
if last_cmd_output_duration >= opts.notify_on_cmd_finish_min_duration:
|
||||
cmd = NotificationCommand()
|
||||
cmd.title = 'kitty'
|
||||
cmd.body = 'Command finished in a background window.\nClick to focus.'
|
||||
cmd.actions = 'focus'
|
||||
cmd.only_when = OnlyWhen(mode)
|
||||
notify_with_command(cmd, self.id)
|
||||
# }}}
|
||||
|
||||
# mouse actions {{{
|
||||
|
||||
Reference in New Issue
Block a user