Browse Source

wayland: Scale custom cursors with the pointer scale

When emulating display modes or using display scaling, custom cursors need to be scaled, or they can appear too large or small relative to the window size.

(cherry picked from commit 3d21b3bc680a9c49f62f6dfc219e2d81fc787ed5)
Frank Praznik 1 day ago
parent
commit
b560f8ab17

+ 36 - 6
src/video/wayland/SDL_waylandmouse.c

@@ -1103,12 +1103,14 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa
 
 
             dst_height = dst_width;
             dst_height = dst_width;
         } else {
         } else {
-            // If viewports aren't available, the scale is always 1.0.
-            state->scale = viddata->viewporter && focus ? focus->scale_factor : 1.0;
-            dst_width = cursor_data->cursor_data.custom.width;
-            dst_height = cursor_data->cursor_data.custom.height;
-            hot_x = cursor_data->cursor_data.custom.hot_x;
-            hot_y = cursor_data->cursor_data.custom.hot_y;
+            /* If viewports aren't available, the scale is always 1.0.
+             * The dimensions are scaled by the pointer scale, so custom cursors will be scaled relative to the window size.
+             */
+            state->scale = viddata->viewporter && focus ? SDL_min(focus->pointer_scale.x, focus->pointer_scale.y) : 1.0;
+            dst_width = SDL_max((int)SDL_lround((double)cursor_data->cursor_data.custom.width / state->scale), 1);
+            dst_height = SDL_max((int)SDL_lround((double)cursor_data->cursor_data.custom.height / state->scale), 1);
+            hot_x = (int)SDL_lround((double)cursor_data->cursor_data.custom.hot_x / state->scale);
+            hot_y = (int)SDL_lround((double)cursor_data->cursor_data.custom.hot_y / state->scale);
         }
         }
 
 
         state->current_cursor = cursor_data;
         state->current_cursor = cursor_data;
@@ -1180,6 +1182,34 @@ static void Wayland_CursorStateResetCursor(SDL_WaylandCursorState *state)
     state->current_frame = -1;
     state->current_frame = -1;
 }
 }
 
 
+void Wayland_DisplayUpdatePointerFocusedScale(SDL_WindowData *updated_window)
+{
+    SDL_VideoData *viddata = updated_window->waylandData;
+    SDL_WaylandSeat *seat;
+    const double new_scale = SDL_min(updated_window->pointer_scale.x, updated_window->pointer_scale.y);
+
+    wl_list_for_each (seat, &viddata->seat_list, link) {
+        if (seat->pointer.focus == updated_window) {
+            SDL_WaylandCursorState *state = &seat->pointer.cursor_state;
+            if (state->current_cursor && !state->current_cursor->is_system_cursor && state->scale != new_scale) {
+                Wayland_CursorStateResetCursor(state);
+                Wayland_SeatUpdatePointerCursor(seat);
+            }
+        }
+
+        SDL_WaylandPenTool *tool;
+        wl_list_for_each (tool, &seat->tablet.tool_list, link) {
+            if (tool->focus == updated_window) {
+                SDL_WaylandCursorState *state = &tool->cursor_state;
+                if (state->current_cursor && !state->current_cursor->is_system_cursor && state->scale != new_scale) {
+                    Wayland_CursorStateResetCursor(&tool->cursor_state);
+                    Wayland_TabletToolUpdateCursor(tool);
+                }
+            }
+        }
+    }
+}
+
 static bool Wayland_ShowCursor(SDL_Cursor *cursor)
 static bool Wayland_ShowCursor(SDL_Cursor *cursor)
 {
 {
     SDL_VideoDevice *vd = SDL_GetVideoDevice();
     SDL_VideoDevice *vd = SDL_GetVideoDevice();

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

@@ -27,6 +27,8 @@
 extern void Wayland_InitMouse(SDL_VideoData *data);
 extern void Wayland_InitMouse(SDL_VideoData *data);
 extern void Wayland_FiniMouse(SDL_VideoData *data);
 extern void Wayland_FiniMouse(SDL_VideoData *data);
 extern void Wayland_SeatUpdatePointerCursor(SDL_WaylandSeat *seat);
 extern void Wayland_SeatUpdatePointerCursor(SDL_WaylandSeat *seat);
+extern void Wayland_SeatResetCursor(SDL_WaylandSeat *seat);
+extern void Wayland_DisplayUpdatePointerFocusedScale(SDL_WindowData *updated_window);
 extern void Wayland_TabletToolUpdateCursor(SDL_WaylandPenTool *tool);
 extern void Wayland_TabletToolUpdateCursor(SDL_WaylandPenTool *tool);
 extern void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float x, float y);
 extern void Wayland_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float x, float y);
 extern void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata);
 extern void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata);

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

@@ -30,6 +30,7 @@
 #include "../../core/unix/SDL_appid.h"
 #include "../../core/unix/SDL_appid.h"
 #include "../SDL_egl_c.h"
 #include "../SDL_egl_c.h"
 #include "SDL_waylandevents_c.h"
 #include "SDL_waylandevents_c.h"
+#include "SDL_waylandmouse.h"
 #include "SDL_waylandwindow.h"
 #include "SDL_waylandwindow.h"
 #include "SDL_waylandvideo.h"
 #include "SDL_waylandvideo.h"
 #include "../../SDL_hints_c.h"
 #include "../../SDL_hints_c.h"
@@ -305,6 +306,8 @@ static void ConfigureWindowGeometry(SDL_Window *window)
 {
 {
     SDL_WindowData *data = window->internal;
     SDL_WindowData *data = window->internal;
     const double scale_factor = GetWindowScale(window);
     const double scale_factor = GetWindowScale(window);
+    const double prev_pointer_scale_x = data->pointer_scale.x;
+    const double prev_pointer_scale_y = data->pointer_scale.y;
     const int old_pixel_width = data->current.pixel_width;
     const int old_pixel_width = data->current.pixel_width;
     const int old_pixel_height = data->current.pixel_height;
     const int old_pixel_height = data->current.pixel_height;
     int window_width, window_height;
     int window_width, window_height;
@@ -429,6 +432,11 @@ static void ConfigureWindowGeometry(SDL_Window *window)
         }
         }
     }
     }
 
 
+    // Update the scale for any focused cursors.
+    if (prev_pointer_scale_x != data->pointer_scale.x || prev_pointer_scale_y != data->pointer_scale.y) {
+        Wayland_DisplayUpdatePointerFocusedScale(data);
+    }
+
     /* Update the min/max dimensions, primarily if the state was changed, and for non-resizable
     /* Update the min/max dimensions, primarily if the state was changed, and for non-resizable
      * xdg-toplevel windows where the limits should match the window size.
      * xdg-toplevel windows where the limits should match the window size.
      */
      */