From ff0421802358eac78e06d7931cfbd871fd847120 Mon Sep 17 00:00:00 2001 From: kossLAN Date: Fri, 29 May 2026 20:44:28 -0400 Subject: [PATCH] wl-seat: split device and object handlers --- src/ifs/wl_seat.rs | 354 +----------------------------- src/ifs/wl_seat/device_handler.rs | 151 +++++++++++++ src/ifs/wl_seat/position_hint.rs | 55 +++++ src/ifs/wl_seat/seat_object.rs | 143 ++++++++++++ 4 files changed, 357 insertions(+), 346 deletions(-) create mode 100644 src/ifs/wl_seat/device_handler.rs create mode 100644 src/ifs/wl_seat/position_hint.rs create mode 100644 src/ifs/wl_seat/seat_object.rs diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index 2ba1fb02..1a02f4d3 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -1,9 +1,12 @@ mod event_handling; +mod device_handler; pub mod ext_transient_seat_manager_v1; pub mod ext_transient_seat_v1; mod gesture_owner; mod kb_owner; mod pointer_owner; +mod position_hint; +mod seat_object; pub mod tablet; pub mod text_input; mod touch_owner; @@ -107,7 +110,6 @@ use { }, wire_ei::EiSeatId, }, - CursorPositionType::Warp, ahash::AHashMap, jay_config::{ input::FallbackOutputMode as ConfigFallbackOutputMode, @@ -128,7 +130,12 @@ use { }; pub use { event_handling::NodeSeatState, + position_hint::{ + CursorPositionType, PositionHintRequest, handle_position_hint_requests, + handle_warp_mouse_to_focus, + }, pointer_owner::{ToplevelSelector, WorkspaceSelector}, + seat_object::WlSeatError, }; pub const POINTER: u32 = 1; @@ -1782,147 +1789,6 @@ pub struct WlSeat { tracker: Tracker, } -const READ_ONLY_KEYMAP_SINCE: Version = Version(7); - -impl WlSeat { - fn send_capabilities(self: &Rc) { - self.client.event(Capabilities { - self_id: self.id, - capabilities: self.global.capabilities.get(), - }) - } - - fn send_name(self: &Rc, name: &str) { - self.client.event(Name { - self_id: self.id, - name, - }) - } - - pub fn keymap_fd(&self, state: &KeyboardState) -> Result { - let fd = match self.client.is_xwayland { - true => &state.map.xwayland_map, - _ => &state.map.map, - }; - if self.version >= READ_ONLY_KEYMAP_SINCE { - return Ok(fd.clone()); - } - Ok(fd.create_unprotected_fd()?) - } -} - -impl WlSeatRequestHandler for WlSeat { - type Error = WlSeatError; - - fn get_pointer(&self, req: GetPointer, slf: &Rc) -> Result<(), Self::Error> { - let p = Rc::new(WlPointer::new(req.id, slf)); - track!(self.client, p); - self.client.add_client_obj(&p)?; - self.pointers.set(req.id, p.clone()); - let surface = self - .global - .pointer_node() - .and_then(|n| n.node_into_surface()); - if let Some(surface) = surface - && surface.client.id == self.client.id - { - let (x, y) = self.global.pointer_cursor.position(); - let (x_int, y_int) = surface - .buffer_abs_pos - .get() - .translate(x.round_down(), y.round_down()); - p.send_enter( - self.client.next_serial(), - surface.id, - x.apply_fract(x_int), - y.apply_fract(y_int), - ); - } - Ok(()) - } - - fn get_keyboard(&self, req: GetKeyboard, slf: &Rc) -> Result<(), Self::Error> { - let p = Rc::new(WlKeyboard::new(req.id, slf)); - track!(self.client, p); - self.client.add_client_obj(&p)?; - self.keyboards.set(req.id, p.clone()); - if let Some(surface) = self.global.keyboard_node.get().node_into_surface() - && surface.client.id == self.client.id - { - p.enter( - self.client.next_serial(), - surface.id, - &self.global.seat_kb_state.get().borrow().kb_state, - ); - } - if self.version >= REPEAT_INFO_SINCE { - let (rate, delay) = self.global.repeat_rate.get(); - p.send_repeat_info(rate, delay); - } - Ok(()) - } - - fn get_touch(&self, req: GetTouch, slf: &Rc) -> Result<(), Self::Error> { - let p = Rc::new(WlTouch::new(req.id, slf)); - track!(self.client, p); - self.client.add_client_obj(&p)?; - self.touches.set(req.id, p); - Ok(()) - } - - fn release(&self, _req: Release, _slf: &Rc) -> Result<(), Self::Error> { - { - let mut bindings = self.global.bindings.borrow_mut(); - if let Entry::Occupied(mut hm) = bindings.entry(self.client.id) { - hm.get_mut().remove(&self.id); - if hm.get().is_empty() { - hm.remove(); - } - } - } - self.client.remove_obj(self)?; - Ok(()) - } -} - -object_base! { - self = WlSeat; - version = self.version; -} - -impl Object for WlSeat { - fn break_loops(&self) { - { - let mut bindings = self.global.bindings.borrow_mut(); - if let Entry::Occupied(mut hm) = bindings.entry(self.client.id) { - hm.get_mut().remove(&self.id); - if hm.get().is_empty() { - hm.remove(); - } - } - } - self.pointers.clear(); - self.relative_pointers.clear(); - self.keyboards.clear(); - self.touches.clear(); - } -} - -dedicated_add_obj!(WlSeat, WlSeatId, seats); - -#[derive(Debug, Error)] -pub enum WlSeatError { - #[error(transparent)] - ClientError(Box), - #[error(transparent)] - TransferError(#[from] TransferError), - #[error(transparent)] - WlKeyboardError(Box), - #[error("Data source has a toplevel attached")] - OfferHasDrag, -} -efrom!(WlSeatError, ClientError); -efrom!(WlSeatError, WlKeyboardError); pub fn collect_kb_foci2(node: Rc, seats: &mut SmallVec<[Rc; 3]>) { node.node_visit(&mut generic_node_visitor(|node| { @@ -1935,207 +1801,3 @@ pub fn collect_kb_foci(node: Rc) -> SmallVec<[Rc; 3]> { collect_kb_foci2(node, &mut res); res } - -impl DeviceHandlerData { - pub fn set_seat(&self, _state: &State, seat: Option>) { - if let Some(new) = &seat { - if let Some(old) = self.seat.get() - && old.id() == new.id() - { - return; - } - } else { - if self.seat.is_none() { - return; - } - } - self.destroy_physical_keyboard_state(); - let old = self.seat.set(seat.clone()); - if let Some(old) = old { - if let Some(info) = &self.tablet_init { - old.tablet_remove_tablet(info.id); - } - if let Some(info) = &self.tablet_pad_init { - old.tablet_remove_tablet_pad(info.id); - } - if self.is_touch { - old.num_touch_devices.fetch_sub(1); - old.update_capabilities(); - } - } - if let Some(seat) = &seat { - if let Some(info) = &self.tablet_init { - seat.tablet_add_tablet(self.device.id(), info); - } - if let Some(info) = &self.tablet_pad_init { - seat.tablet_add_tablet_pad(self.device.id(), info); - } - if self.is_touch { - seat.num_touch_devices.fetch_add(1); - seat.update_capabilities(); - } - } - self.attach_event_listeners(); - } - - fn destroy_physical_keyboard_state(&self) { - self.mods_listener.detach(); - if let Some(seat) = self.seat.get() { - seat.destroy_physical_keyboard(self.keyboard_id); - }; - } - - fn attach_event_listeners(&self) { - if self.is_kb - && let Some(seat) = self.seat.get() - { - seat.attach_modifiers_listener( - self.keyboard_id, - &self.mods_listener, - self.keymap.get().as_ref(), - ); - }; - } - - pub fn set_keymap(&self, _state: &State, keymap: Option>) { - self.destroy_physical_keyboard_state(); - self.keymap.set(keymap); - self.attach_event_listeners(); - } - - pub fn set_output(&self, _state: &State, output: Option<&WlOutputGlobal>) { - match output { - None => { - log::info!("Removing output mapping of {}", self.device.name()); - self.output.take(); - } - Some(o) => { - log::info!("Mapping {} to {}", self.device.name(), o.connector.name); - self.output.set(Some(o.opt.clone())); - } - } - } - - pub fn get_rect(&self, state: &State) -> Rect { - if let Some(output) = self.output.get() - && let Some(output) = output.get() - { - return output.pos.get(); - } - state.root.extents.get() - } - - pub fn set_accel_profile(&self, _state: &State, v: InputDeviceAccelProfile) { - self.device.set_accel_profile(v); - } - - pub fn set_accel_speed(&self, _state: &State, v: f64) { - self.device.set_accel_speed(v); - } - - pub fn set_tap_enabled(&self, _state: &State, v: bool) { - self.device.set_tap_enabled(v); - } - - pub fn set_drag_enabled(&self, _state: &State, v: bool) { - self.device.set_drag_enabled(v); - } - - pub fn set_drag_lock_enabled(&self, _state: &State, v: bool) { - self.device.set_drag_lock_enabled(v); - } - - pub fn set_left_handed(&self, _state: &State, v: bool) { - self.device.set_left_handed(v); - } - - pub fn set_natural_scrolling_enabled(&self, _state: &State, v: bool) { - self.device.set_natural_scrolling_enabled(v); - } - - pub fn set_px_per_scroll_wheel(&self, _state: &State, v: f64) { - self.px_per_scroll_wheel.set(v); - } - - pub fn set_transform_matrix(&self, _state: &State, v: TransformMatrix) { - self.device.set_transform_matrix(v); - } - - pub fn set_calibration_matrix(&self, _state: &State, v: [[f32; 3]; 2]) { - self.device.set_calibration_matrix(v); - } - - pub fn set_click_method(&self, _state: &State, v: InputDeviceClickMethod) { - self.device.set_click_method(v); - } - - pub fn set_middle_button_emulation_enabled(&self, _state: &State, v: bool) { - self.device.set_middle_button_emulation_enabled(v); - } -} - -impl LedsListener for DeviceHandlerData { - fn leds(&self, leds: Leds) { - self.device.set_enabled_leds(leds); - } -} - -impl LedsListener for WlSeatGlobal { - fn leds(&self, leds: Leds) { - self.dispatch_seat_leds_listeners(leds) - } -} - -pub struct PositionHintRequest { - seat: Rc, - client_id: ClientId, - old_pos: (Fixed, Fixed), - new_pos: (Fixed, Fixed), -} - -pub async fn handle_position_hint_requests(state: Rc) { - loop { - let req = state.position_hint_requests.pop().await; - let (x, y) = (req.new_pos.0.round_down(), req.new_pos.1.round_down()); - if state.node_at(x, y).node.node_client_id() != Some(req.client_id) { - continue; - } - let current_pos = req.seat.pointer_cursor.position(); - let (x, y) = ( - req.new_pos.0 + (current_pos.0 - req.old_pos.0), - req.new_pos.1 + (current_pos.1 - req.old_pos.1), - ); - req.seat.motion_event_abs(state.now_usec(), x, y, Warp); - } -} - -pub async fn handle_warp_mouse_to_focus(state: Rc) { - loop { - state.pending_warp_mouse_to_focus.non_empty().await; - state.eng.yield_now().await; - while let Some(seat) = state.pending_warp_mouse_to_focus.try_pop() { - seat.warp_mouse_to_focus_scheduled.set(false); - let skip_target_check = seat.warp_mouse_to_focus_skip_target_check.take(); - let Some(tl) = seat.keyboard_node.get().node_toplevel() else { - continue; - }; - let (x, y) = tl.node_absolute_position().center(); - if !skip_target_check { - let Some(target) = state.node_at(x, y).node.node_toplevel() else { - continue; - }; - if target.node_id() != tl.node_id() { - continue; - } - } - let (x, y) = (Fixed::from_int(x), Fixed::from_int(y)); - seat.motion_event_abs(state.now_usec(), x, y, Warp); - } - } -} - -#[derive(Copy, Clone, Debug, Eq, PartialEq)] -pub enum CursorPositionType { - Motion, - Warp, -} diff --git a/src/ifs/wl_seat/device_handler.rs b/src/ifs/wl_seat/device_handler.rs new file mode 100644 index 00000000..7bb01f5d --- /dev/null +++ b/src/ifs/wl_seat/device_handler.rs @@ -0,0 +1,151 @@ +use super::*; + +impl DeviceHandlerData { + pub fn set_seat(&self, _state: &State, seat: Option>) { + if let Some(new) = &seat { + if let Some(old) = self.seat.get() + && old.id() == new.id() + { + return; + } + } else { + if self.seat.is_none() { + return; + } + } + self.destroy_physical_keyboard_state(); + let old = self.seat.set(seat.clone()); + if let Some(old) = old { + if let Some(info) = &self.tablet_init { + old.tablet_remove_tablet(info.id); + } + if let Some(info) = &self.tablet_pad_init { + old.tablet_remove_tablet_pad(info.id); + } + if self.is_touch { + old.num_touch_devices.fetch_sub(1); + old.update_capabilities(); + } + } + if let Some(seat) = &seat { + if let Some(info) = &self.tablet_init { + seat.tablet_add_tablet(self.device.id(), info); + } + if let Some(info) = &self.tablet_pad_init { + seat.tablet_add_tablet_pad(self.device.id(), info); + } + if self.is_touch { + seat.num_touch_devices.fetch_add(1); + seat.update_capabilities(); + } + } + self.attach_event_listeners(); + } + + fn destroy_physical_keyboard_state(&self) { + self.mods_listener.detach(); + if let Some(seat) = self.seat.get() { + seat.destroy_physical_keyboard(self.keyboard_id); + }; + } + + fn attach_event_listeners(&self) { + if self.is_kb + && let Some(seat) = self.seat.get() + { + seat.attach_modifiers_listener( + self.keyboard_id, + &self.mods_listener, + self.keymap.get().as_ref(), + ); + }; + } + + pub fn set_keymap(&self, _state: &State, keymap: Option>) { + self.destroy_physical_keyboard_state(); + self.keymap.set(keymap); + self.attach_event_listeners(); + } + + pub fn set_output(&self, _state: &State, output: Option<&WlOutputGlobal>) { + match output { + None => { + log::info!("Removing output mapping of {}", self.device.name()); + self.output.take(); + } + Some(o) => { + log::info!("Mapping {} to {}", self.device.name(), o.connector.name); + self.output.set(Some(o.opt.clone())); + } + } + } + + pub fn get_rect(&self, state: &State) -> Rect { + if let Some(output) = self.output.get() + && let Some(output) = output.get() + { + return output.pos.get(); + } + state.root.extents.get() + } + + pub fn set_accel_profile(&self, _state: &State, v: InputDeviceAccelProfile) { + self.device.set_accel_profile(v); + } + + pub fn set_accel_speed(&self, _state: &State, v: f64) { + self.device.set_accel_speed(v); + } + + pub fn set_tap_enabled(&self, _state: &State, v: bool) { + self.device.set_tap_enabled(v); + } + + pub fn set_drag_enabled(&self, _state: &State, v: bool) { + self.device.set_drag_enabled(v); + } + + pub fn set_drag_lock_enabled(&self, _state: &State, v: bool) { + self.device.set_drag_lock_enabled(v); + } + + pub fn set_left_handed(&self, _state: &State, v: bool) { + self.device.set_left_handed(v); + } + + pub fn set_natural_scrolling_enabled(&self, _state: &State, v: bool) { + self.device.set_natural_scrolling_enabled(v); + } + + pub fn set_px_per_scroll_wheel(&self, _state: &State, v: f64) { + self.px_per_scroll_wheel.set(v); + } + + pub fn set_transform_matrix(&self, _state: &State, v: TransformMatrix) { + self.device.set_transform_matrix(v); + } + + pub fn set_calibration_matrix(&self, _state: &State, v: [[f32; 3]; 2]) { + self.device.set_calibration_matrix(v); + } + + pub fn set_click_method(&self, _state: &State, v: InputDeviceClickMethod) { + self.device.set_click_method(v); + } + + pub fn set_middle_button_emulation_enabled(&self, _state: &State, v: bool) { + self.device.set_middle_button_emulation_enabled(v); + } +} + +impl LedsListener for DeviceHandlerData { + fn leds(&self, leds: Leds) { + self.device.set_enabled_leds(leds); + } +} + +impl LedsListener for WlSeatGlobal { + fn leds(&self, leds: Leds) { + self.dispatch_seat_leds_listeners(leds) + } +} diff --git a/src/ifs/wl_seat/position_hint.rs b/src/ifs/wl_seat/position_hint.rs new file mode 100644 index 00000000..17b74454 --- /dev/null +++ b/src/ifs/wl_seat/position_hint.rs @@ -0,0 +1,55 @@ +use {super::*, CursorPositionType::Warp}; + +pub struct PositionHintRequest { + pub(super) seat: Rc, + pub(super) client_id: ClientId, + pub(super) old_pos: (Fixed, Fixed), + pub(super) new_pos: (Fixed, Fixed), +} + +pub async fn handle_position_hint_requests(state: Rc) { + loop { + let req = state.position_hint_requests.pop().await; + let (x, y) = (req.new_pos.0.round_down(), req.new_pos.1.round_down()); + if state.node_at(x, y).node.node_client_id() != Some(req.client_id) { + continue; + } + let current_pos = req.seat.pointer_cursor.position(); + let (x, y) = ( + req.new_pos.0 + (current_pos.0 - req.old_pos.0), + req.new_pos.1 + (current_pos.1 - req.old_pos.1), + ); + req.seat.motion_event_abs(state.now_usec(), x, y, Warp); + } +} + +pub async fn handle_warp_mouse_to_focus(state: Rc) { + loop { + state.pending_warp_mouse_to_focus.non_empty().await; + state.eng.yield_now().await; + while let Some(seat) = state.pending_warp_mouse_to_focus.try_pop() { + seat.warp_mouse_to_focus_scheduled.set(false); + let skip_target_check = seat.warp_mouse_to_focus_skip_target_check.take(); + let Some(tl) = seat.keyboard_node.get().node_toplevel() else { + continue; + }; + let (x, y) = tl.node_absolute_position().center(); + if !skip_target_check { + let Some(target) = state.node_at(x, y).node.node_toplevel() else { + continue; + }; + if target.node_id() != tl.node_id() { + continue; + } + } + let (x, y) = (Fixed::from_int(x), Fixed::from_int(y)); + seat.motion_event_abs(state.now_usec(), x, y, Warp); + } + } +} + +#[derive(Copy, Clone, Debug, Eq, PartialEq)] +pub enum CursorPositionType { + Motion, + Warp, +} diff --git a/src/ifs/wl_seat/seat_object.rs b/src/ifs/wl_seat/seat_object.rs new file mode 100644 index 00000000..4c39bc53 --- /dev/null +++ b/src/ifs/wl_seat/seat_object.rs @@ -0,0 +1,143 @@ +use super::*; + +const READ_ONLY_KEYMAP_SINCE: Version = Version(7); + +impl WlSeat { + pub(super) fn send_capabilities(self: &Rc) { + self.client.event(Capabilities { + self_id: self.id, + capabilities: self.global.capabilities.get(), + }) + } + + pub(super) fn send_name(self: &Rc, name: &str) { + self.client.event(Name { + self_id: self.id, + name, + }) + } + + pub fn keymap_fd(&self, state: &KeyboardState) -> Result { + let fd = match self.client.is_xwayland { + true => &state.map.xwayland_map, + _ => &state.map.map, + }; + if self.version >= READ_ONLY_KEYMAP_SINCE { + return Ok(fd.clone()); + } + Ok(fd.create_unprotected_fd()?) + } +} + +impl WlSeatRequestHandler for WlSeat { + type Error = WlSeatError; + + fn get_pointer(&self, req: GetPointer, slf: &Rc) -> Result<(), Self::Error> { + let p = Rc::new(WlPointer::new(req.id, slf)); + track!(self.client, p); + self.client.add_client_obj(&p)?; + self.pointers.set(req.id, p.clone()); + let surface = self + .global + .pointer_node() + .and_then(|n| n.node_into_surface()); + if let Some(surface) = surface + && surface.client.id == self.client.id + { + let (x, y) = self.global.pointer_cursor.position(); + let (x_int, y_int) = surface + .buffer_abs_pos + .get() + .translate(x.round_down(), y.round_down()); + p.send_enter( + self.client.next_serial(), + surface.id, + x.apply_fract(x_int), + y.apply_fract(y_int), + ); + } + Ok(()) + } + + fn get_keyboard(&self, req: GetKeyboard, slf: &Rc) -> Result<(), Self::Error> { + let p = Rc::new(WlKeyboard::new(req.id, slf)); + track!(self.client, p); + self.client.add_client_obj(&p)?; + self.keyboards.set(req.id, p.clone()); + if let Some(surface) = self.global.keyboard_node.get().node_into_surface() + && surface.client.id == self.client.id + { + p.enter( + self.client.next_serial(), + surface.id, + &self.global.seat_kb_state.get().borrow().kb_state, + ); + } + if self.version >= REPEAT_INFO_SINCE { + let (rate, delay) = self.global.repeat_rate.get(); + p.send_repeat_info(rate, delay); + } + Ok(()) + } + + fn get_touch(&self, req: GetTouch, slf: &Rc) -> Result<(), Self::Error> { + let p = Rc::new(WlTouch::new(req.id, slf)); + track!(self.client, p); + self.client.add_client_obj(&p)?; + self.touches.set(req.id, p); + Ok(()) + } + + fn release(&self, _req: Release, _slf: &Rc) -> Result<(), Self::Error> { + { + let mut bindings = self.global.bindings.borrow_mut(); + if let Entry::Occupied(mut hm) = bindings.entry(self.client.id) { + hm.get_mut().remove(&self.id); + if hm.get().is_empty() { + hm.remove(); + } + } + } + self.client.remove_obj(self)?; + Ok(()) + } +} + +object_base! { + self = WlSeat; + version = self.version; +} + +impl Object for WlSeat { + fn break_loops(&self) { + { + let mut bindings = self.global.bindings.borrow_mut(); + if let Entry::Occupied(mut hm) = bindings.entry(self.client.id) { + hm.get_mut().remove(&self.id); + if hm.get().is_empty() { + hm.remove(); + } + } + } + self.pointers.clear(); + self.relative_pointers.clear(); + self.keyboards.clear(); + self.touches.clear(); + } +} + +dedicated_add_obj!(WlSeat, WlSeatId, seats); + +#[derive(Debug, Error)] +pub enum WlSeatError { + #[error(transparent)] + ClientError(Box), + #[error(transparent)] + TransferError(#[from] TransferError), + #[error(transparent)] + WlKeyboardError(Box), + #[error("Data source has a toplevel attached")] + OfferHasDrag, +} +efrom!(WlSeatError, ClientError); +efrom!(WlSeatError, WlKeyboardError);