diff --git a/kitty/boss.py b/kitty/boss.py index c1b7b4796..31ace3c23 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -1039,11 +1039,13 @@ class Boss: for w, val in changes.items(): w.ignore_focus_changes = val - def on_child_death(self, window_id: int) -> None: + def on_child_death(self, window_id: int, child_died: bool, exit_status: int) -> None: prev_active_window = self.active_window window = self.window_id_map.pop(window_id, None) if window is None: return + window.child_died, window.child_exit_status = child_died, exit_status + window.child_exit_code = os.waitstatus_to_exitcode(exit_status) with self.suppress_focus_change_events(): for close_action in window.actions_on_close: try: diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 14b6b6988..d69f2aee7 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -63,10 +63,11 @@ typedef struct { typedef struct { Screen *screen; - bool needs_removal; + bool needs_removal, child_died; int fd; unsigned long id; pid_t pid; + int exit_status; } Child; static const Child EMPTY_CHILD = {0}; @@ -564,7 +565,8 @@ parse_input(ChildMonitor *self) { // the python function could call into other functions in this module remove_count--; if (remove_notify[remove_count].screen) do_parse(self, remove_notify[remove_count].screen, now, true); - PyObject *t = PyObject_CallFunction(self->death_notify, "k", remove_notify[remove_count].id); + PyObject *t = PyObject_CallFunction( + self->death_notify, "kOi", remove_notify[remove_count].id, remove_notify[remove_count].child_died ? Py_True : Py_False, remove_notify[remove_count].exit_status); if (t == NULL) PyErr_Print(); else Py_DECREF(t); FREE_CHILD(remove_notify[remove_count]); @@ -1553,11 +1555,13 @@ handle_signal(const siginfo_t *siginfo, void *data) { } static void -mark_child_for_removal(ChildMonitor *self, pid_t pid) { +mark_child_for_removal(ChildMonitor *self, pid_t pid, int exit_status) { children_mutex(lock); for (size_t i = 0; i < self->count; i++) { if (children[i].pid == pid) { children[i].needs_removal = true; + children[i].exit_status = exit_status; + children[i].child_died = true; break; } } @@ -1589,7 +1593,7 @@ reap_children(ChildMonitor *self, bool enable_close_on_child_death) { if (pid == -1) { if (errno != EINTR) break; } else if (pid > 0) { - if (enable_close_on_child_death) mark_child_for_removal(self, pid); + if (enable_close_on_child_death) mark_child_for_removal(self, pid, status); mark_monitored_pids(pid, status); } else break; } diff --git a/kitty/fast_data_types.pyi b/kitty/fast_data_types.pyi index 656fe5cb4..d97cd5737 100644 --- a/kitty/fast_data_types.pyi +++ b/kitty/fast_data_types.pyi @@ -1444,7 +1444,7 @@ class ChildMonitor: def __init__( self, - death_notify: Callable[[int], None], + death_notify: Callable[[int, bool, int], None], dump_callback: Optional[Callable[[int, str, Any], None]], talk_fd: int = -1, listen_fd: int = -1, diff --git a/kitty/window.py b/kitty/window.py index 0b6e56956..097693b47 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -692,6 +692,9 @@ class Window: serialized_id: int = 0 show_title_bar: bool = False # must be set before calling set_geometry is_drag_target: bool = False # highlight this window's title bar as a drop target + child_died: bool = False + child_exit_status: int = 0 + child_exit_code: int = 0 @classmethod @contextmanager