소스 검색

wayland: Implement the pointer warp protocol

The pointer warp protocol allows us to warp the pointer to a different position on
the surface, without any hacks like locking and unlocking the pointer.
Xaver Hugl 11 달 전
부모
커밋
ec1297199e
4개의 변경된 파일120개의 추가작업 그리고 31개의 파일을 삭제
  1. 39 31
      src/video/wayland/SDL_waylandmouse.c
  2. 8 0
      src/video/wayland/SDL_waylandvideo.c
  3. 1 0
      src/video/wayland/SDL_waylandvideo.h
  4. 72 0
      wayland-protocols/pointer-warp-v1.xml

+ 39 - 31
src/video/wayland/SDL_waylandmouse.c

@@ -38,6 +38,7 @@
 #include "cursor-shape-v1-client-protocol.h"
 #include "pointer-constraints-unstable-v1-client-protocol.h"
 #include "viewporter-client-protocol.h"
+#include "pointer-warp-v1-client-protocol.h"
 
 #include "../../SDL_hints_c.h"
 
@@ -832,43 +833,50 @@ void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float
     SDL_VideoData *d = vd->internal;
 
     if (seat->pointer.wl_pointer) {
-        bool toggle_lock = !seat->pointer.locked_pointer;
-        bool update_grabs = false;
-
-        /* The pointer confinement protocol allows setting a hint to warp the pointer,
-         * but only when the pointer is locked.
-         *
-         * Lock the pointer, set the position hint, unlock, and hope for the best.
-         */
-        if (toggle_lock) {
-            if (seat->pointer.confined_pointer) {
-                zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer);
-                seat->pointer.confined_pointer = NULL;
-                update_grabs = true;
+        if (d->wp_pointer_warp_v1) {
+            // It's a protocol error to warp the pointer outside of the surface, so clamp the position.
+            const wl_fixed_t f_x = wl_fixed_from_double(SDL_clamp(x / window->pointer_scale.x, 0, window->current.logical_width));
+            const wl_fixed_t f_y = wl_fixed_from_double(SDL_clamp(y / window->pointer_scale.y, 0, window->current.logical_height));
+            wp_pointer_warp_v1_warp_pointer(d->wp_pointer_warp_v1, window->surface, seat->pointer.wl_pointer, f_x, f_y, seat->pointer.enter_serial);
+        } else {
+            bool toggle_lock = !seat->pointer.locked_pointer;
+            bool update_grabs = false;
+
+            /* The pointer confinement protocol allows setting a hint to warp the pointer,
+             * but only when the pointer is locked.
+             *
+             * Lock the pointer, set the position hint, unlock, and hope for the best.
+             */
+            if (toggle_lock) {
+                if (seat->pointer.confined_pointer) {
+                    zwp_confined_pointer_v1_destroy(seat->pointer.confined_pointer);
+                    seat->pointer.confined_pointer = NULL;
+                    update_grabs = true;
+                }
+                seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, window->surface,
+                                                                                       seat->pointer.wl_pointer, NULL,
+                                                                                       ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT);
             }
-            seat->pointer.locked_pointer = zwp_pointer_constraints_v1_lock_pointer(d->pointer_constraints, window->surface,
-                                                                                   seat->pointer.wl_pointer, NULL,
-                                                                                   ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_ONESHOT);
-        }
 
-        const wl_fixed_t f_x = wl_fixed_from_double(x / window->pointer_scale.x);
-        const wl_fixed_t f_y = wl_fixed_from_double(y / window->pointer_scale.y);
-        zwp_locked_pointer_v1_set_cursor_position_hint(seat->pointer.locked_pointer, f_x, f_y);
-        wl_surface_commit(window->surface);
+            const wl_fixed_t f_x = wl_fixed_from_double(x / window->pointer_scale.x);
+            const wl_fixed_t f_y = wl_fixed_from_double(y / window->pointer_scale.y);
+            zwp_locked_pointer_v1_set_cursor_position_hint(seat->pointer.locked_pointer, f_x, f_y);
+            wl_surface_commit(window->surface);
 
-        if (toggle_lock) {
-            zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer);
-            seat->pointer.locked_pointer = NULL;
+            if (toggle_lock) {
+                zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer);
+                seat->pointer.locked_pointer = NULL;
 
-            if (update_grabs) {
-                Wayland_SeatUpdatePointerGrab(seat);
+                if (update_grabs) {
+                    Wayland_SeatUpdatePointerGrab(seat);
+                }
             }
-        }
 
-        /* NOTE: There is a pending warp event under discussion that should replace this when available.
-         * https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340
-         */
-        SDL_SendMouseMotion(0, window->sdlwindow, seat->pointer.sdl_id, false, x, y);
+            /* NOTE: There is a pending warp event under discussion that should replace this when available.
+             * https://gitlab.freedesktop.org/wayland/wayland/-/merge_requests/340
+             */
+            SDL_SendMouseMotion(0, window->sdlwindow, seat->pointer.sdl_id, false, x, y);
+        }
     }
 }
 

+ 8 - 0
src/video/wayland/SDL_waylandvideo.c

@@ -66,6 +66,7 @@
 #include "xdg-shell-client-protocol.h"
 #include "xdg-toplevel-icon-v1-client-protocol.h"
 #include "color-management-v1-client-protocol.h"
+#include "pointer-warp-v1-client-protocol.h"
 
 #ifdef HAVE_LIBDECOR_H
 #include <libdecor.h>
@@ -1311,6 +1312,8 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
     } else if (SDL_strcmp(interface, "wp_color_manager_v1") == 0) {
         d->wp_color_manager_v1 = wl_registry_bind(d->registry, id, &wp_color_manager_v1_interface, 1);
         Wayland_InitColorManager(d);
+    } else if (SDL_strcmp(interface, "wp_pointer_warp_v1") == 0) {
+        d->wp_pointer_warp_v1 = wl_registry_bind(d->registry, id, &wp_pointer_warp_v1_interface, 1);
     }
 }
 
@@ -1620,6 +1623,11 @@ static void Wayland_VideoCleanup(SDL_VideoDevice *_this)
         data->wp_color_manager_v1 = NULL;
     }
 
+    if (data->wp_pointer_warp_v1) {
+        wp_pointer_warp_v1_destroy(data->wp_pointer_warp_v1);
+        data->wp_pointer_warp_v1 = NULL;
+    }
+
     if (data->compositor) {
         wl_compositor_destroy(data->compositor);
         data->compositor = NULL;

+ 1 - 0
src/video/wayland/SDL_waylandvideo.h

@@ -65,6 +65,7 @@ struct SDL_VideoData
     } shell;
     struct zwp_relative_pointer_manager_v1 *relative_pointer_manager;
     struct zwp_pointer_constraints_v1 *pointer_constraints;
+    struct wp_pointer_warp_v1 *wp_pointer_warp_v1;
     struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
     struct wl_data_device_manager *data_device_manager;
     struct zwp_primary_selection_device_manager_v1 *primary_selection_device_manager;

+ 72 - 0
wayland-protocols/pointer-warp-v1.xml

@@ -0,0 +1,72 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<protocol name="pointer_warp_v1">
+  <copyright>
+    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.
+  </copyright>
+
+  <interface name="wp_pointer_warp_v1" version="1">
+    <description summary="reposition the pointer to a location on a surface">
+      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.
+    </description>
+
+    <request name="destroy" type="destructor">
+      <description summary="destroy the warp manager">
+        Destroy the pointer warp manager.
+      </description>
+    </request>
+
+    <request name="warp_pointer">
+      <description summary="reposition the pointer">
+        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.
+
+      </description>
+      <arg name="surface" type="object" interface="wl_surface"
+           summary="surface to position the pointer on"/>
+      <arg name="pointer" type="object" interface="wl_pointer"
+           summary="the pointer that should be repositioned"/>
+      <arg name="x" type="fixed"/>
+      <arg name="y" type="fixed"/>
+      <arg name="serial" type="uint" summary="serial number of the enter event"/>
+    </request>
+  </interface>
+</protocol>