Browse Source

wayland: Enable relative pointer mode based on the window flag

This can be toggled per-window, so use the individual window flags instead of the global toggle to selectively enable it only for the relevant window in a multi-seat scenario, as is already done with keyboard and pointer grabs.
Frank Praznik 3 months ago
parent
commit
b0a282e31f

+ 3 - 2
src/events/SDL_mouse.c

@@ -1314,8 +1314,9 @@ static void SDL_MaybeEnableWarpEmulation(SDL_Window *window, float x, float y)
                 // Require two consecutive warps to the center within a certain timespan to enter warp emulation mode.
                 // Require two consecutive warps to the center within a certain timespan to enter warp emulation mode.
                 const Uint64 now = SDL_GetTicksNS();
                 const Uint64 now = SDL_GetTicksNS();
                 if (now - mouse->last_center_warp_time_ns < WARP_EMULATION_THRESHOLD_NS) {
                 if (now - mouse->last_center_warp_time_ns < WARP_EMULATION_THRESHOLD_NS) {
-                    if (SDL_SetRelativeMouseMode(true)) {
-                        mouse->warp_emulation_active = true;
+                    mouse->warp_emulation_active = true;
+                    if (!SDL_SetRelativeMouseMode(true)) {
+                        mouse->warp_emulation_active = false;
                     }
                     }
                 }
                 }
 
 

+ 47 - 57
src/video/wayland/SDL_waylandevents.c

@@ -912,7 +912,7 @@ static void pointer_handle_button_common(SDL_WaylandSeat *seat, uint32_t serial,
          *
          *
          * The mouse is not captured in relative mode.
          * The mouse is not captured in relative mode.
          */
          */
-        if (!seat->display->relative_mode_enabled || !Wayland_SeatHasRelativePointerFocus(seat)) {
+        if (!seat->pointer.relative_pointer) {
             if (seat->pointer.buttons_pressed != 0) {
             if (seat->pointer.buttons_pressed != 0) {
                 window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
                 window->sdlwindow->flags |= SDL_WINDOW_MOUSE_CAPTURE;
             } else {
             } else {
@@ -1164,30 +1164,23 @@ static void relative_pointer_handle_relative_motion(void *data,
                                                     wl_fixed_t dy_unaccel_w)
                                                     wl_fixed_t dy_unaccel_w)
 {
 {
     SDL_WaylandSeat *seat = data;
     SDL_WaylandSeat *seat = data;
+    SDL_WindowData *window = seat->pointer.focus;
+    SDL_Mouse *mouse = SDL_GetMouse();
 
 
-    if (seat->display->relative_mode_enabled) {
-        SDL_WindowData *window = seat->pointer.focus;
-
-        // Relative motion follows keyboard focus.
-        if (Wayland_SeatHasRelativePointerFocus(seat)) {
-            SDL_Mouse *mouse = SDL_GetMouse();
-
-            // Relative pointer event times are in microsecond granularity.
-            const Uint64 timestamp = Wayland_AdjustEventTimestampBase(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
+    // Relative pointer event times are in microsecond granularity.
+    const Uint64 timestamp = Wayland_AdjustEventTimestampBase(SDL_US_TO_NS(((Uint64)time_hi << 32) | (Uint64)time_lo));
 
 
-            double dx;
-            double dy;
-            if (mouse->InputTransform || !mouse->enable_relative_system_scale) {
-                dx = wl_fixed_to_double(dx_unaccel_w);
-                dy = wl_fixed_to_double(dy_unaccel_w);
-            } else {
-                dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x;
-                dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y;
-            }
-
-            SDL_SendMouseMotion(timestamp, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy);
-        }
+    double dx;
+    double dy;
+    if (mouse->InputTransform || !mouse->enable_relative_system_scale) {
+        dx = wl_fixed_to_double(dx_unaccel_w);
+        dy = wl_fixed_to_double(dy_unaccel_w);
+    } else {
+        dx = wl_fixed_to_double(dx_w) * window->pointer_scale.x;
+        dy = wl_fixed_to_double(dy_w) * window->pointer_scale.y;
     }
     }
+
+    SDL_SendMouseMotion(timestamp, window->sdlwindow, seat->pointer.sdl_id, true, (float)dx, (float)dy);
 }
 }
 
 
 static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
 static const struct zwp_relative_pointer_v1_listener relative_pointer_listener = {
@@ -2077,26 +2070,6 @@ static const struct wl_keyboard_listener keyboard_listener = {
     keyboard_handle_repeat_info, // Version 4
     keyboard_handle_repeat_info, // Version 4
 };
 };
 
 
-static void Wayland_SeatCreateRelativePointer(SDL_WaylandSeat *seat)
-{
-    if (seat->display->relative_pointer_manager) {
-        if (seat->pointer.wl_pointer && !seat->pointer.relative_pointer) {
-            seat->pointer.relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(seat->display->relative_pointer_manager, seat->pointer.wl_pointer);
-            zwp_relative_pointer_v1_add_listener(seat->pointer.relative_pointer,
-                                                 &relative_pointer_listener,
-                                                 seat);
-        }
-    }
-}
-
-void Wayland_DisplayInitRelativePointerManager(SDL_VideoData *display)
-{
-    SDL_WaylandSeat *seat;
-    wl_list_for_each(seat, &display->seat_list, link) {
-        Wayland_SeatCreateRelativePointer(seat);
-    }
-}
-
 static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event)
 static void Wayland_SeatDestroyPointer(SDL_WaylandSeat *seat, bool send_event)
 {
 {
     // Make sure focus is removed from a surface before the pointer is destroyed.
     // Make sure focus is removed from a surface before the pointer is destroyed.
@@ -2239,8 +2212,6 @@ static void seat_handle_capabilities(void *data, struct wl_seat *wl_seat, enum w
         wl_pointer_set_user_data(seat->pointer.wl_pointer, seat);
         wl_pointer_set_user_data(seat->pointer.wl_pointer, seat);
         wl_pointer_add_listener(seat->pointer.wl_pointer, &pointer_listener, seat);
         wl_pointer_add_listener(seat->pointer.wl_pointer, &pointer_listener, seat);
 
 
-        Wayland_SeatCreateRelativePointer(seat);
-
         seat->pointer.sdl_id = SDL_GetNextObjectID();
         seat->pointer.sdl_id = SDL_GetNextObjectID();
 
 
         if (seat->name) {
         if (seat->name) {
@@ -3490,16 +3461,36 @@ void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events)
     SDL_free(seat);
     SDL_free(seat);
 }
 }
 
 
-bool Wayland_SeatHasRelativePointerFocus(SDL_WaylandSeat *seat)
+static void Wayland_SeatUpdateRelativePointer(SDL_WaylandSeat *seat)
 {
 {
-    /* If a seat has both keyboard and pointer capabilities, relative focus will follow the keyboard
-     * attached to that seat. Otherwise, relative focus will be gained if any other seat has keyboard
-     * focus on the window with pointer focus.
-     */
-    if (seat->keyboard.wl_keyboard) {
-        return seat->keyboard.focus && seat->keyboard.focus == seat->pointer.focus;
-    } else {
-        return seat->pointer.focus && seat->pointer.focus->keyboard_focus_count != 0;
+    if (seat->display->relative_pointer_manager) {
+        bool relative_focus = false;
+
+        if (seat->pointer.focus) {
+            /* If a seat has both keyboard and pointer capabilities, relative focus will follow the keyboard
+             * attached to that seat. Otherwise, relative focus will be gained if any other seat has keyboard
+             * focus on the window with pointer focus.
+             */
+            if (seat->pointer.focus->sdlwindow->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE) {
+                if (seat->keyboard.wl_keyboard) {
+                    relative_focus = seat->keyboard.focus == seat->pointer.focus;
+                } else {
+                    relative_focus = seat->pointer.focus->keyboard_focus_count != 0;
+                }
+            } else {
+                relative_focus = SDL_GetMouse()->warp_emulation_active;
+            }
+        }
+
+        if (relative_focus) {
+            if (!seat->pointer.relative_pointer) {
+                seat->pointer.relative_pointer = zwp_relative_pointer_manager_v1_get_relative_pointer(seat->display->relative_pointer_manager, seat->pointer.wl_pointer);
+                zwp_relative_pointer_v1_add_listener(seat->pointer.relative_pointer, &relative_pointer_listener, seat);
+            }
+        } else if (seat->pointer.relative_pointer) {
+            zwp_relative_pointer_v1_destroy(seat->pointer.relative_pointer);
+            seat->pointer.relative_pointer = NULL;
+        }
     }
     }
 }
 }
 
 
@@ -3532,11 +3523,10 @@ static void Wayland_SeatUpdateKeyboardGrab(SDL_WaylandSeat *seat)
 void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
 void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
 {
 {
     SDL_VideoData *display = seat->display;
     SDL_VideoData *display = seat->display;
+    Wayland_SeatUpdateRelativePointer(seat);
 
 
     if (display->pointer_constraints) {
     if (display->pointer_constraints) {
-        const bool has_relative_focus = Wayland_SeatHasRelativePointerFocus(seat);
-
-        if (seat->pointer.locked_pointer && (!display->relative_mode_enabled || !has_relative_focus)) {
+        if (seat->pointer.locked_pointer && !seat->pointer.relative_pointer) {
             zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer);
             zwp_locked_pointer_v1_destroy(seat->pointer.locked_pointer);
             seat->pointer.locked_pointer = NULL;
             seat->pointer.locked_pointer = NULL;
 
 
@@ -3546,7 +3536,7 @@ void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat)
 
 
         if (seat->pointer.wl_pointer) {
         if (seat->pointer.wl_pointer) {
             // If relative mode is active, and the pointer focus matches the keyboard focus, lock it.
             // If relative mode is active, and the pointer focus matches the keyboard focus, lock it.
-            if (seat->display->relative_mode_enabled && has_relative_focus) {
+            if (seat->pointer.relative_pointer) {
                 if (!seat->pointer.locked_pointer) {
                 if (!seat->pointer.locked_pointer) {
                     // Creating a lock on a surface with an active confinement region on the same seat is a protocol error.
                     // Creating a lock on a surface with an active confinement region on the same seat is a protocol error.
                     if (seat->pointer.confined_pointer) {
                     if (seat->pointer.confined_pointer) {

+ 0 - 2
src/video/wayland/SDL_waylandevents_c.h

@@ -191,7 +191,6 @@ extern int Wayland_WaitEventTimeout(SDL_VideoDevice *_this, Sint64 timeoutNS);
 
 
 extern void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitInputTimestampManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitCursorShapeManager(SDL_VideoData *display);
-extern void Wayland_DisplayInitRelativePointerManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitTabletManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitTabletManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitDataDeviceManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display);
 extern void Wayland_DisplayInitPrimarySelectionDeviceManager(SDL_VideoData *display);
@@ -201,7 +200,6 @@ extern void Wayland_DisplayCreateTextInputManager(SDL_VideoData *d, uint32_t id)
 extern void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat, Uint32 id);
 extern void Wayland_DisplayCreateSeat(SDL_VideoData *display, struct wl_seat *wl_seat, Uint32 id);
 extern void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events);
 extern void Wayland_SeatDestroy(SDL_WaylandSeat *seat, bool send_events);
 
 
-extern bool Wayland_SeatHasRelativePointerFocus(SDL_WaylandSeat *seat);
 extern void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat);
 extern void Wayland_SeatUpdatePointerGrab(SDL_WaylandSeat *seat);
 extern void Wayland_DisplayUpdatePointerGrabs(SDL_VideoData *display, SDL_WindowData *window);
 extern void Wayland_DisplayUpdatePointerGrabs(SDL_VideoData *display, SDL_WindowData *window);
 extern void Wayland_DisplayUpdateKeyboardGrabs(SDL_VideoData *display, SDL_WindowData *window);
 extern void Wayland_DisplayUpdateKeyboardGrabs(SDL_VideoData *display, SDL_WindowData *window);

+ 4 - 8
src/video/wayland/SDL_waylandmouse.c

@@ -881,8 +881,7 @@ static bool Wayland_WarpMouseRelative(SDL_Window *window, float x, float y)
 
 
     if (d->pointer_constraints) {
     if (d->pointer_constraints) {
         wl_list_for_each (seat, &d->seat_list, link) {
         wl_list_for_each (seat, &d->seat_list, link) {
-            if (wind == seat->pointer.focus ||
-                (!seat->pointer.focus && wind == seat->keyboard.focus)) {
+            if (wind == seat->pointer.focus) {
                 Wayland_SeatWarpMouse(seat, wind, x, y);
                 Wayland_SeatWarpMouse(seat, wind, x, y);
             }
             }
         }
         }
@@ -939,7 +938,7 @@ static bool Wayland_SetRelativeMouseMode(bool enabled)
         return SDL_SetError("Failed to enable relative mode: compositor lacks support for the required zwp_pointer_constraints_v1 protocol");
         return SDL_SetError("Failed to enable relative mode: compositor lacks support for the required zwp_pointer_constraints_v1 protocol");
     }
     }
 
 
-    data->relative_mode_enabled = enabled;
+    // Windows have a relative mode flag, so just update the grabs on a state change.
     Wayland_DisplayUpdatePointerGrabs(data, NULL);
     Wayland_DisplayUpdatePointerGrabs(data, NULL);
     return true;
     return true;
 }
 }
@@ -1122,13 +1121,10 @@ void Wayland_SeatUpdateCursor(SDL_WaylandSeat *seat)
     SDL_WindowData *pointer_focus = seat->pointer.focus;
     SDL_WindowData *pointer_focus = seat->pointer.focus;
 
 
     if (pointer_focus && mouse->cursor_visible) {
     if (pointer_focus && mouse->cursor_visible) {
-        const bool has_relative_focus = Wayland_SeatHasRelativePointerFocus(seat);
-
-        if (!seat->display->relative_mode_enabled || !has_relative_focus || !mouse->relative_mode_hide_cursor) {
+        if (!seat->pointer.relative_pointer || !mouse->relative_mode_hide_cursor) {
             const SDL_HitTestResult rc = pointer_focus->hit_test_result;
             const SDL_HitTestResult rc = pointer_focus->hit_test_result;
 
 
-            if ((seat->display->relative_mode_enabled && has_relative_focus) ||
-                rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
+            if (seat->pointer.relative_pointer || rc == SDL_HITTEST_NORMAL || rc == SDL_HITTEST_DRAGGABLE) {
                 Wayland_SeatSetCursor(seat, mouse->cur_cursor);
                 Wayland_SeatSetCursor(seat, mouse->cur_cursor);
             } else {
             } else {
                 Wayland_SeatSetCursor(seat, sys_cursors[rc]);
                 Wayland_SeatSetCursor(seat, sys_cursors[rc]);

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

@@ -1267,7 +1267,6 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
         d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
         d->shm = wl_registry_bind(registry, id, &wl_shm_interface, 1);
     } else if (SDL_strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
     } else if (SDL_strcmp(interface, "zwp_relative_pointer_manager_v1") == 0) {
         d->relative_pointer_manager = wl_registry_bind(d->registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
         d->relative_pointer_manager = wl_registry_bind(d->registry, id, &zwp_relative_pointer_manager_v1_interface, 1);
-        Wayland_DisplayInitRelativePointerManager(d);
     } else if (SDL_strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
     } else if (SDL_strcmp(interface, "zwp_pointer_constraints_v1") == 0) {
         d->pointer_constraints = wl_registry_bind(d->registry, id, &zwp_pointer_constraints_v1_interface, 1);
         d->pointer_constraints = wl_registry_bind(d->registry, id, &zwp_pointer_constraints_v1_interface, 1);
     } else if (SDL_strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) {
     } else if (SDL_strcmp(interface, "zwp_keyboard_shortcuts_inhibit_manager_v1") == 0) {

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

@@ -96,9 +96,7 @@ struct SDL_VideoData
     int output_count;
     int output_count;
     int output_max;
     int output_max;
 
 
-    bool relative_mode_enabled;
     bool display_externally_owned;
     bool display_externally_owned;
-
     bool scale_to_display_enabled;
     bool scale_to_display_enabled;
 };
 };
 
 

+ 1 - 1
src/video/wayland/SDL_waylandwindow.c

@@ -2209,7 +2209,7 @@ static const struct xdg_activation_token_v1_listener activation_listener_xdg = {
  *
  *
  * As you might expect from Wayland, the general policy is to go with #2 unless
  * As you might expect from Wayland, the general policy is to go with #2 unless
  * the client can prove to the compositor beyond a reasonable doubt that raising
  * the client can prove to the compositor beyond a reasonable doubt that raising
- * the window will not be malicuous behavior.
+ * the window will not be malicious behavior.
  *
  *
  * For SDL this means RaiseWindow and FlashWindow both use the same protocol,
  * For SDL this means RaiseWindow and FlashWindow both use the same protocol,
  * but in different ways: RaiseWindow will provide as _much_ information as
  * but in different ways: RaiseWindow will provide as _much_ information as