From a189c2f51b9c8fd24979d38dac06e8fee1dec078 Mon Sep 17 00:00:00 2001 From: Till Wegmueller Date: Sat, 4 Apr 2026 18:51:22 +0200 Subject: [PATCH] Add keyboard and pointer input handling via Smithay seat Initialize keyboard and pointer on the Wayland seat at startup, and route Winit input events (keyboard, pointer motion, button, scroll) through process_input_event to the Smithay seat so clients can receive input. Click-to-focus raises the clicked window and sets keyboard focus. --- crates/wrsrvd/src/main.rs | 4 +- crates/wrsrvd/src/state.rs | 134 +++++++++++++++++++++++++++++++++++-- 2 files changed, 132 insertions(+), 6 deletions(-) diff --git a/crates/wrsrvd/src/main.rs b/crates/wrsrvd/src/main.rs index 5a63215..e5f855e 100644 --- a/crates/wrsrvd/src/main.rs +++ b/crates/wrsrvd/src/main.rs @@ -128,8 +128,8 @@ fn main() -> Result<()> { WinitEvent::Focus(focused) => { info!(focused, "window focus changed"); } - WinitEvent::Input(_event) => { - // Input handling is Task 7 -- ignore for now. + WinitEvent::Input(event) => { + data.state.process_input_event(event); } WinitEvent::Redraw => { render::render_output_frame( diff --git a/crates/wrsrvd/src/state.rs b/crates/wrsrvd/src/state.rs index 3bb1596..ef3e2ec 100644 --- a/crates/wrsrvd/src/state.rs +++ b/crates/wrsrvd/src/state.rs @@ -1,9 +1,19 @@ use smithay::{ - desktop::Space, - input::{Seat, SeatState}, + backend::input::{ + AbsolutePositionEvent, Axis, AxisSource, ButtonState, Event as InputEventTrait, + InputBackend, InputEvent, KeyboardKeyEvent, + PointerAxisEvent as JsonPointerAxisEvent, + PointerButtonEvent as JsonPointerButtonEvent, + }, + desktop::{Space, WindowSurfaceType}, + input::{ + Seat, SeatState, + keyboard::FilterResult, + pointer::{AxisFrame, ButtonEvent, MotionEvent}, + }, output::Output, reexports::wayland_server::{Display, DisplayHandle}, - utils::{Clock, Monotonic}, + utils::{Clock, Monotonic, SERIAL_COUNTER}, wayland::{ compositor::CompositorState, output::OutputManagerState, @@ -43,7 +53,11 @@ impl WayRay { let data_device_state = DataDeviceState::new::(&dh); let mut seat_state = SeatState::new(); - let seat = seat_state.new_wl_seat(&dh, "wayray"); + let mut seat = seat_state.new_wl_seat(&dh, "wayray"); + + seat.add_keyboard(Default::default(), 200, 25) + .expect("failed to add keyboard to seat"); + seat.add_pointer(); let output_manager_state = OutputManagerState::new_with_xdg_output::(&dh); @@ -63,4 +77,116 @@ impl WayRay { output, } } + + /// Process an input event from the backend and forward it to the appropriate + /// Smithay seat device (keyboard or pointer). + pub fn process_input_event(&mut self, event: InputEvent) { + match event { + InputEvent::Keyboard { event } => { + let serial = SERIAL_COUNTER.next_serial(); + let keyboard = self.seat.get_keyboard().unwrap(); + keyboard.input::<(), _>( + self, + event.key_code(), + event.state(), + serial, + event.time_msec(), + |_, _, _| FilterResult::Forward, + ); + } + InputEvent::PointerMotionAbsolute { event } => { + let output_size = self.output.current_mode().unwrap().size; + let pos = event.position_transformed(output_size.to_logical(1)); + + let serial = SERIAL_COUNTER.next_serial(); + let pointer = self.seat.get_pointer().unwrap(); + + // Find the element (window) under the pointer and get its surface. + let under = self.space.element_under(pos).and_then(|(window, loc)| { + window + .surface_under(pos - loc.to_f64(), WindowSurfaceType::ALL) + .map(|(surface, surf_loc)| (surface, (surf_loc + loc).to_f64())) + }); + + pointer.motion( + self, + under, + &MotionEvent { + location: pos, + serial, + time: event.time_msec(), + }, + ); + pointer.frame(self); + } + InputEvent::PointerButton { event } => { + let serial = SERIAL_COUNTER.next_serial(); + let pointer = self.seat.get_pointer().unwrap(); + + // On button press, focus the window under the pointer. + if event.state() == ButtonState::Pressed { + let pos = pointer.current_location(); + if let Some((window, _loc)) = self.space.element_under(pos) { + let window = window.clone(); + self.space.raise_element(&window, true); + + let keyboard = self.seat.get_keyboard().unwrap(); + let wl_surface = window.toplevel().map(|t| t.wl_surface().clone()); + keyboard.set_focus(self, wl_surface, serial); + } + } + + pointer.button( + self, + &ButtonEvent { + serial, + time: event.time_msec(), + button: event.button_code(), + state: event.state(), + }, + ); + pointer.frame(self); + } + InputEvent::PointerAxis { event } => { + let pointer = self.seat.get_pointer().unwrap(); + + let source = event.source(); + + let horizontal_amount = event + .amount(Axis::Horizontal) + .unwrap_or_else(|| event.amount_v120(Axis::Horizontal).unwrap_or(0.0) * 3.0 / 120.0); + let vertical_amount = event + .amount(Axis::Vertical) + .unwrap_or_else(|| event.amount_v120(Axis::Vertical).unwrap_or(0.0) * 3.0 / 120.0); + + let mut frame = AxisFrame::new(event.time_msec()).source(source); + + if horizontal_amount != 0.0 { + frame = frame.value(Axis::Horizontal, horizontal_amount); + if let Some(v120) = event.amount_v120(Axis::Horizontal) { + frame = frame.v120(Axis::Horizontal, v120 as i32); + } + } + if vertical_amount != 0.0 { + frame = frame.value(Axis::Vertical, vertical_amount); + if let Some(v120) = event.amount_v120(Axis::Vertical) { + frame = frame.v120(Axis::Vertical, v120 as i32); + } + } + + if source == AxisSource::Finger { + if horizontal_amount == 0.0 { + frame = frame.stop(Axis::Horizontal); + } + if vertical_amount == 0.0 { + frame = frame.stop(Axis::Vertical); + } + } + + pointer.axis(self, frame); + pointer.frame(self); + } + _ => {} // Ignore other events + } + } }