From 498ee6db359d105263b8e782b53bc1b0b9e662f6 Mon Sep 17 00:00:00 2001 From: Andrew Hamilton Date: Tue, 29 Sep 2026 23:15:02 +1000 Subject: [PATCH] fix: simplify shutdown teardown, lock/inhibit ordering, small input gaps Shutdown/teardown now reuses remove_tab instead of an open-coded free loop, closing listener/thumb/MRU/timer gaps with less code; remove_tab also destroys foreign-toplevel handles, and shutdown removes the full standalone pointer listener set including gestures. Key handling: delete the session-locked early return that swallowed lock-screen typing (grabs stay swallowed by their chord handlers, passive keys now forward; overlay gated centrally), and move Ctrl+Alt+Q quit above the shortcuts inhibitor so VT switch and quit stay live under inhibition (pure reorder). Input: swallow pointer/touch during quit-confirm as well as overlay, reset idle on scroll in both axis paths, and guard close_timer against double-arm. Net fewer lines; all 176 smoke tests pass. --- kick.c | 103 +++++++++++++++++++++++++++------------------------------ 1 file changed, 48 insertions(+), 55 deletions(-) diff --git a/kick.c b/kick.c index 62d50a8..7f96bb4 100644 --- a/kick.c +++ b/kick.c @@ -1651,21 +1651,8 @@ static void shutdown_cleanup(void) { shutting_down = true; terminate_clients(); struct kick_tab *tab, *tab_tmp; - wl_list_for_each_safe(tab, tab_tmp, &server.tabs, link) { - struct kick_window *w, *w_tmp; - wl_list_for_each_safe(w, w_tmp, &tab->windows, link) { - if (w->icon_wlr) { - wlr_texture_destroy(w->icon_wlr); - w->icon_wlr = NULL; - } - icon_proto_release(w); - free(w->cmd); - free(w->tag); - free(w->tag_desc); - free(w); - } - free(tab); - } + wl_list_for_each_safe(tab, tab_tmp, &server.tabs, link) + remove_tab(tab); wl_list_init(&server.tabs); wl_list_init(&server.mru); server.mru_alt_anchor = NULL; @@ -1691,6 +1678,14 @@ static void shutdown_cleanup(void) { wl_list_remove(&pd->button.link); wl_list_remove(&pd->axis.link); wl_list_remove(&pd->frame.link); + wl_list_remove(&pd->swipe_begin.link); + wl_list_remove(&pd->swipe_update.link); + wl_list_remove(&pd->swipe_end.link); + wl_list_remove(&pd->pinch_begin.link); + wl_list_remove(&pd->pinch_update.link); + wl_list_remove(&pd->pinch_end.link); + wl_list_remove(&pd->hold_begin.link); + wl_list_remove(&pd->hold_end.link); wl_list_remove(&pd->destroy.link); free(pd); } @@ -4372,6 +4367,7 @@ static void remove_tab(struct kick_tab *tab) { wlr_texture_destroy(w->icon_wlr); thumb_release(w); icon_proto_release(w); + foreign_handle_destroy(w); if (w->toplevel) window_detach_xdg_listeners(w); #if HAVE_XWAYLAND @@ -4569,10 +4565,12 @@ static void close_window(struct kick_window *w) { kill_window(w, SIGTERM); kick_log(KICK_LOG_INFO, "kick: closing tab %d (pid %d)\n", died, (int)shown); - w->close_timer = wl_event_loop_add_timer(server.loop, close_window_escalate, - w); - if (w->close_timer) - wl_event_source_timer_update(w->close_timer, KICK_CLOSE_ESCALATE_MS); + if (!w->close_timer) { + w->close_timer = wl_event_loop_add_timer(server.loop, close_window_escalate, + w); + if (w->close_timer) + wl_event_source_timer_update(w->close_timer, KICK_CLOSE_ESCALATE_MS); + } } /* Alt+Delete on the active tab closes the active WINDOW, not the whole tab: @@ -10143,6 +10141,7 @@ static double overlay_wheel_acc = 0.0; static void update_overlay_visible(bool alt) { if (alt == host.overlay_visible) return; + if (server.session_locked) return; if (alt) { /* Prefix went down: snapshot the shown window. Every focus change from here until release is a preview -- window_enter leaves the @@ -10273,18 +10272,6 @@ static bool process_key_event(uint32_t time, uint32_t key, uint32_t state, } return true; } - /* Session locked: swallow every compositor grab chord so the user can't - cycle tabs, close windows or open terminals behind the lock screen - (passive keys still flow to the focused lock surface). The quit chord - and confirm modal above run first and remain available, as does the VT - switch below (a matching compositor session action). */ - if (server.session_locked) - return true; - /* A focused surface with an active shortcuts inhibitor (game/VM) grabs - the compositor's chords: swallow the tab/window grabs so they reach - the client. Quit and VT switch above/below stay live. */ - if (kb_inhibit_focused()) - return true; /* Ctrl+Alt+F1..F12 switch virtual terminals in standalone mode. wlroots has no built-in handler; the compositor must notice the key and ask the session (libseat/logind) to activate another VT. Both the press and @@ -10300,6 +10287,29 @@ static bool process_key_event(uint32_t time, uint32_t key, uint32_t state, } return true; } + /* Ctrl+Alt+Q quits kick (both the host close and this chord share + quit_requested): with more than one window open a modal asks before + anything is torn down. Above the inhibitor so quit stays live. */ + { bool is_q = server.xkb_state + ? (sym == XKB_KEY_q || sym == XKB_KEY_Q) + : key == 16; + if (alt && ctrl && is_q) { + kbd_repeat_cancel(); + if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if (!quit_key_held) { + quit_key_held = true; + quit_requested(); + } + } else { + quit_key_held = false; + } + return true; + } + } + /* Inhibited (game/VM grabs chords): swallow tab/window grabs below so + they reach the client. VT switch and quit above stay live. */ + if (kb_inhibit_focused()) + return true; /* Function keys are not grabbed (prefix+F11 above is the one exception): they pass through to the focused client, which may have its own bindings (e.g. a browser's F11 fullscreen request, which @@ -10429,27 +10439,6 @@ static bool process_key_event(uint32_t time, uint32_t key, uint32_t state, return true; } } - /* Ctrl+Alt+Q quits kick (both the host close and this chord share - quit_requested): with more than one window open a modal asks before - anything is torn down. Grabbed so the focused client never sees the - chord; a latch drops host-synthesized repeats so one hold can't fire - the request (or the confirm) more than once. */ - { bool is_q = server.xkb_state - ? (sym == XKB_KEY_q || sym == XKB_KEY_Q) - : key == 16; - if (alt && ctrl && is_q) { - kbd_repeat_cancel(); - if (state == WL_KEYBOARD_KEY_STATE_PRESSED) { - if (!quit_key_held) { - quit_key_held = true; - quit_requested(); - } - } else { - quit_key_held = false; - } - return true; - } - } /* prefix+Delete closes the active window (or the whole tab when it is the only window). Grabbed so the focused client never sees the key; a latch drops host-synthesized repeats so one hold can't fire it per repeat. @@ -11526,6 +11515,8 @@ static void standalone_ptr_button_event(struct wl_listener *listener, void *data static void standalone_ptr_axis_event(struct wl_listener *listener, void *data) { (void)listener; struct wlr_pointer_axis_event *ev = data; + if (server.idle_notifier && server.seat) + wlr_idle_notifier_v1_notify_activity(server.idle_notifier, server.seat); if (!server.seat) return; /* libinput scales discrete steps by WLR_POINTER_AXIS_DISCRETE_STEP (±120 per notch; the seat divides it back off for clients), while @@ -12009,7 +12000,7 @@ static bool overlay_preview_select(struct kick_window *w) { } static bool overlay_eats_pointer(void) { - return host.overlay_visible && !confirm_quit_pending && + return (host.overlay_visible || confirm_quit_pending) && !server.session_locked; } @@ -12067,12 +12058,12 @@ static void seat_touch_deliver(uint32_t time_msec, int32_t id, int action, if (!server.seat) return; if (server.idle_notifier) wlr_idle_notifier_v1_notify_activity(server.idle_notifier, server.seat); - /* While the Alt overlay is up, touches never reach clients: a tap on + /* While the Alt overlay or quit confirm is up, touches never reach clients: a tap on a row previews that window (single tap selects, drag motion re-targets the preview as the finger slides between titles), anything else is ignored. Checked before constraint clamping -- the overlay sits outside any constrained surface. */ - if (host.overlay_visible && !confirm_quit_pending && !server.session_locked) { + if ((host.overlay_visible || confirm_quit_pending) && !server.session_locked) { if (action == KICK_TOUCH_DOWN) { /* First-touch-wins: only the finger that claims the idle driver previews; concurrent fingers are swallowed silently @@ -12244,6 +12235,8 @@ static void host_ptr_axis(void *data, struct wl_pointer *p, uint32_t time, uint32_t axis, wl_fixed_t value) { (void)data; (void)p; + if (server.idle_notifier && server.seat) + wlr_idle_notifier_v1_notify_activity(server.idle_notifier, server.seat); if (!server.seat) return; int32_t discrete = axis < 2 ? host.axis_discrete[axis] : 0; host.axis_discrete[axis] = 0;