SDL: wayland: Optimize custom cursor image lookup

From 3a591636142507ce3bfae31c95c3b7bbcac6a95b Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Thu, 30 Oct 2025 11:05:59 -0400
Subject: [PATCH] wayland: Optimize custom cursor image lookup

Store the images in order from smallest to largest to simplify lookup and be able to early-out of the search loop even if there is no exact match for the scale.
---
 src/video/wayland/SDL_waylandevents_c.h |  2 +-
 src/video/wayland/SDL_waylandmouse.c    | 31 +++++++++++--------------
 2 files changed, 15 insertions(+), 18 deletions(-)

diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index 57d58368f22ff..59488f53cb8fe 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -126,7 +126,7 @@ typedef struct SDL_WaylandSeat
     struct wl_list link;
 
     Uint32 last_implicit_grab_serial; // The serial of the last implicit grab event for window activation and selection data.
-    Uint32 registry_id;                        // The ID of the Wayland seat object,
+    Uint32 registry_id;               // The ID of the Wayland seat object,
 
     struct
     {
diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c
index 348e92295cc6e..a6759f6c3b579 100644
--- a/src/video/wayland/SDL_waylandmouse.c
+++ b/src/video/wayland/SDL_waylandmouse.c
@@ -309,23 +309,11 @@ static struct wl_buffer *Wayland_CursorStateGetFrame(SDL_WaylandCursorState *sta
              * desired size are preferred over images that are smaller.
              */
             CustomCursorImage *closest = NULL;
-            int desired_w = (int)SDL_round(data->cursor_data.custom.width * state->scale);
-            int desired_h = (int)SDL_round(data->cursor_data.custom.height * state->scale);
-            int desired_size = desired_w * desired_h;
-            int closest_distance = -1;
-            int closest_size = -1;
-            for (int i = 0; i < data->cursor_data.custom.images_per_frame && closest_distance && data->cursor_data.custom.images[offset + i].buffer; ++i) {
-                CustomCursorImage *candidate = &data->cursor_data.custom.images[offset + i];
-                int size = candidate->width * candidate->height;
-                int delta_w = candidate->width - desired_w;
-                int delta_h = candidate->height - desired_h;
-                int distance = (delta_w * delta_w) + (delta_h * delta_h);
-                if (closest_distance < 0 || distance < closest_distance ||
-                    (size > desired_size && closest_size < desired_size)) {
-                    closest = candidate;
-                    closest_distance = distance;
-                    closest_size = size;
-                }
+            const int target_area = SDL_lround(data->cursor_data.custom.width * data->cursor_data.custom.height * state->scale);
+            int closest_area = 0;
+            for (int i = 0; i < data->cursor_data.custom.images_per_frame && closest_area < target_area && data->cursor_data.custom.images[offset + i].buffer; ++i) {
+                closest = &data->cursor_data.custom.images[offset + i];
+                closest_area = closest->width * closest->height;
             }
 
             return closest ? closest->buffer : NULL;
@@ -732,6 +720,14 @@ static bool Wayland_GetSystemCursor(SDL_CursorData *cdata, SDL_WaylandCursorStat
     return true;
 }
 
+static int surface_sort_callback(const void *a, const void *b)
+{
+    SDL_Surface *s1 = (SDL_Surface *)a;
+    SDL_Surface *s2 = (SDL_Surface *)b;
+
+    return (s1->w * s1->h) <= (s2->w * s2->h) ? -1 : 1;
+}
+
 static SDL_Cursor *Wayland_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int frame_count, int hot_x, int hot_y)
 {
     SDL_Cursor *cursor = SDL_calloc(1, sizeof(*cursor));
@@ -760,6 +756,7 @@ static SDL_Cursor *Wayland_CreateAnimatedCursor(SDL_CursorFrameInfo *frames, int
             goto failed;
         }
 
+        SDL_qsort(surfaces[i].surfaces, surfaces[i].count, sizeof(SDL_Surface *), surface_sort_callback);
         max_images = SDL_max(max_images, surfaces[i].count);
         for (int j = 0; j < surfaces[i].count; ++j) {
             pool_size += surfaces[i].surfaces[j]->w * surfaces[i].surfaces[j]->h * 4;