From 3d21b3bc680a9c49f62f6dfc219e2d81fc787ed5 Mon Sep 17 00:00:00 2001 From: Frank Praznik Date: Tue, 17 Mar 2026 12:34:59 -0400 Subject: [PATCH] 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. --- src/video/wayland/SDL_waylandmouse.c | 42 +++++++++++++++++++++++---- src/video/wayland/SDL_waylandmouse.h | 1 + src/video/wayland/SDL_waylandwindow.c | 8 +++++ 3 files changed, 45 insertions(+), 6 deletions(-) diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c index 72b9d14d5e..2ae9a418ae 100644 --- a/src/video/wayland/SDL_waylandmouse.c +++ b/src/video/wayland/SDL_waylandmouse.c @@ -1103,12 +1103,14 @@ static void Wayland_CursorStateSetCursor(SDL_WaylandCursorState *state, const Wa dst_height = dst_width; } 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; @@ -1180,6 +1182,34 @@ static void Wayland_CursorStateResetCursor(SDL_WaylandCursorState *state) 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) { SDL_VideoDevice *vd = SDL_GetVideoDevice(); diff --git a/src/video/wayland/SDL_waylandmouse.h b/src/video/wayland/SDL_waylandmouse.h index 85ba5eef24..1707445a35 100644 --- a/src/video/wayland/SDL_waylandmouse.h +++ b/src/video/wayland/SDL_waylandmouse.h @@ -29,6 +29,7 @@ extern void Wayland_FiniMouse(SDL_VideoData *data); extern void Wayland_SeatUpdatePointerCursor(SDL_WaylandSeat *seat); extern void Wayland_SeatSetDefaultCursor(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_SeatWarpMouse(SDL_WaylandSeat *seat, SDL_WindowData *window, float x, float y); extern void Wayland_CursorStateSetFrameCallback(SDL_WaylandCursorState *state, void *userdata); diff --git a/src/video/wayland/SDL_waylandwindow.c b/src/video/wayland/SDL_waylandwindow.c index d2ad64974d..20da36b633 100644 --- a/src/video/wayland/SDL_waylandwindow.c +++ b/src/video/wayland/SDL_waylandwindow.c @@ -30,6 +30,7 @@ #include "../../core/unix/SDL_appid.h" #include "../SDL_egl_c.h" #include "SDL_waylandevents_c.h" +#include "SDL_waylandmouse.h" #include "SDL_waylandwindow.h" #include "SDL_waylandvideo.h" #include "../../SDL_hints_c.h" @@ -287,6 +288,8 @@ static void ConfigureWindowGeometry(SDL_Window *window) { SDL_WindowData *data = window->internal; 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_height = data->current.pixel_height; int window_width = 0; @@ -566,6 +569,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 * xdg-toplevel windows where the limits should match the window size. */