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.
This commit is contained in:
Till Wegmueller 2026-04-04 18:51:22 +02:00
parent cbfc6e95df
commit a189c2f51b
2 changed files with 132 additions and 6 deletions

View file

@ -128,8 +128,8 @@ fn main() -> Result<()> {
WinitEvent::Focus(focused) => { WinitEvent::Focus(focused) => {
info!(focused, "window focus changed"); info!(focused, "window focus changed");
} }
WinitEvent::Input(_event) => { WinitEvent::Input(event) => {
// Input handling is Task 7 -- ignore for now. data.state.process_input_event(event);
} }
WinitEvent::Redraw => { WinitEvent::Redraw => {
render::render_output_frame( render::render_output_frame(

View file

@ -1,9 +1,19 @@
use smithay::{ use smithay::{
desktop::Space, backend::input::{
input::{Seat, SeatState}, 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, output::Output,
reexports::wayland_server::{Display, DisplayHandle}, reexports::wayland_server::{Display, DisplayHandle},
utils::{Clock, Monotonic}, utils::{Clock, Monotonic, SERIAL_COUNTER},
wayland::{ wayland::{
compositor::CompositorState, compositor::CompositorState,
output::OutputManagerState, output::OutputManagerState,
@ -43,7 +53,11 @@ impl WayRay {
let data_device_state = DataDeviceState::new::<Self>(&dh); let data_device_state = DataDeviceState::new::<Self>(&dh);
let mut seat_state = SeatState::new(); 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::<Self>(&dh); let output_manager_state = OutputManagerState::new_with_xdg_output::<Self>(&dh);
@ -63,4 +77,116 @@ impl WayRay {
output, 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<B: InputBackend>(&mut self, event: InputEvent<B>) {
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
}
}
} }