SDL: video: Support multiple video driver entries having the same name string

From ca2c9f680be42c4dfa8c0a0db6ed2f51a567182f Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Sat, 30 Mar 2024 12:01:27 -0400
Subject: [PATCH] video: Support multiple video driver entries having the same
 name string

Allow multiple bootstrap entries for a single video driver with the same name, which internally allows preferential and fallback init conditions while hiding the implementation details from applications (e.g. applications will just see "wayland", regardless of whether it's using the preferred or fallback driver list entry).

If a driver is requested, all instances of it in the list will be tried before reporting failure, and client applications programmatically enumerating the video drivers will be presented with a deduplicated list of entries.
---
 src/video/SDL_sysvideo.h             |  2 +-
 src/video/SDL_video.c                | 38 ++++++++++++++++++++++++----
 src/video/wayland/SDL_waylandvideo.c | 15 +++++------
 3 files changed, 41 insertions(+), 14 deletions(-)

diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 9e03a86687ed5..6aeee58fd64ce 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -504,7 +504,7 @@ extern VideoBootStrap KMSDRM_bootstrap;
 extern VideoBootStrap DUMMY_bootstrap;
 extern VideoBootStrap DUMMY_evdev_bootstrap;
 extern VideoBootStrap Wayland_preferred_bootstrap;
-extern VideoBootStrap Wayland_fallback_bootstrap;
+extern VideoBootStrap Wayland_bootstrap;
 extern VideoBootStrap VIVANTE_bootstrap;
 extern VideoBootStrap Emscripten_bootstrap;
 extern VideoBootStrap OFFSCREEN_bootstrap;
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index 353e110403819..578d06a5af1fd 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -70,14 +70,14 @@ static VideoBootStrap *bootstrap[] = {
 #ifdef SDL_VIDEO_DRIVER_COCOA
     &COCOA_bootstrap,
 #endif
+#ifdef SDL_VIDEO_DRIVER_X11
 #ifdef SDL_VIDEO_DRIVER_WAYLAND
     &Wayland_preferred_bootstrap,
 #endif
-#ifdef SDL_VIDEO_DRIVER_X11
     &X11_bootstrap,
 #endif
 #ifdef SDL_VIDEO_DRIVER_WAYLAND
-    &Wayland_fallback_bootstrap,
+    &Wayland_bootstrap,
 #endif
 #ifdef SDL_VIDEO_DRIVER_VIVANTE
     &VIVANTE_bootstrap,
@@ -514,15 +514,41 @@ static int SDL_UninitializedVideo(void)
     return SDL_SetError("Video subsystem has not been initialized");
 }
 
+/* Deduplicated list of video bootstrap drivers. */
+static const VideoBootStrap *deduped_bootstrap[SDL_arraysize(bootstrap) - 1];
+
 int SDL_GetNumVideoDrivers(void)
 {
-    return SDL_arraysize(bootstrap) - 1;
+    static int num_drivers = -1;
+
+    if (num_drivers >= 0) {
+        return num_drivers;
+    }
+
+    num_drivers = 0;
+
+    /* Build a list of unique video drivers. */
+    for (int i = 0; bootstrap[i] != NULL; ++i) {
+        SDL_bool duplicate = SDL_FALSE;
+        for (int j = 0; j < i; ++j) {
+            if (SDL_strcmp(bootstrap[i]->name, bootstrap[j]->name) == 0) {
+                duplicate = SDL_TRUE;
+                break;
+            }
+        }
+
+        if (!duplicate) {
+            deduped_bootstrap[num_drivers++] = bootstrap[i];
+        }
+    }
+
+    return num_drivers;
 }
 
 const char *SDL_GetVideoDriver(int index)
 {
     if (index >= 0 && index < SDL_GetNumVideoDrivers()) {
-        return bootstrap[index]->name;
+        return deduped_bootstrap[index]->name;
     }
     return NULL;
 }
@@ -580,7 +606,9 @@ int SDL_VideoInit(const char *driver_name)
                 if ((driver_attempt_len == SDL_strlen(bootstrap[i]->name)) &&
                     (SDL_strncasecmp(bootstrap[i]->name, driver_attempt, driver_attempt_len) == 0)) {
                     video = bootstrap[i]->create();
-                    break;
+                    if (video) {
+                        break;
+                    }
                 }
             }
 
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 7a5f2cb8747bc..3aa9b8705e18d 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -68,7 +68,6 @@
 #include <libdecor.h>
 #endif
 
-#define WAYLANDVID_PREFERRED_DRIVER_NAME "wayland_preferred"
 #define WAYLANDVID_DRIVER_NAME "wayland"
 
 /* Clamp certain core protocol versions on older versions of libwayland. */
@@ -420,7 +419,7 @@ static SDL_bool Wayland_IsPreferred(struct wl_display *display)
     return preferred_data.has_fifo_v1 && preferred_data.has_commit_timing_v1;
 }
 
-static SDL_VideoDevice *Wayland_CreateDevice(SDL_bool fallback)
+static SDL_VideoDevice *Wayland_CreateDevice(SDL_bool require_preferred_protocols)
 {
     SDL_VideoDevice *device;
     SDL_VideoData *data;
@@ -451,11 +450,11 @@ static SDL_VideoDevice *Wayland_CreateDevice(SDL_bool fallback)
 
     /*
      * If we are checking for preferred Wayland, then let's query for
-     * fifo-v1 and commit-timing-v1's existance so we don't regress
+     * fifo-v1 and commit-timing-v1's existence, so we don't regress
      * GPU-bound performance and frame-pacing by default due to
      * swapchain starvation.
      */
-    if (!fallback && !Wayland_IsPreferred(display)) {
+    if (require_preferred_protocols && !Wayland_IsPreferred(display)) {
         WAYLAND_wl_display_disconnect(display);
         SDL_WAYLAND_UnloadSymbols();
         return NULL;
@@ -596,21 +595,21 @@ static SDL_VideoDevice *Wayland_CreateDevice(SDL_bool fallback)
 
 static SDL_VideoDevice *Wayland_Preferred_CreateDevice(void)
 {
-    return Wayland_CreateDevice(SDL_FALSE);
+    return Wayland_CreateDevice(SDL_TRUE);
 }
 
 static SDL_VideoDevice *Wayland_Fallback_CreateDevice(void)
 {
-    return Wayland_CreateDevice(SDL_TRUE);
+    return Wayland_CreateDevice(SDL_FALSE);
 }
 
 VideoBootStrap Wayland_preferred_bootstrap = {
-    WAYLANDVID_PREFERRED_DRIVER_NAME, "SDL Wayland video driver",
+    WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
     Wayland_Preferred_CreateDevice,
     Wayland_ShowMessageBox
 };
 
-VideoBootStrap Wayland_fallback_bootstrap = {
+VideoBootStrap Wayland_bootstrap = {
     WAYLANDVID_DRIVER_NAME, "SDL Wayland video driver",
     Wayland_Fallback_CreateDevice,
     Wayland_ShowMessageBox