From 9ee2d3ceee93ff0d0afdb47f594ea4fee95455fc Mon Sep 17 00:00:00 2001 From: Adam Malczewski Date: Sat, 13 Jun 2026 16:05:28 +0900 Subject: Kernel: Ctrl+Alt+F1..F12 VT switching (session escape hatch) Intercept the XF86Switch_VT_1..12 keysyms before the keybinding filter and call wlr_session_change_vt, so the user can always switch consoles while unbox runs. Clean no-op without a session (headless/nested). Pure vt_for_keysym helper + doctest; wlroots reached via the wlr.hpp wrapper. Real-seat verified on the CF-AX3. --- packages/kernel/src/input.cpp | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'packages/kernel/src/input.cpp') diff --git a/packages/kernel/src/input.cpp b/packages/kernel/src/input.cpp index 336947e..6056c53 100644 --- a/packages/kernel/src/input.cpp +++ b/packages/kernel/src/input.cpp @@ -1,5 +1,7 @@ #include "server_impl.hpp" +#include "vt_core.hpp" + #include namespace unbox::kernel { @@ -75,6 +77,23 @@ void Server::Impl::new_keyboard(wlr_input_device* device) { const std::uint32_t modifiers = wlr_keyboard_get_modifiers(keyboard->keyboard); const bool pressed = event->state == WL_KEYBOARD_KEY_STATE_PRESSED; + // SESSION ESCAPE HATCH: Ctrl+Alt+Fn (XF86Switch_VT_1..12) switches the + // Linux VT. Handled HERE, kernel-hardwired, BEFORE the key_filter, so no + // extension can intercept, consume, or block the only escape from the + // real DRM seat (user decision: not config-driven, not rebindable). On + // PRESS we change the VT; we CONSUME both press and release (the matching + // release carries the same keysym) so the filter never runs and the key + // never reaches the focused client. No session (headless/nested => + // session is NULL) is a clean no-op — never crash. + for (int i = 0; i < nsyms; ++i) { + if (const std::optional vt = vt_for_keysym(syms[i])) { + if (pressed && session != nullptr) { + wlr_session_change_vt(session, *vt); + } + return; // consume: no filter, no client forward (press or release) + } + } + // Thread each resolved keysym through the key_filter; a filter link may // CONSUME the key (set handled=true) — that is how extensions implement // compositor shortcuts. If any resolution is consumed, suppress the -- cgit v1.2.3