SDL: wayland: Use release for display and seat objects, if available

From 5c27bc81d8f80d4b7c28822cfb0d9f6f31497fa0 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 3 Apr 2023 12:57:36 -0400
Subject: [PATCH] wayland: Use release for display and seat objects, if
 available

wl_seat and wl_output gained release methods, which should be preferred over destroy methods if they are available.

Bumps wl_output to version 3.
---
 src/video/wayland/SDL_waylandevents.c | 24 +++++--
 src/video/wayland/SDL_waylandvideo.c  | 99 +++++++++------------------
 src/video/wayland/SDL_waylandvideo.h  |  4 +-
 3 files changed, 56 insertions(+), 71 deletions(-)

diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index aa376556ff8f..8b56941cb634 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -2730,16 +2730,28 @@ void Wayland_display_destroy_input(SDL_VideoData *d)
     }
 
     if (input->keyboard) {
-        wl_keyboard_destroy(input->keyboard);
+        if (wl_keyboard_get_version(input->keyboard) >= WL_KEYBOARD_RELEASE_SINCE_VERSION) {
+            wl_keyboard_release(input->keyboard);
+        } else {
+            wl_keyboard_destroy(input->keyboard);
+        }
     }
 
     if (input->pointer) {
-        wl_pointer_destroy(input->pointer);
+        if (wl_pointer_get_version(input->pointer) >= WL_POINTER_RELEASE_SINCE_VERSION) {
+            wl_pointer_release(input->pointer);
+        } else {
+            wl_pointer_destroy(input->pointer);
+        }
     }
 
     if (input->touch) {
         SDL_DelTouch(1);
-        wl_touch_destroy(input->touch);
+        if (wl_touch_get_version(input->touch) >= WL_TOUCH_RELEASE_SINCE_VERSION) {
+            wl_touch_release(input->touch);
+        } else {
+            wl_touch_destroy(input->touch);
+        }
     }
 
     if (input->tablet) {
@@ -2747,7 +2759,11 @@ void Wayland_display_destroy_input(SDL_VideoData *d)
     }
 
     if (input->seat) {
-        wl_seat_destroy(input->seat);
+        if (wl_seat_get_version(input->seat) >= WL_SEAT_RELEASE_SINCE_VERSION) {
+            wl_seat_release(input->seat);
+        } else {
+            wl_seat_destroy(input->seat);
+        }
     }
 
     if (input->xkb.compose_state) {
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 5dd2e49e4609..8985593bdf05 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -188,6 +188,7 @@ static SDL_VideoDevice *Wayland_CreateDevice(void)
 
     data->initializing = SDL_TRUE;
     data->display = display;
+    WAYLAND_wl_list_init(&data->output_list);
 
     /* Initialize all variables that we clean on shutdown */
     device = SDL_calloc(1, sizeof(SDL_VideoDevice));
@@ -660,12 +661,12 @@ static const struct wl_output_listener output_listener = {
     display_handle_scale
 };
 
-static int Wayland_add_display(SDL_VideoData *d, uint32_t id)
+static int Wayland_add_display(SDL_VideoData *d, uint32_t id, uint32_t version)
 {
     struct wl_output *output;
     SDL_DisplayData *data;
 
-    output = wl_registry_bind(d->registry, id, &wl_output_interface, 2);
+    output = wl_registry_bind(d->registry, id, &wl_output_interface, version);
     if (output == NULL) {
         return SDL_SetError("Failed to retrieve output.");
     }
@@ -679,17 +680,7 @@ static int Wayland_add_display(SDL_VideoData *d, uint32_t id)
     SDL_WAYLAND_register_output(output);
 
     /* Keep a list of outputs for deferred xdg-output initialization. */
-    if (d->output_list != NULL) {
-        SDL_DisplayData *node = d->output_list;
-
-        while (node->next != NULL) {
-            node = node->next;
-        }
-
-        node->next = data;
-    } else {
-        d->output_list = data;
-    }
+    WAYLAND_wl_list_insert(&d->output_list, &data->link);
 
     if (data->videodata->xdg_output_manager) {
         data->xdg_output = zxdg_output_manager_v1_get_xdg_output(data->videodata->xdg_output_manager, output);
@@ -698,49 +689,34 @@ static int Wayland_add_display(SDL_VideoData *d, uint32_t id)
     return 0;
 }
 
-static void Wayland_free_display(SDL_VideoData *d, uint32_t id)
+static void Wayland_free_display(SDL_VideoDisplay *display)
 {
-    SDL_DisplayID *displays;
-    SDL_VideoDisplay *display;
-    SDL_DisplayData *data;
-    int i;
+    if (display) {
+        SDL_DisplayData *display_data = display->driverdata;
+        int i;
 
-    displays = SDL_GetDisplays(NULL);
-    if (displays) {
-        for (i = 0; displays[i]; ++i) {
-            display = SDL_GetVideoDisplay(displays[i]);
-            data = display->driverdata;
-            if (data->registry_id == id) {
-                if (d->output_list != NULL) {
-                    SDL_DisplayData *node = d->output_list;
-                    if (node == data) {
-                        d->output_list = node->next;
-                    } else {
-                        while (node->next != data && node->next != NULL) {
-                            node = node->next;
-                        }
-                        if (node->next != NULL) {
-                            node->next = node->next->next;
-                        }
-                    }
-                }
-                SDL_DelVideoDisplay(displays[i], SDL_FALSE);
-                if (data->xdg_output) {
-                    zxdg_output_v1_destroy(data->xdg_output);
-                }
-                wl_output_destroy(data->output);
-                SDL_free(data);
-                break;
-            }
+        if (wl_output_get_version(display_data->output) >= WL_OUTPUT_RELEASE_SINCE_VERSION) {
+            wl_output_release(display_data->output);
+        } else {
+            wl_output_destroy(display_data->output);
+        }
+
+        /* Unlink this display. */
+        WAYLAND_wl_list_remove(&display_data->link);
+
+        /* Null the driverdata member of the mode structs, or they will be wrongly freed. */
+        for (i = display->num_fullscreen_modes; i--;) {
+            display->fullscreen_modes[i].driverdata = NULL;
         }
-        SDL_free(displays);
+        display->desktop_mode.driverdata = NULL;
+        SDL_DelVideoDisplay(display->id, SDL_FALSE);
     }
 }
 
 static void Wayland_init_xdg_output(SDL_VideoData *d)
 {
     SDL_DisplayData *node;
-    for (node = d->output_list; node != NULL; node = node->next) {
+    wl_list_for_each(node, &d->output_list, link) {
         node->xdg_output = zxdg_output_manager_v1_get_xdg_output(node->videodata->xdg_output_manager, node->output);
         zxdg_output_v1_add_listener(node->xdg_output, &xdg_output_listener, node);
     }
@@ -795,7 +771,7 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
     if (SDL_strcmp(interface, "wl_compositor") == 0) {
         d->compositor = wl_registry_bind(d->registry, id, &wl_compositor_interface, SDL_min(4, version));
     } else if (SDL_strcmp(interface, "wl_output") == 0) {
-        Wayland_add_display(d, id);
+        Wayland_add_display(d, id, SDL_min(version, 3));
     } else if (SDL_strcmp(interface, "wl_seat") == 0) {
         Wayland_display_add_input(d, id, version);
     } else if (SDL_strcmp(interface, "xdg_wm_base") == 0) {
@@ -856,8 +832,15 @@ static void display_handle_global(void *data, struct wl_registry *registry, uint
 static void display_remove_global(void *data, struct wl_registry *registry, uint32_t id)
 {
     SDL_VideoData *d = data;
+    SDL_DisplayData  *node;
+
     /* We don't get an interface, just an ID, so assume it's a wl_output :shrug: */
-    Wayland_free_display(d, id);
+    wl_list_for_each (node, &d->output_list, link) {
+        if (node->registry_id == id) {
+            Wayland_free_display(SDL_GetVideoDisplay(node->display));
+            break;
+        }
+    }
 }
 
 static const struct wl_registry_listener registry_listener = {
@@ -969,28 +952,14 @@ static int Wayland_GetDisplayBounds(_THIS, SDL_VideoDisplay *display, SDL_Rect *
 static void Wayland_VideoCleanup(_THIS)
 {
     SDL_VideoData *data = _this->driverdata;
-    int i, j;
+    int i;
 
     Wayland_FiniMouse(data);
 
     for (i = _this->num_displays - 1; i >= 0; --i) {
         SDL_VideoDisplay *display = &_this->displays[i];
-
-        if (display->driverdata->xdg_output) {
-            zxdg_output_v1_destroy(display->driverdata->xdg_output);
-        }
-
-        wl_output_destroy(display->driverdata->output);
-        SDL_free(display->driverdata);
-        display->driverdata = NULL;
-
-        for (j = display->num_fullscreen_modes; j--;) {
-            display->fullscreen_modes[j].driverdata = NULL;
-        }
-        display->desktop_mode.driverdata = NULL;
-        SDL_DelVideoDisplay(display->id, SDL_FALSE);
+        Wayland_free_display(display);
     }
-    data->output_list = NULL;
 
     Wayland_display_destroy_input(data);
     Wayland_display_destroy_pointer_constraints(data);
diff --git a/src/video/wayland/SDL_waylandvideo.h b/src/video/wayland/SDL_waylandvideo.h
index 6f70442f2893..b6b94bc9cdd8 100644
--- a/src/video/wayland/SDL_waylandvideo.h
+++ b/src/video/wayland/SDL_waylandvideo.h
@@ -82,7 +82,7 @@ struct SDL_VideoData
     struct xkb_context *xkb_context;
     struct SDL_WaylandInput *input;
     struct SDL_WaylandTabletManager *tablet_manager;
-    SDL_DisplayData *output_list;
+    struct wl_list output_list;
 
 #ifdef SDL_VIDEO_DRIVER_WAYLAND_QT_TOUCH
     struct SDL_WaylandTouch *touch;
@@ -110,7 +110,7 @@ struct SDL_DisplayData
     SDL_DisplayID display;
     SDL_VideoDisplay placeholder;
     int wl_output_done_count;
-    SDL_DisplayData *next;
+    struct wl_list link;
 };
 
 /* Needed here to get wl_surface declaration, fixes GitHub#4594 */