SDL: wayland: Create the data_device only after both device_manager and input exist.

From 7510245af92c801162cf469cf313d8ebe6cfa298 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Thu, 8 Apr 2021 14:08:35 -0400
Subject: [PATCH] wayland: Create the data_device only after both
 device_manager and input exist.

There is no guarantee on what order the Wayland interfaces will come in, but the
callbacks were assuming that wl_data_device_manager would could before wl_seat.
This would cause certain desktops to not have any data_device to work with,
meaning certain features like the clipboard would silently no-op.
---
 src/video/wayland/SDL_waylandevents.c   | 55 ++++++++++++++++---------
 src/video/wayland/SDL_waylandevents_c.h |  2 +
 src/video/wayland/SDL_waylandvideo.c    |  2 +-
 3 files changed, 39 insertions(+), 20 deletions(-)

diff --git a/src/video/wayland/SDL_waylandevents.c b/src/video/wayland/SDL_waylandevents.c
index 62a2557ec..08c1866ab 100644
--- a/src/video/wayland/SDL_waylandevents.c
+++ b/src/video/wayland/SDL_waylandevents.c
@@ -1087,11 +1087,45 @@ static const struct wl_data_device_listener data_device_listener = {
     data_device_handle_selection
 };
 
+static void
+Wayland_create_data_device(SDL_VideoData *d)
+{
+    SDL_WaylandDataDevice *data_device = NULL;
+
+    data_device = SDL_calloc(1, sizeof *data_device);
+    if (data_device == NULL) {
+        return;
+    }
+
+    data_device->data_device = wl_data_device_manager_get_data_device(
+        d->data_device_manager, d->input->seat
+    );
+    data_device->video_data = d;
+
+    if (data_device->data_device == NULL) {
+        SDL_free(data_device);
+    } else {
+        wl_data_device_set_user_data(data_device->data_device, data_device);
+        wl_data_device_add_listener(data_device->data_device,
+                                    &data_device_listener, data_device);
+        d->input->data_device = data_device;
+    }
+}
+
+void
+Wayland_add_data_device_manager(SDL_VideoData *d, uint32_t id, uint32_t version)
+{
+    d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, SDL_min(3, version));
+
+    if (d->input != NULL) {
+        Wayland_create_data_device(d);
+    }
+}
+
 void
 Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version)
 {
     struct SDL_WaylandInput *input;
-    SDL_WaylandDataDevice *data_device = NULL;
 
     input = SDL_calloc(1, sizeof *input);
     if (input == NULL)
@@ -1104,24 +1138,7 @@ Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version)
     d->input = input;
 
     if (d->data_device_manager != NULL) {
-        data_device = SDL_calloc(1, sizeof *data_device);
-        if (data_device == NULL) {
-            return;
-        }
-
-        data_device->data_device = wl_data_device_manager_get_data_device(
-            d->data_device_manager, input->seat
-        );
-        data_device->video_data = d;
-
-        if (data_device->data_device == NULL) {
-            SDL_free(data_device);
-        } else {
-            wl_data_device_set_user_data(data_device->data_device, data_device);
-            wl_data_device_add_listener(data_device->data_device,
-                                        &data_device_listener, data_device);
-            input->data_device = data_device;
-        }
+        Wayland_create_data_device(d);
     }
 
     wl_seat_add_listener(input->seat, &seat_listener, input);
diff --git a/src/video/wayland/SDL_waylandevents_c.h b/src/video/wayland/SDL_waylandevents_c.h
index 8e3fc998a..720608b12 100644
--- a/src/video/wayland/SDL_waylandevents_c.h
+++ b/src/video/wayland/SDL_waylandevents_c.h
@@ -80,6 +80,8 @@ struct SDL_WaylandInput {
 
 extern void Wayland_PumpEvents(_THIS);
 
+extern void Wayland_add_data_device_manager(SDL_VideoData *d, uint32_t id, uint32_t version);
+
 extern void Wayland_display_add_input(SDL_VideoData *d, uint32_t id, uint32_t version);
 extern void Wayland_display_destroy_input(SDL_VideoData *d);
 
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index e017747a4..0608f0fd6 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -423,7 +423,7 @@ display_handle_global(void *data, struct wl_registry *registry, uint32_t id,
     } else if (strcmp(interface, "zwp_idle_inhibit_manager_v1") == 0) {
         d->idle_inhibit_manager = wl_registry_bind(d->registry, id, &zwp_idle_inhibit_manager_v1_interface, 1);
     } else if (strcmp(interface, "wl_data_device_manager") == 0) {
-        d->data_device_manager = wl_registry_bind(d->registry, id, &wl_data_device_manager_interface, SDL_min(3, version));
+        Wayland_add_data_device_manager(d, id, version);
     } else if (strcmp(interface, "zxdg_decoration_manager_v1") == 0) {
         d->decoration_manager = wl_registry_bind(d->registry, id, &zxdg_decoration_manager_v1_interface, 1);