// SPDX-License-Identifier: LGPL-2.1-only // Copyright (C) 2026 Liberux Labs, S. L. //! The Wayland backend's input: `wl_keyboard`, `wl_pointer` and `wl_touch` turned into the events of [`crate::input::event`]. //! //! Each handler records what only later Wayland requests read (the serials a window move or resize, a selection, a popup grab and the cursor shape quote back to the compositor), resolves the `wl_surface` to a [`SurfaceFocus`], drops pointer and touch events on the secondary per-output surfaces, whose coordinates would hit-test the main surface's widgets, and forwards the rest to the runtime's entry points. No input logic lives here, so this file is the template for another platform's backend. use smithay_client_toolkit::seat::keyboard:: { KeyboardHandler, KeyEvent, Keysym, Modifiers, RawModifiers, RepeatInfo, }; use smithay_client_toolkit::seat::pointer:: { AxisScroll, PointerEvent, PointerEventKind, PointerHandler, BTN_BACK, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT, BTN_SIDE, }; use smithay_client_toolkit::seat::touch::TouchHandler; use smithay_client_toolkit::reexports::client:: { protocol:: { wl_keyboard::WlKeyboard, wl_pointer::{ self, WlPointer }, wl_surface::WlSurface, wl_touch::WlTouch, }, Connection, QueueHandle, }; use crate::app::App; use crate::event_loop::{ AppData, SurfaceFocus }; use crate::input::event::{ Axis, AxisSource, KeyInput, PointerButton, PointerInput, PointerKind, TouchInput, TouchPhase }; fn key_input( event: KeyEvent ) -> KeyInput { KeyInput { keysym: event.keysym, keycode: event.raw_code, text: event.utf8 } } fn pointer_button( code: u32 ) -> PointerButton { match code { BTN_LEFT => PointerButton::Left, BTN_RIGHT => PointerButton::Right, BTN_MIDDLE => PointerButton::Middle, BTN_SIDE | BTN_BACK => PointerButton::Back, _ => PointerButton::Other, } } /// High-resolution detents when the compositor sends them, the older whole ones otherwise. fn axis( scroll: AxisScroll ) -> Axis { let notches = if scroll.value120 != 0 { scroll.value120 as f32 / 120.0 } else { scroll.discrete as f32 }; Axis { distance: scroll.absolute, notches } } fn axis_source( source: Option ) -> AxisSource { match source { Some( wl_pointer::AxisSource::Wheel ) => AxisSource::Wheel, Some( wl_pointer::AxisSource::Finger | wl_pointer::AxisSource::Continuous ) => AxisSource::Continuous, _ => AxisSource::Other, } } impl KeyboardHandler for AppData { fn enter( &mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &WlKeyboard, surface: &WlSurface, _serial: u32, _raw: &[u32], _keysyms: &[Keysym], ) { let focus = self.focus_for_surface( surface ).unwrap_or( SurfaceFocus::Main ); self.keyboard_enter( focus ); } fn leave( &mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &WlKeyboard, _surface: &WlSurface, _serial: u32, ) { self.keyboard_leave(); } fn press_key( &mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &WlKeyboard, serial: u32, event: KeyEvent, ) { self.platform.last_input_serial = serial; self.key_press( key_input( event ) ); } fn release_key( &mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &WlKeyboard, _serial: u32, event: KeyEvent, ) { self.key_release( &key_input( event ) ); } fn update_modifiers( &mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &WlKeyboard, _serial: u32, modifiers: Modifiers, _raw_modifiers: RawModifiers, _layout: u32, ) { self.modifiers_changed( modifiers.ctrl, modifiers.shift, modifiers.alt ); } fn update_repeat_info( &mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &WlKeyboard, info: RepeatInfo, ) { match info { RepeatInfo::Repeat { rate, delay } => self.set_repeat_info( rate.get(), delay ), RepeatInfo::Disable => self.set_repeat_info( 0, 0 ), } } fn repeat_key( &mut self, _conn: &Connection, _qh: &QueueHandle, _keyboard: &WlKeyboard, _serial: u32, event: KeyEvent, ) { self.key_repeated( key_input( event ) ); } } impl PointerHandler for AppData { fn pointer_frame( &mut self, _conn: &Connection, _qh: &QueueHandle, _pointer: &WlPointer, events: &[PointerEvent], ) { for event in events { if self.is_secondary_surface( &event.surface ) { continue; } let focus = self.focus_for_surface( &event.surface ).unwrap_or( SurfaceFocus::Main ); let kind = match event.kind { PointerEventKind::Enter { serial } => { self.platform.last_pointer_enter_serial = serial; PointerKind::Enter } PointerEventKind::Leave { .. } => PointerKind::Leave, PointerEventKind::Motion { .. } => PointerKind::Motion, PointerEventKind::Press { button, serial, .. } => { let button = pointer_button( button ); if matches!( button, PointerButton::Left | PointerButton::Middle ) { self.platform.last_pointer_serial = serial; self.platform.last_input_serial = serial; } PointerKind::Press( button ) } PointerEventKind::Release { button, .. } => PointerKind::Release( pointer_button( button ) ), PointerEventKind::Axis { horizontal, vertical, source, .. } => PointerKind::Axis { horizontal: axis( horizontal ), vertical: axis( vertical ), source: axis_source( source ), }, }; self.pointer_input( &PointerInput { focus, position: event.position, kind } ); } } } impl TouchHandler for AppData { fn down( &mut self, _conn: &Connection, _qh: &QueueHandle, _touch: &WlTouch, serial: u32, time: u32, surface: WlSurface, id: i32, position: ( f64, f64 ), ) { self.platform.last_input_serial = serial; if self.is_secondary_surface( &surface ) { return; } let focus = self.focus_for_surface( &surface ).unwrap_or( SurfaceFocus::Main ); self.touch_input( &TouchInput { id, time, phase: TouchPhase::Down { focus, position } } ); } fn up( &mut self, _conn: &Connection, _qh: &QueueHandle, _touch: &WlTouch, _serial: u32, time: u32, id: i32, ) { self.touch_input( &TouchInput { id, time, phase: TouchPhase::Up } ); } fn motion( &mut self, _conn: &Connection, _qh: &QueueHandle, _touch: &WlTouch, time: u32, id: i32, position: ( f64, f64 ), ) { self.touch_input( &TouchInput { id, time, phase: TouchPhase::Motion { position } } ); } fn shape( &mut self, _conn: &Connection, _qh: &QueueHandle, _touch: &WlTouch, _id: i32, _major: f64, _minor: f64, ) {} fn orientation( &mut self, _conn: &Connection, _qh: &QueueHandle, _touch: &WlTouch, _id: i32, _orientation: f64, ) {} fn cancel( &mut self, _conn: &Connection, _qh: &QueueHandle, _touch: &WlTouch ) { self.reset_touch_state(); } } #[ cfg( test ) ] mod tests { use super::*; #[ test ] fn evdev_buttons_are_named() { assert_eq!( pointer_button( 0x110 ), PointerButton::Left ); assert_eq!( pointer_button( 0x111 ), PointerButton::Right ); assert_eq!( pointer_button( 0x112 ), PointerButton::Middle ); assert_eq!( pointer_button( 0x113 ), PointerButton::Back ); assert_eq!( pointer_button( 0x116 ), PointerButton::Back ); assert_eq!( pointer_button( 0x114 ), PointerButton::Other ); } #[ test ] fn high_resolution_detents_win() { let scroll = AxisScroll { absolute: 15.0, discrete: 1, value120: 60, ..AxisScroll::default() }; assert_eq!( axis( scroll ), Axis { distance: 15.0, notches: 0.5 } ); } #[ test ] fn whole_detents_from_an_older_compositor() { let scroll = AxisScroll { absolute: -30.0, discrete: -2, ..AxisScroll::default() }; assert_eq!( axis( scroll ), Axis { distance: -30.0, notches: -2.0 } ); } #[ test ] fn a_touchpad_scrolls_without_detents() { let scroll = AxisScroll { absolute: 3.5, ..AxisScroll::default() }; assert_eq!( axis( scroll ), Axis { distance: 3.5, notches: 0.0 } ); } #[ test ] fn axis_sources_are_named() { assert_eq!( axis_source( Some( wl_pointer::AxisSource::Wheel ) ), AxisSource::Wheel ); assert_eq!( axis_source( Some( wl_pointer::AxisSource::Finger ) ), AxisSource::Continuous ); assert_eq!( axis_source( Some( wl_pointer::AxisSource::Continuous ) ), AxisSource::Continuous ); assert_eq!( axis_source( Some( wl_pointer::AxisSource::WheelTilt ) ), AxisSource::Other ); assert_eq!( axis_source( None ), AxisSource::Other ); } #[ test ] fn a_key_event_keeps_its_code_and_text() { let event = KeyEvent { time: 7, raw_code: 30, keysym: Keysym::a, utf8: Some( "a".into() ) }; assert_eq!( key_input( event ), KeyInput { keysym: Keysym::a, keycode: 30, text: Some( "a".into() ) } ); } }