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/tests/test_kernel.cpp | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) (limited to 'packages/kernel/tests/test_kernel.cpp') diff --git a/packages/kernel/tests/test_kernel.cpp b/packages/kernel/tests/test_kernel.cpp index dbb5c02..912d35e 100644 --- a/packages/kernel/tests/test_kernel.cpp +++ b/packages/kernel/tests/test_kernel.cpp @@ -13,9 +13,12 @@ // state machine, implicit-grab ownership, hit-test geometry) are doctest-ed // directly, no wlroots. #include "../src/ui_core.hpp" +// The VT-switch escape hatch's pure core (keysym -> VT number), no wlroots. +#include "../src/vt_core.hpp" #include #include +#include #include #include #include @@ -348,6 +351,35 @@ TEST_CASE("substrate: a click over a ui surface is CONSUMED (no click-through)") unsetenv("UNBOX_UI_SUBSTRATE_FORCE_SHM"); } +// ============================================================================ +// VT-switch escape hatch — PURE CORE (no wlroots): keysym -> VT number. The +// glue (input.cpp) calls wlr_session_change_vt on a hit and consumes; this +// helper decides the hit. Ctrl+Alt+Fn arrives as XF86Switch_VT_1..12. +// ============================================================================ + +TEST_CASE("vt_for_keysym: maps the XF86Switch_VT range to 1..12") { + using unbox::kernel::vt_for_keysym; + + // Both endpoints of the range. + CHECK(vt_for_keysym(XKB_KEY_XF86Switch_VT_1) == 1U); + CHECK(vt_for_keysym(XKB_KEY_XF86Switch_VT_12) == 12U); + // A representative interior value. + CHECK(vt_for_keysym(XKB_KEY_XF86Switch_VT_2) == 2U); + CHECK(vt_for_keysym(XKB_KEY_XF86Switch_VT_7) == 7U); + + // Just outside the range on both sides => nullopt (no VT-switch). + CHECK(vt_for_keysym(XKB_KEY_XF86Switch_VT_1 - 1) == std::nullopt); + CHECK(vt_for_keysym(XKB_KEY_XF86Switch_VT_12 + 1) == std::nullopt); + + // Plain F1..F12 (no Ctrl+Alt) resolve to ordinary keysyms, NOT the + // XF86Switch_VT range — they must pass through untouched. + CHECK(vt_for_keysym(XKB_KEY_F1) == std::nullopt); + CHECK(vt_for_keysym(XKB_KEY_F12) == std::nullopt); + + // An unrelated keysym. + CHECK(vt_for_keysym(XKB_KEY_a) == std::nullopt); +} + // ============================================================================ // The typed bus — PURE CORE (strict; zero mocks of unbox modules, no wlroots // running). A test DisableSink stands in for the kernel's isolation registry. -- cgit v1.2.3