From 4dceee5252781d323bfcd45808e2637f3ea02429 Mon Sep 17 00:00:00 2001 From: Kiisu_Master <142301391+Kiisu-Master@users.noreply.github.com> Date: Sat, 1 Nov 2025 17:42:50 +0200 Subject: [PATCH] Add pointer warping on wayland --- platform/linuxbsd/wayland/SCsub | 1 + .../wayland/display_server_wayland.cpp | 13 +-- platform/linuxbsd/wayland/wayland_embedder.h | 4 + platform/linuxbsd/wayland/wayland_thread.cpp | 95 ++++++++++++++++--- platform/linuxbsd/wayland/wayland_thread.h | 6 ++ thirdparty/README.md | 2 + .../staging/pointer-warp/README | 7 ++ .../staging/pointer-warp/pointer-warp-v1.xml | 72 ++++++++++++++ 8 files changed, 176 insertions(+), 24 deletions(-) create mode 100644 thirdparty/wayland-protocols/staging/pointer-warp/README create mode 100644 thirdparty/wayland-protocols/staging/pointer-warp/pointer-warp-v1.xml diff --git a/platform/linuxbsd/wayland/SCsub b/platform/linuxbsd/wayland/SCsub index 7bdf03da39..d34bc47a11 100644 --- a/platform/linuxbsd/wayland/SCsub +++ b/platform/linuxbsd/wayland/SCsub @@ -69,6 +69,7 @@ generated_sources = [ generate_from_xml( "xdg_toplevel_icon", "#thirdparty/wayland-protocols/staging/xdg-toplevel-icon/xdg-toplevel-icon-v1.xml" ), + generate_from_xml("pointer_warp", "#thirdparty/wayland-protocols/staging/pointer-warp/pointer-warp-v1.xml"), # Unstable protocols generate_from_xml( "idle_inhibit", "#thirdparty/wayland-protocols/unstable/idle-inhibit/idle-inhibit-unstable-v1.xml" diff --git a/platform/linuxbsd/wayland/display_server_wayland.cpp b/platform/linuxbsd/wayland/display_server_wayland.cpp index dce5ea1845..19d67028e8 100644 --- a/platform/linuxbsd/wayland/display_server_wayland.cpp +++ b/platform/linuxbsd/wayland/display_server_wayland.cpp @@ -450,20 +450,9 @@ bool DisplayServerWayland::mouse_is_mode_override_enabled() const { return mouse_mode_override_enabled; } -// NOTE: This is hacked together (and not guaranteed to work in the first place) -// as for some reason the there's no proper way to ask the compositor to warp -// the pointer, although, at the time of writing, there's a proposal for a -// proper protocol for this. See: -// https://gitlab.freedesktop.org/wayland/wayland-protocols/-/issues/158 void DisplayServerWayland::warp_mouse(const Point2i &p_to) { MutexLock mutex_lock(wayland_thread.mutex); - - WaylandThread::PointerConstraint old_constraint = wayland_thread.pointer_get_constraint(); - - wayland_thread.pointer_set_constraint(WaylandThread::PointerConstraint::LOCKED); - wayland_thread.pointer_set_hint(p_to); - - wayland_thread.pointer_set_constraint(old_constraint); + wayland_thread.pointer_warp(p_to); } Point2i DisplayServerWayland::mouse_get_position() const { diff --git a/platform/linuxbsd/wayland/wayland_embedder.h b/platform/linuxbsd/wayland/wayland_embedder.h index 62861d9674..4c0e230f1c 100644 --- a/platform/linuxbsd/wayland/wayland_embedder.h +++ b/platform/linuxbsd/wayland/wayland_embedder.h @@ -58,6 +58,7 @@ #include "protocol/linux_explicit_synchronization_unstable_v1.gen.h" #include "protocol/pointer_constraints.gen.h" #include "protocol/pointer_gestures.gen.h" +#include "protocol/pointer_warp.gen.h" #include "protocol/primary_selection.gen.h" #include "protocol/relative_pointer.gen.h" #include "protocol/tablet.gen.h" @@ -451,6 +452,9 @@ class WaylandEmbedder { &wp_tearing_control_manager_v1_interface, &wp_tearing_control_v1_interface, + // pointer-warp-v1 + &wp_pointer_warp_v1_interface, + // Our custom things. &godot_embedding_compositor_interface, &godot_embedded_client_interface, diff --git a/platform/linuxbsd/wayland/wayland_thread.cpp b/platform/linuxbsd/wayland/wayland_thread.cpp index c2cefe629f..fcb39d83d9 100644 --- a/platform/linuxbsd/wayland/wayland_thread.cpp +++ b/platform/linuxbsd/wayland/wayland_thread.cpp @@ -726,6 +726,12 @@ void WaylandThread::_wl_registry_on_global(void *data, struct wl_registry *wl_re return; } + if (strcmp(interface, wp_pointer_warp_v1_interface.name) == 0) { + registry->wp_pointer_warp = (struct wp_pointer_warp_v1 *)wl_registry_bind(wl_registry, name, &wp_pointer_warp_v1_interface, 1); + registry->wp_pointer_warp_name = name; + return; + } + if (strcmp(interface, FIFO_INTERFACE_NAME) == 0) { registry->wp_fifo_manager_name = name; } @@ -1097,6 +1103,17 @@ void WaylandThread::_wl_registry_on_global_remove(void *data, struct wl_registry return; } + if (name == registry->wp_pointer_warp_name) { + if (registry->wp_pointer_warp) { + wp_pointer_warp_v1_destroy(registry->wp_pointer_warp); + registry->wp_pointer_warp = nullptr; + } + + registry->wp_pointer_warp_name = 0; + + return; + } + { // Iterate through all of the seats to find if any got removed. List::Element *E = registry->wl_seats.front(); @@ -3661,6 +3678,21 @@ void WaylandThread::seat_state_set_hint(SeatState *p_ss, int p_x, int p_y) { zwp_locked_pointer_v1_set_cursor_position_hint(p_ss->wp_locked_pointer, wl_fixed_from_int(p_x), wl_fixed_from_int(p_y)); } +void WaylandThread::seat_state_warp_pointer(SeatState *p_ss, int p_x, int p_y) { + if (registry.wp_pointer_warp == nullptr) { + return; + } + + if (p_ss->pointer_data.pointed_id == DisplayServer::INVALID_WINDOW_ID) { + return; + } + + struct wl_surface *surface = window_get_wl_surface(p_ss->pointer_data.pointed_id); + ERR_FAIL_NULL(surface); + + wp_pointer_warp_v1_warp_pointer(registry.wp_pointer_warp, surface, p_ss->wl_pointer, wl_fixed_from_int(p_x), wl_fixed_from_int(p_y), p_ss->pointer_enter_serial); +} + void WaylandThread::seat_state_confine_pointer(SeatState *p_ss) { ERR_FAIL_NULL(p_ss); @@ -4724,25 +4756,60 @@ void WaylandThread::pointer_set_hint(const Point2i &p_hint) { } WindowState *ws = window_get_state(ss->pointer_data.pointed_id); - - int hint_x = 0; - int hint_y = 0; - - if (ws) { - // NOTE: It looks like it's not really recommended to convert from - // "godot-space" to "wayland-space" and in general I received mixed feelings - // discussing about this. I'm not really sure about the maths behind this but, - // oh well, we're setting a cursor hint. ¯\_(ツ)_/¯ - // See: https://oftc.irclog.whitequark.org/wayland/2023-08-23#1692756914-1692816818 - hint_x = std::round(p_hint.x / window_state_get_scale_factor(ws)); - hint_y = std::round(p_hint.y / window_state_get_scale_factor(ws)); + if (!ws) { + return; } + // NOTE: It looks like it's not really recommended to convert from + // "godot-space" to "wayland-space" and in general I received mixed feelings + // discussing about this. I'm not really sure about the maths behind this but, + // oh well, we're setting a cursor hint. ¯\_(ツ)_/¯ + // See: https://oftc.irclog.whitequark.org/wayland/2023-08-23#1692756914-1692816818 + int hint_x = Math::round(p_hint.x / window_state_get_scale_factor(ws)); + int hint_y = Math::round(p_hint.y / window_state_get_scale_factor(ws)); + if (ss) { seat_state_set_hint(ss, hint_x, hint_y); } } +void WaylandThread::pointer_warp(const Point2i &p_to) { + // NOTE: This is for compositors that don't support the pointer-warp protocol. + // It's hacked together and not guaranteed to work. + if (registry.wp_pointer_warp == nullptr) { + PointerConstraint old_constraint = pointer_get_constraint(); + + pointer_set_constraint(PointerConstraint::LOCKED); + pointer_set_hint(p_to); + + pointer_set_constraint(old_constraint); + + return; + } + + SeatState *ss = wl_seat_get_seat_state(wl_seat_current); + if (!ss) { + return; + } + + WindowState *ws = window_get_state(ss->pointer_data.pointed_id); + if (!ws) { + return; + } + + // NOTE: It looks like it's not really recommended to convert from + // "godot-space" to "wayland-space" and in general I received mixed feelings + // discussing about this. I'm not really sure about the maths behind this but, + // oh well. ¯\_(ツ)_/¯ + // See: https://oftc.irclog.whitequark.org/wayland/2023-08-23#1692756914-1692816818 + int wl_pos_x = Math::round(p_to.x / window_state_get_scale_factor(ws)); + int wl_pos_y = Math::round(p_to.y / window_state_get_scale_factor(ws)); + + if (ss) { + seat_state_warp_pointer(ss, wl_pos_x, wl_pos_y); + } +} + WaylandThread::PointerConstraint WaylandThread::pointer_get_constraint() const { return pointer_constraint; } @@ -5608,6 +5675,10 @@ void WaylandThread::destroy() { zwp_relative_pointer_manager_v1_destroy(registry.wp_relative_pointer_manager); } + if (registry.wp_pointer_warp) { + wp_pointer_warp_v1_destroy(registry.wp_pointer_warp); + } + if (registry.xdg_activation) { xdg_activation_v1_destroy(registry.xdg_activation); } diff --git a/platform/linuxbsd/wayland/wayland_thread.h b/platform/linuxbsd/wayland/wayland_thread.h index d589b5f584..180681c46d 100644 --- a/platform/linuxbsd/wayland/wayland_thread.h +++ b/platform/linuxbsd/wayland/wayland_thread.h @@ -59,6 +59,7 @@ #include "wayland/protocol/cursor_shape.gen.h" #include "wayland/protocol/pointer_constraints.gen.h" #include "wayland/protocol/pointer_gestures.gen.h" +#include "wayland/protocol/pointer_warp.gen.h" #include "wayland/protocol/relative_pointer.gen.h" #undef pointer #include "wayland/protocol/fractional_scale.gen.h" @@ -230,6 +231,9 @@ public: struct zwp_text_input_manager_v3 *wp_text_input_manager = nullptr; uint32_t wp_text_input_manager_name = 0; + struct wp_pointer_warp_v1 *wp_pointer_warp = nullptr; + uint32_t wp_pointer_warp_name = 0; + // We're really not meant to use this one directly but we still need to know // whether it's available. uint32_t wp_fifo_manager_name = 0; @@ -1085,6 +1089,7 @@ public: void seat_state_unlock_pointer(SeatState *p_ss); void seat_state_lock_pointer(SeatState *p_ss); void seat_state_set_hint(SeatState *p_ss, int p_x, int p_y); + void seat_state_warp_pointer(SeatState *p_ss, int p_x, int p_y); void seat_state_confine_pointer(SeatState *p_ss); static void seat_state_update_cursor(SeatState *p_ss); @@ -1145,6 +1150,7 @@ public: void pointer_set_constraint(PointerConstraint p_constraint); void pointer_set_hint(const Point2i &p_hint); + void pointer_warp(const Point2i &p_to); PointerConstraint pointer_get_constraint() const; DisplayServer::WindowID pointer_get_pointed_window_id() const; DisplayServer::WindowID pointer_get_last_pointed_window_id() const; diff --git a/thirdparty/README.md b/thirdparty/README.md index 977f291911..6c5a03753e 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -1200,6 +1200,8 @@ Files extracted from upstream source: - `staging/xdg-activation/README` - `staging/xdg-activation/xdg-activation-v1.xml` - `staging/xdg-system-bell/xdg-system-bell-v1.xml` +- `staging/pointer-warp/pointer-warp-v1.xml` +- `staging/pointer-warp/README` - `unstable/idle-inhibit/README` - `unstable/idle-inhibit/idle-inhibit-unstable-v1.xml` - `unstable/pointer-constraints/README` diff --git a/thirdparty/wayland-protocols/staging/pointer-warp/README b/thirdparty/wayland-protocols/staging/pointer-warp/README new file mode 100644 index 0000000000..a948475454 --- /dev/null +++ b/thirdparty/wayland-protocols/staging/pointer-warp/README @@ -0,0 +1,7 @@ +pointer-warp protocol + +Maintainers: +Neal Gompa (@Conan_Kudo) +Xaver Hugl (@Zamundaaa) +Matthias Klumpp (@mak) +Vlad Zahorodnii (@zzag) diff --git a/thirdparty/wayland-protocols/staging/pointer-warp/pointer-warp-v1.xml b/thirdparty/wayland-protocols/staging/pointer-warp/pointer-warp-v1.xml new file mode 100644 index 0000000000..158dad83c5 --- /dev/null +++ b/thirdparty/wayland-protocols/staging/pointer-warp/pointer-warp-v1.xml @@ -0,0 +1,72 @@ + + + + Copyright © 2024 Neal Gompa + Copyright © 2024 Xaver Hugl + Copyright © 2024 Matthias Klumpp + Copyright © 2024 Vlad Zahorodnii + + Permission is hereby granted, free of charge, to any person obtaining a + copy of this software and associated documentation files (the "Software"), + to deal in the Software without restriction, including without limitation + the rights to use, copy, modify, merge, publish, distribute, sublicense, + and/or sell copies of the Software, and to permit persons to whom the + Software is furnished to do so, subject to the following conditions: + The above copyright notice and this permission notice (including the next + paragraph) shall be included in all copies or substantial portions of the + Software. + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING + FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER + DEALINGS IN THE SOFTWARE. + + + + + This global interface allows applications to request the pointer to be + moved to a position relative to a wl_surface. + + Note that if the desired behavior is to constrain the pointer to an area + or lock it to a position, this protocol does not provide a reliable way + to do that. The pointer constraint and pointer lock protocols should be + used for those use cases instead. + + Warning! The protocol described in this file is currently in the testing + phase. Backward compatible changes may be added together with the + corresponding interface version bump. Backward incompatible changes can + only be done by creating a new major version of the extension. + + + + + Destroy the pointer warp manager. + + + + + + Request the compositor to move the pointer to a surface-local position. + Whether or not the compositor honors the request is implementation defined, + but it should + - honor it if the surface has pointer focus, including + when it has an implicit pointer grab + - reject it if the enter serial is incorrect + - reject it if the requested position is outside of the surface + + Note that the enter serial is valid for any surface of the client, + and does not have to be from the surface the pointer is warped to. + + + + + + + + + +