From bf512cdd9bbe6e89369cbbcb5e6cd8bf88665fd9 Mon Sep 17 00:00:00 2001 From: Samuel Onoja Date: Thu, 24 Sep 2026 16:44:18 +0100 Subject: [PATCH] feat: support direct session vt switching --- compositor/src/backends/direct.rs | 10 +++++ compositor/src/input.rs | 61 +++++++++++++++++++++++++++++++ 2 files changed, 71 insertions(+) diff --git a/compositor/src/backends/direct.rs b/compositor/src/backends/direct.rs index 3f4af1e..d1dfbc4 100644 --- a/compositor/src/backends/direct.rs +++ b/compositor/src/backends/direct.rs @@ -273,6 +273,16 @@ pub fn render_all(state: &mut Ferese) { } } +pub fn switch_vt(state: &mut Ferese, vt: i32) { + let Some(backend) = state.direct_backend.as_mut() else { + return; + }; + + if let Err(error) = backend.session.change_vt(vt) { + tracing::error!(vt, %error, "failed to switch virtual terminal"); + } +} + fn render_device(state: &mut Ferese, node: DrmNode) { let Some(mut device) = state .direct_backend diff --git a/compositor/src/input.rs b/compositor/src/input.rs index 44fabde..9a5129d 100644 --- a/compositor/src/input.rs +++ b/compositor/src/input.rs @@ -35,6 +35,20 @@ impl Ferese { } let symbol = keysym.modified_sym().raw(); + if let Some(vt) = virtual_terminal( + symbol, + modifiers.ctrl, + modifiers.alt, + data.direct_backend.is_some(), + ) { + if state == KeyState::Pressed { + data.intercepted_keys.insert(keycode); + crate::backends::direct::switch_vt(data, vt); + } + + return FilterResult::Intercept(()); + } + let direction = if modifiers.logo && !modifiers.ctrl && !modifiers.alt { match symbol { keysyms::KEY_h | keysyms::KEY_H => Some(Direction::Left), @@ -344,3 +358,50 @@ impl Ferese { }); } } + +fn virtual_terminal(symbol: u32, ctrl: bool, alt: bool, direct: bool) -> Option { + if !direct { + return None; + } + + if (keysyms::KEY_XF86Switch_VT_1..=keysyms::KEY_XF86Switch_VT_12).contains(&symbol) { + return Some((symbol - keysyms::KEY_XF86Switch_VT_1 + 1) as i32); + } + + (ctrl && alt && (keysyms::KEY_F1..=keysyms::KEY_F12).contains(&symbol)) + .then_some((symbol - keysyms::KEY_F1 + 1) as i32) +} + +#[cfg(test)] +mod tests { + use super::virtual_terminal; + use smithay::input::keyboard::keysyms; + + #[test] + fn maps_xkb_virtual_terminal_symbols_for_direct_sessions() { + assert_eq!( + virtual_terminal(keysyms::KEY_XF86Switch_VT_1, false, false, true), + Some(1) + ); + assert_eq!( + virtual_terminal(keysyms::KEY_XF86Switch_VT_12, false, false, true), + Some(12) + ); + } + + #[test] + fn accepts_plain_function_symbols_only_with_control_alt() { + assert_eq!(virtual_terminal(keysyms::KEY_F7, true, true, true), Some(7)); + assert_eq!(virtual_terminal(keysyms::KEY_F7, true, false, true), None); + assert_eq!(virtual_terminal(keysyms::KEY_F7, false, true, true), None); + } + + #[test] + fn leaves_virtual_terminal_keys_to_nested_clients() { + assert_eq!( + virtual_terminal(keysyms::KEY_XF86Switch_VT_3, false, false, false), + None + ); + assert_eq!(virtual_terminal(keysyms::KEY_F3, true, true, false), None); + } +}