From c68e47f457c78f325415c492f765717bf339c1bf Mon Sep 17 00:00:00 2001 From: Julian Orth Date: Thu, 17 Jul 2025 14:56:24 +0200 Subject: [PATCH] wayland: implement pointer-warp-v1 --- docs/features.md | 2 + release-notes.md | 1 + src/globals.rs | 2 + src/ifs/wl_seat.rs | 1 + src/ifs/wl_seat/wp_pointer_warp_v1.rs | 120 ++++++++++++++++++++++++++ wire/wp_pointer_warp_v1.txt | 11 +++ 6 files changed, 137 insertions(+) create mode 100644 src/ifs/wl_seat/wp_pointer_warp_v1.rs create mode 100644 wire/wp_pointer_warp_v1.txt diff --git a/docs/features.md b/docs/features.md index 528cd70d..ba8ac527 100644 --- a/docs/features.md +++ b/docs/features.md @@ -182,6 +182,7 @@ Jay supports the following wayland protocols: | wp_fifo_manager_v1 | 1 | | | wp_fractional_scale_manager_v1 | 1 | | | wp_linux_drm_syncobj_manager_v1 | 1 | | +| wp_pointer_warp_v1 | 1 | | | wp_presentation | 2 | | | wp_security_context_manager_v1 | 1 | | | wp_single_pixel_buffer_manager_v1 | 1 | | @@ -195,6 +196,7 @@ Jay supports the following wayland protocols: | zwlr_data_control_manager_v1 | 2 | Yes | | zwlr_foreign_toplevel_manager_v1 | 3 | Yes | | zwlr_layer_shell_v1 | 5 | No[^lsaccess] | +| zwlr_output_manager_v1 | 4 | Yes | | zwlr_screencopy_manager_v1 | 3 | Yes | | zwp_idle_inhibit_manager_v1 | 1 | | | zwp_input_method_manager_v2 | 1 | Yes | diff --git a/release-notes.md b/release-notes.md index a8fcb002..8e4bed9d 100644 --- a/release-notes.md +++ b/release-notes.md @@ -36,6 +36,7 @@ [setup.md](docs/setup.md). - Implement wlr-foreign-toplevel-management-v1. - Implement wlr-output-management-v1. +- Implement pointer-warp-v1. # 1.10.0 (2025-04-22) diff --git a/src/globals.rs b/src/globals.rs index 4a39e817..9de63369 100644 --- a/src/globals.rs +++ b/src/globals.rs @@ -34,6 +34,7 @@ use { zwp_input_method_manager_v2::ZwpInputMethodManagerV2Global, zwp_text_input_manager_v3::ZwpTextInputManagerV3Global, }, + wp_pointer_warp_v1::WpPointerWarpV1Global, zwp_pointer_constraints_v1::ZwpPointerConstraintsV1Global, zwp_pointer_gestures_v1::ZwpPointerGesturesV1Global, zwp_relative_pointer_manager_v1::ZwpRelativePointerManagerV1Global, @@ -229,6 +230,7 @@ impl Globals { add_singleton!(WpColorManagerV1Global); add_singleton!(XdgToplevelTagManagerV1Global); add_singleton!(JayHeadManagerV1Global); + add_singleton!(WpPointerWarpV1Global); } pub fn add_backend_singletons(&self, backend: &Rc) { diff --git a/src/ifs/wl_seat.rs b/src/ifs/wl_seat.rs index eb9bd004..359ea8c2 100644 --- a/src/ifs/wl_seat.rs +++ b/src/ifs/wl_seat.rs @@ -10,6 +10,7 @@ mod touch_owner; pub mod wl_keyboard; pub mod wl_pointer; pub mod wl_touch; +pub mod wp_pointer_warp_v1; pub mod zwp_pointer_constraints_v1; pub mod zwp_pointer_gesture_hold_v1; pub mod zwp_pointer_gesture_pinch_v1; diff --git a/src/ifs/wl_seat/wp_pointer_warp_v1.rs b/src/ifs/wl_seat/wp_pointer_warp_v1.rs new file mode 100644 index 00000000..7626f127 --- /dev/null +++ b/src/ifs/wl_seat/wp_pointer_warp_v1.rs @@ -0,0 +1,120 @@ +use { + crate::{ + client::{Client, ClientError}, + globals::{Global, GlobalName}, + ifs::wl_seat::PositionHintRequest, + leaks::Tracker, + object::{Object, Version}, + tree::Node, + wire::{ + WpPointerWarpV1Id, + wp_pointer_warp_v1::{Destroy, WarpPointer, WpPointerWarpV1RequestHandler}, + }, + }, + std::rc::Rc, + thiserror::Error, +}; + +pub struct WpPointerWarpV1Global { + name: GlobalName, +} + +impl WpPointerWarpV1Global { + pub fn new(name: GlobalName) -> Self { + Self { name } + } + + fn bind_( + self: Rc, + id: WpPointerWarpV1Id, + client: &Rc, + version: Version, + ) -> Result<(), WpPointerWarpV1Error> { + let obj = Rc::new(WpPointerWarpV1 { + id, + client: client.clone(), + tracker: Default::default(), + version, + }); + track!(client, obj); + client.add_client_obj(&obj)?; + Ok(()) + } +} + +global_base!(WpPointerWarpV1Global, WpPointerWarpV1, WpPointerWarpV1Error); + +impl Global for WpPointerWarpV1Global { + fn singleton(&self) -> bool { + true + } + + fn version(&self) -> u32 { + 1 + } +} + +simple_add_global!(WpPointerWarpV1Global); + +pub struct WpPointerWarpV1 { + pub id: WpPointerWarpV1Id, + pub client: Rc, + pub tracker: Tracker, + pub version: Version, +} + +impl WpPointerWarpV1RequestHandler for WpPointerWarpV1 { + type Error = WpPointerWarpV1Error; + + fn destroy(&self, _req: Destroy, _slf: &Rc) -> Result<(), Self::Error> { + self.client.remove_obj(self)?; + Ok(()) + } + + fn warp_pointer(&self, req: WarpPointer, _slf: &Rc) -> Result<(), Self::Error> { + let Some(serial) = self.client.map_serial(req.serial) else { + return Ok(()); + }; + if Some(serial) != self.client.last_enter_serial.get() { + return Ok(()); + } + let pointer = self.client.lookup(req.pointer)?; + let seat = &pointer.seat.global; + let Some(pointer_node) = seat.pointer_node() else { + return Ok(()); + }; + if pointer_node.node_client_id() != Some(self.client.id) { + return Ok(()); + } + let (x, y) = (req.x, req.y); + let surface = self.client.lookup(req.surface)?; + let buffer = surface.node_absolute_position(); + let (x_int, y_int) = buffer.translate_inv(x.round_down(), y.round_down()); + self.client + .state + .position_hint_requests + .push(PositionHintRequest { + seat: seat.clone(), + node: surface.node_id.into(), + old_pos: seat.pointer_cursor.position(), + new_pos: (x.apply_fract(x_int), y.apply_fract(y_int)), + }); + Ok(()) + } +} + +object_base! { + self = WpPointerWarpV1; + version = self.version; +} + +impl Object for WpPointerWarpV1 {} + +simple_add_obj!(WpPointerWarpV1); + +#[derive(Debug, Error)] +pub enum WpPointerWarpV1Error { + #[error(transparent)] + ClientError(Box), +} +efrom!(WpPointerWarpV1Error, ClientError); diff --git a/wire/wp_pointer_warp_v1.txt b/wire/wp_pointer_warp_v1.txt new file mode 100644 index 00000000..8155026c --- /dev/null +++ b/wire/wp_pointer_warp_v1.txt @@ -0,0 +1,11 @@ +request destroy { + +} + +request warp_pointer { + surface: id(wl_surface), + pointer: id(wl_pointer), + x: fixed, + y: fixed, + serial: u32, +}