diff --git a/kitty/boss.py b/kitty/boss.py index aeb667c3e..53512001c 100644 --- a/kitty/boss.py +++ b/kitty/boss.py @@ -724,6 +724,9 @@ class Boss: text = '\n'.join(parse_uri_list(text)) w.paste(text) + def confirm_os_window_close(self, os_window_id: int) -> None: + mark_os_window_for_close(os_window_id) + def on_os_window_closed(self, os_window_id: int, viewport_width: int, viewport_height: int) -> None: self.cached_values['window-size'] = viewport_width, viewport_height tm = self.os_window_map.pop(os_window_id, None) diff --git a/kitty/child-monitor.c b/kitty/child-monitor.c index 043b73706..d08f01fde 100644 --- a/kitty/child-monitor.c +++ b/kitty/child-monitor.c @@ -865,25 +865,42 @@ process_pending_resizes(monotonic_t now) { static inline void close_all_windows(void) { - for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], true); + for (size_t w = 0; w < global_state.num_os_windows; w++) mark_os_window_for_close(&global_state.os_windows[w], IMPERATIVE_CLOSE_REQUESTED); +} + +static inline void +close_os_window(ChildMonitor *self, OSWindow *os_window) { + destroy_os_window(os_window); + call_boss(on_os_window_closed, "Kii", os_window->id, os_window->window_width, os_window->window_height); + for (size_t t=0; t < os_window->num_tabs; t++) { + Tab *tab = os_window->tabs + t; + for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id); + } + remove_os_window(os_window->id); } static inline bool process_pending_closes(ChildMonitor *self) { - global_state.has_pending_closes = false; bool has_open_windows = false; for (size_t w = global_state.num_os_windows; w > 0; w--) { OSWindow *os_window = global_state.os_windows + w - 1; - if (should_os_window_close(os_window)) { - destroy_os_window(os_window); - call_boss(on_os_window_closed, "Kii", os_window->id, os_window->window_width, os_window->window_height); - for (size_t t=0; t < os_window->num_tabs; t++) { - Tab *tab = os_window->tabs + t; - for (size_t w = 0; w < tab->num_windows; w++) mark_child_for_close(self, tab->windows[w].id); - } - remove_os_window(os_window->id); - } else has_open_windows = true; + switch(os_window->close_request) { + case NO_CLOSE_REQUESTED: + has_open_windows = true; + break; + case CONFIRMABLE_CLOSE_REQUESTED: + os_window->close_request = NO_CLOSE_REQUESTED; + call_boss(confirm_os_window_close, "K", os_window->id); + if (os_window->close_request == IMPERATIVE_CLOSE_REQUESTED) { + close_os_window(self, os_window); + } else has_open_windows = true; + break; + case IMPERATIVE_CLOSE_REQUESTED: + close_os_window(self, os_window); + break; + } } + global_state.has_pending_closes = false; #ifdef __APPLE__ if (!OPT(macos_quit_when_last_window_closed)) { if (!has_open_windows && !application_quit_requested()) has_open_windows = true; diff --git a/kitty/glfw.c b/kitty/glfw.c index 5ac8e33ea..6345b2535 100644 --- a/kitty/glfw.c +++ b/kitty/glfw.c @@ -153,6 +153,10 @@ static void window_close_callback(GLFWwindow* window) { if (!set_callback_window(window)) return; global_state.has_pending_closes = true; + if (global_state.callback_os_window->close_request < CONFIRMABLE_CLOSE_REQUESTED) { + global_state.callback_os_window->close_request = CONFIRMABLE_CLOSE_REQUESTED; + } + glfwSetWindowShouldClose(window, false); request_tick_callback(); global_state.callback_os_window = NULL; } @@ -973,12 +977,6 @@ wakeup_main_loop() { glfwPostEmptyEvent(); } -void -mark_os_window_for_close(OSWindow* w, bool yes) { - global_state.has_pending_closes = true; - glfwSetWindowShouldClose(w->handle, yes); -} - bool should_os_window_be_rendered(OSWindow* w) { return ( @@ -988,11 +986,6 @@ should_os_window_be_rendered(OSWindow* w) { ) ? false : true; } -bool -should_os_window_close(OSWindow* w) { - return glfwWindowShouldClose(w->handle) ? true : false; -} - static PyObject* primary_monitor_size(PYNOARG) { GLFWmonitor* monitor = glfwGetPrimaryMonitor(); diff --git a/kitty/state.c b/kitty/state.c index f73781217..b6ba48923 100644 --- a/kitty/state.c +++ b/kitty/state.c @@ -423,6 +423,14 @@ os_window_regions(OSWindow *os_window, Region *central, Region *tab_bar) { } } +void +mark_os_window_for_close(OSWindow* w, CloseRequest cr) { + global_state.has_pending_closes = true; + w->close_request = cr; +} + + + // Python API {{{ #define PYWRAP0(name) static PyObject* py##name(PYNOARG) @@ -784,10 +792,10 @@ PYWRAP1(os_window_has_background_image) { PYWRAP1(mark_os_window_for_close) { id_type os_window_id; - int yes = 1; - PA("K|p", &os_window_id, &yes); + CloseRequest cr = IMPERATIVE_CLOSE_REQUESTED; + PA("K|i", &os_window_id, &cr); WITH_OS_WINDOW(os_window_id) - mark_os_window_for_close(os_window, yes ? true : false); + mark_os_window_for_close(os_window, cr); Py_RETURN_TRUE; END_WITH_OS_WINDOW Py_RETURN_FALSE; diff --git a/kitty/state.h b/kitty/state.h index 33d0b9672..af4dfe81b 100644 --- a/kitty/state.h +++ b/kitty/state.h @@ -139,6 +139,7 @@ typedef struct { } OSWindowGeometry; enum RENDER_STATE { RENDER_FRAME_NOT_REQUESTED, RENDER_FRAME_REQUESTED, RENDER_FRAME_READY }; +typedef enum { NO_CLOSE_REQUESTED, CONFIRMABLE_CLOSE_REQUESTED, IMPERATIVE_CLOSE_REQUESTED } CloseRequest; typedef struct { monotonic_t last_resize_event_at; @@ -183,6 +184,7 @@ typedef struct { uint64_t render_calls; id_type last_focused_counter; ssize_t gvao_idx; + CloseRequest close_request; } OSWindow; @@ -220,9 +222,8 @@ void remove_vao(ssize_t vao_idx); bool remove_os_window(id_type os_window_id); void make_os_window_context_current(OSWindow *w); void update_os_window_references(void); -void mark_os_window_for_close(OSWindow* w, bool yes); +void mark_os_window_for_close(OSWindow* w, CloseRequest cr); void update_os_window_viewport(OSWindow *window, bool); -bool should_os_window_close(OSWindow* w); bool should_os_window_be_rendered(OSWindow* w); void wakeup_main_loop(void); void swap_window_buffers(OSWindow *w);