SDL: emscripten, wayland, x11: Share the table of CSS cursor names (5e51a)

From 5e51a37518cf258706a6de38984e7466cd209f79 Mon Sep 17 00:00:00 2001
From: Simon McVittie <[EMAIL REDACTED]>
Date: Fri, 16 Feb 2024 12:43:05 +0000
Subject: [PATCH] emscripten, wayland, x11: Share the table of CSS cursor names

As suggested in #8939.

Signed-off-by: Simon McVittie <smcv@collabora.com>
(cherry picked from commit 271df2fdd8f611bde4b36ed82e8289e8ff2506b6)
---
 src/video/SDL_sysvideo.h                   |  5 ++
 src/video/SDL_video.c                      | 89 ++++++++++++++++++++++
 src/video/emscripten/SDL_emscriptenmouse.c | 44 +----------
 src/video/wayland/SDL_waylandmouse.c       | 18 ++---
 src/video/x11/SDL_x11mouse.c               | 14 +---
 5 files changed, 102 insertions(+), 68 deletions(-)

diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 7dfbe32715c6..344efec489de 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -24,6 +24,7 @@
 #define SDL_sysvideo_h_
 
 #include "SDL_messagebox.h"
+#include "SDL_mouse.h"
 #include "SDL_shape.h"
 #include "SDL_thread.h"
 #include "SDL_metal.h"
@@ -529,6 +530,10 @@ extern int SDL_GetPointDisplayIndex(const SDL_Point *point);
 
 extern int SDL_GL_SwapWindowWithResult(SDL_Window *window);
 
+#if defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND) || defined(SDL_VIDEO_DRIVER_EMSCRIPTEN)
+const char *SDL_GetCSSCursorName(SDL_SystemCursor id, const char **fallback_name);
+#endif
+
 #endif /* SDL_sysvideo_h_ */
 
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 2ad82fafb80a..ee723a57b197 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -4780,4 +4780,93 @@ void SDL_Metal_GetDrawableSize(SDL_Window *window, int *w, int *h)
     }
 }
 
+#if defined(SDL_VIDEO_DRIVER_X11) || defined(SDL_VIDEO_DRIVER_WAYLAND) || defined(SDL_VIDEO_DRIVER_EMSCRIPTEN)
+const char *SDL_GetCSSCursorName(SDL_SystemCursor id, const char **fallback_name)
+{
+    /* Reference: https://www.w3.org/TR/css-ui-4/#cursor */
+    /* Also in: https://www.freedesktop.org/wiki/Specifications/cursor-spec/ */
+    switch (id) {
+    case SDL_SYSTEM_CURSOR_ARROW:
+        return "default";
+
+    case SDL_SYSTEM_CURSOR_IBEAM:
+        return "text";
+
+    case SDL_SYSTEM_CURSOR_WAIT:
+        return "wait";
+
+    case SDL_SYSTEM_CURSOR_CROSSHAIR:
+        return "crosshair";
+
+    case SDL_SYSTEM_CURSOR_WAITARROW:
+        return "progress";
+
+    case SDL_SYSTEM_CURSOR_SIZENWSE:
+        if (fallback_name) {
+            /* only a single arrow */
+            *fallback_name = "nw-resize";
+        }
+        return "nwse-resize";
+
+    case SDL_SYSTEM_CURSOR_SIZENESW:
+        if (fallback_name) {
+            /* only a single arrow */
+            *fallback_name = "ne-resize";
+        }
+        return "nesw-resize";
+
+    case SDL_SYSTEM_CURSOR_SIZEWE:
+        if (fallback_name) {
+            *fallback_name = "col-resize";
+        }
+        return "ew-resize";
+
+    case SDL_SYSTEM_CURSOR_SIZENS:
+        if (fallback_name) {
+            *fallback_name = "row-resize";
+        }
+        return "ns-resize";
+
+    case SDL_SYSTEM_CURSOR_SIZEALL:
+        return "all-scroll";
+
+    case SDL_SYSTEM_CURSOR_NO:
+        return "not-allowed";
+
+    case SDL_SYSTEM_CURSOR_HAND:
+        return "pointer";
+
+#if 0
+    case SDL_SYSTEM_CURSOR_WINDOW_TOPLEFT:
+        return "nw-resize";
+
+    case SDL_SYSTEM_CURSOR_WINDOW_TOP:
+        return "n-resize";
+
+    case SDL_SYSTEM_CURSOR_WINDOW_TOPRIGHT:
+        return "ne-resize";
+
+    case SDL_SYSTEM_CURSOR_WINDOW_RIGHT:
+        return "e-resize";
+
+    case SDL_SYSTEM_CURSOR_WINDOW_BOTTOMRIGHT:
+        return "se-resize";
+
+    case SDL_SYSTEM_CURSOR_WINDOW_BOTTOM:
+        return "s-resize";
+
+    case SDL_SYSTEM_CURSOR_WINDOW_BOTTOMLEFT:
+        return "sw-resize";
+
+    case SDL_SYSTEM_CURSOR_WINDOW_LEFT:
+        return "w-resize";
+#endif
+
+    default:
+        SDL_assert(0);
+        return "default";
+    }
+}
+#endif
+
 /* vi: set ts=4 sw=4 expandtab: */
diff --git a/src/video/emscripten/SDL_emscriptenmouse.c b/src/video/emscripten/SDL_emscriptenmouse.c
index a3553a2fed54..d189e5393cbf 100644
--- a/src/video/emscripten/SDL_emscriptenmouse.c
+++ b/src/video/emscripten/SDL_emscriptenmouse.c
@@ -141,49 +141,7 @@ static SDL_Cursor *Emscripten_CreateCursor(SDL_Surface *surface, int hot_x, int
 
 static SDL_Cursor *Emscripten_CreateSystemCursor(SDL_SystemCursor id)
 {
-    const char *cursor_name = NULL;
-
-    switch (id) {
-    case SDL_SYSTEM_CURSOR_ARROW:
-        cursor_name = "default";
-        break;
-    case SDL_SYSTEM_CURSOR_IBEAM:
-        cursor_name = "text";
-        break;
-    case SDL_SYSTEM_CURSOR_WAIT:
-        cursor_name = "wait";
-        break;
-    case SDL_SYSTEM_CURSOR_CROSSHAIR:
-        cursor_name = "crosshair";
-        break;
-    case SDL_SYSTEM_CURSOR_WAITARROW:
-        cursor_name = "progress";
-        break;
-    case SDL_SYSTEM_CURSOR_SIZENWSE:
-        cursor_name = "nwse-resize";
-        break;
-    case SDL_SYSTEM_CURSOR_SIZENESW:
-        cursor_name = "nesw-resize";
-        break;
-    case SDL_SYSTEM_CURSOR_SIZEWE:
-        cursor_name = "ew-resize";
-        break;
-    case SDL_SYSTEM_CURSOR_SIZENS:
-        cursor_name = "ns-resize";
-        break;
-    case SDL_SYSTEM_CURSOR_SIZEALL:
-        cursor_name = "all-scroll";
-        break;
-    case SDL_SYSTEM_CURSOR_NO:
-        cursor_name = "not-allowed";
-        break;
-    case SDL_SYSTEM_CURSOR_HAND:
-        cursor_name = "pointer";
-        break;
-    default:
-        SDL_assert(0);
-        return NULL;
-    }
+    const char *cursor_name = SDL_GetCSSCursorName(id, NULL);
 
     return Emscripten_CreateCursorFromString(cursor_name, SDL_FALSE);
 }
diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c
index c1f372011a5e..41e5e434801b 100644
--- a/src/video/wayland/SDL_waylandmouse.c
+++ b/src/video/wayland/SDL_waylandmouse.c
@@ -161,6 +161,7 @@ static SDL_bool wayland_get_system_cursor(SDL_VideoData *vdata, Wayland_CursorDa
     struct wl_cursor_theme *theme = NULL;
     struct wl_cursor *cursor;
     const char *cssname = NULL;
+    const char *fallback_name = NULL;
     const char *legacyname = NULL;
 
     char *xcursor_size;
@@ -235,51 +236,39 @@ static SDL_bool wayland_get_system_cursor(SDL_VideoData *vdata, Wayland_CursorDa
     /* Next, find the cursor from the theme... */
     switch (cdata->system_cursor) {
     case SDL_SYSTEM_CURSOR_ARROW:
-        cssname = "default";
         legacyname = "left_ptr";
         break;
     case SDL_SYSTEM_CURSOR_IBEAM:
-        cssname = "text";
         legacyname = "xterm";
         break;
     case SDL_SYSTEM_CURSOR_WAIT:
-        cssname = "wait";
         legacyname = "watch";
         break;
     case SDL_SYSTEM_CURSOR_CROSSHAIR:
-        cssname = "crosshair";
         legacyname = "tcross";
         break;
     case SDL_SYSTEM_CURSOR_WAITARROW:
-        cssname = "progress";
         legacyname = "watch";
         break;
     case SDL_SYSTEM_CURSOR_SIZENWSE:
-        cssname = "nwse-resize";
         legacyname = "top_left_corner";
         break;
     case SDL_SYSTEM_CURSOR_SIZENESW:
-        cssname = "nesw-resize";
         legacyname = "top_right_corner";
         break;
     case SDL_SYSTEM_CURSOR_SIZEWE:
-        cssname = "ew-resize";
         legacyname = "sb_h_double_arrow";
         break;
     case SDL_SYSTEM_CURSOR_SIZENS:
-        cssname = "ns-resize";
         legacyname = "sb_v_double_arrow";
         break;
     case SDL_SYSTEM_CURSOR_SIZEALL:
-        cssname = "all-scroll";
         legacyname = "fleur";
         break;
     case SDL_SYSTEM_CURSOR_NO:
-        cssname = "not-allowed";
         legacyname = "pirate";
         break;
     case SDL_SYSTEM_CURSOR_HAND:
-        cssname = "pointer";
         legacyname = "hand2";
         break;
     default:
@@ -287,7 +276,12 @@ static SDL_bool wayland_get_system_cursor(SDL_VideoData *vdata, Wayland_CursorDa
         return SDL_FALSE;
     }
 
+    cssname = SDL_GetCSSCursorName(cdata->system_cursor, &fallback_name);
+
     cursor = WAYLAND_wl_cursor_theme_get_cursor(theme, cssname);
+    if (!cursor) {
+        cursor = WAYLAND_wl_cursor_theme_get_cursor(theme, fallback_name);
+    }
 
     /* try the legacy name if the fancy new CSS name doesn't work... */
     if (!cursor) {
diff --git a/src/video/x11/SDL_x11mouse.c b/src/video/x11/SDL_x11mouse.c
index 9469494020a1..22878c8f3190 100644
--- a/src/video/x11/SDL_x11mouse.c
+++ b/src/video/x11/SDL_x11mouse.c
@@ -224,7 +224,7 @@ static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id)
 {
     SDL_Cursor *cursor = NULL;
     unsigned int shape = 0;
-    const char *xcursorname = NULL;
+    const char *xcursorname = SDL_GetCSSCursorName(id, NULL);
 
     switch (id) {
     default:
@@ -234,51 +234,39 @@ static SDL_Cursor *X11_CreateSystemCursor(SDL_SystemCursor id)
     /*   http://tronche.com/gui/x/xlib/appendix/b/ */
     case SDL_SYSTEM_CURSOR_ARROW:
         shape = XC_left_ptr;
-        xcursorname = "default";
         break;
     case SDL_SYSTEM_CURSOR_IBEAM:
         shape = XC_xterm;
-        xcursorname = "text";
         break;
     case SDL_SYSTEM_CURSOR_WAIT:
         shape = XC_watch;
-        xcursorname = "wait";
         break;
     case SDL_SYSTEM_CURSOR_CROSSHAIR:
         shape = XC_tcross;
-        xcursorname = "crosshair";
         break;
     case SDL_SYSTEM_CURSOR_WAITARROW:
         shape = XC_watch;
-        xcursorname = "progress";
         break;
     case SDL_SYSTEM_CURSOR_SIZENWSE:
         shape = XC_top_left_corner;
-        xcursorname = "nwse-resize";
         break;
     case SDL_SYSTEM_CURSOR_SIZENESW:
         shape = XC_top_right_corner;
-        xcursorname = "nesw-resize";
         break;
     case SDL_SYSTEM_CURSOR_SIZEWE:
         shape = XC_sb_h_double_arrow;
-        xcursorname = "ew-resize";
         break;
     case SDL_SYSTEM_CURSOR_SIZENS:
         shape = XC_sb_v_double_arrow;
-        xcursorname = "ns-resize";
         break;
     case SDL_SYSTEM_CURSOR_SIZEALL:
         shape = XC_fleur;
-        xcursorname = "all-scroll";
         break;
     case SDL_SYSTEM_CURSOR_NO:
         shape = XC_pirate;
-        xcursorname = "not-allowed";
         break;
     case SDL_SYSTEM_CURSOR_HAND:
         shape = XC_hand2;
-        xcursorname = "pointer";
         break;
     }