1
0
Fork 0
forked from wry/wry

wl-seat: split device and object handlers

This commit is contained in:
kossLAN 2026-05-29 20:44:28 -04:00
parent 054a3a919f
commit ff04218023
No known key found for this signature in database
4 changed files with 357 additions and 346 deletions

View file

@ -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<Self>,
}
const READ_ONLY_KEYMAP_SINCE: Version = Version(7);
impl WlSeat {
fn send_capabilities(self: &Rc<Self>) {
self.client.event(Capabilities {
self_id: self.id,
capabilities: self.global.capabilities.get(),
})
}
fn send_name(self: &Rc<Self>, name: &str) {
self.client.event(Name {
self_id: self.id,
name,
})
}
pub fn keymap_fd(&self, state: &KeyboardState) -> Result<KeymapFd, WlKeyboardError> {
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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<ClientError>),
#[error(transparent)]
TransferError(#[from] TransferError),
#[error(transparent)]
WlKeyboardError(Box<WlKeyboardError>),
#[error("Data source has a toplevel attached")]
OfferHasDrag,
}
efrom!(WlSeatError, ClientError);
efrom!(WlSeatError, WlKeyboardError);
pub fn collect_kb_foci2(node: Rc<dyn Node>, seats: &mut SmallVec<[Rc<WlSeatGlobal>; 3]>) {
node.node_visit(&mut generic_node_visitor(|node| {
@ -1935,207 +1801,3 @@ pub fn collect_kb_foci(node: Rc<dyn Node>) -> SmallVec<[Rc<WlSeatGlobal>; 3]> {
collect_kb_foci2(node, &mut res);
res
}
impl DeviceHandlerData {
pub fn set_seat(&self, _state: &State, seat: Option<Rc<WlSeatGlobal>>) {
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<Rc<KbvmMap>>) {
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<WlSeatGlobal>,
client_id: ClientId,
old_pos: (Fixed, Fixed),
new_pos: (Fixed, Fixed),
}
pub async fn handle_position_hint_requests(state: Rc<State>) {
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<State>) {
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,
}

View file

@ -0,0 +1,151 @@
use super::*;
impl DeviceHandlerData {
pub fn set_seat(&self, _state: &State, seat: Option<Rc<WlSeatGlobal>>) {
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<Rc<KbvmMap>>) {
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)
}
}

View file

@ -0,0 +1,55 @@
use {super::*, CursorPositionType::Warp};
pub struct PositionHintRequest {
pub(super) seat: Rc<WlSeatGlobal>,
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<State>) {
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<State>) {
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,
}

View file

@ -0,0 +1,143 @@
use super::*;
const READ_ONLY_KEYMAP_SINCE: Version = Version(7);
impl WlSeat {
pub(super) fn send_capabilities(self: &Rc<Self>) {
self.client.event(Capabilities {
self_id: self.id,
capabilities: self.global.capabilities.get(),
})
}
pub(super) fn send_name(self: &Rc<Self>, name: &str) {
self.client.event(Name {
self_id: self.id,
name,
})
}
pub fn keymap_fd(&self, state: &KeyboardState) -> Result<KeymapFd, WlKeyboardError> {
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<Self>) -> 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<Self>) -> 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<Self>) -> 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<Self>) -> 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<ClientError>),
#[error(transparent)]
TransferError(#[from] TransferError),
#[error(transparent)]
WlKeyboardError(Box<WlKeyboardError>),
#[error("Data source has a toplevel attached")]
OfferHasDrag,
}
efrom!(WlSeatError, ClientError);
efrom!(WlSeatError, WlKeyboardError);