SDL: wayland: Prepare cursor implementation for reconnect support

From 571ff1a3a96f03a3c1e2e01479077c5d61eadfa0 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Sun, 30 Oct 2022 00:19:09 -0400
Subject: [PATCH] wayland: Prepare cursor implementation for reconnect support

Co-authored-by: David Edmundson <kde@davidedmundson.co.uk>
---
 src/video/wayland/SDL_waylandmouse.c | 91 ++++++++++++++++++++++++----
 src/video/wayland/SDL_waylandmouse.h |  3 +
 src/video/wayland/SDL_waylandvideo.c |  6 +-
 3 files changed, 86 insertions(+), 14 deletions(-)

diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c
index a5a45e724ea8..32499fdd7ec9 100644
--- a/src/video/wayland/SDL_waylandmouse.c
+++ b/src/video/wayland/SDL_waylandmouse.c
@@ -459,24 +459,34 @@ Wayland_CreateDefaultCursor()
 }
 
 static void
-Wayland_FreeCursor(SDL_Cursor *cursor)
+Wayland_FreeCursorData(Wayland_CursorData *d)
 {
-    Wayland_CursorData *d;
+    if (d->buffer) {
+        if (d->shm_data) {
+            wl_buffer_destroy(d->buffer);
+        }
+        d->buffer = NULL;
+    }
 
-    if (!cursor)
-        return;
+    if (d->surface) {
+        wl_surface_destroy(d->surface);
+        d->surface = NULL;
+    }
+}
 
-    d = cursor->driverdata;
+static void
+Wayland_FreeCursor(SDL_Cursor *cursor)
+{
+    if (!cursor) {
+        return;
+    }
 
     /* Probably not a cursor we own */
-    if (!d)
+    if (!cursor->driverdata) {
         return;
+    }
 
-    if (d->buffer && d->shm_data)
-        wl_buffer_destroy(d->buffer);
-
-    if (d->surface)
-        wl_surface_destroy(d->surface);
+    Wayland_FreeCursorData((Wayland_CursorData *) cursor->driverdata);
 
     /* Not sure what's meant to happen to shm_data */
     SDL_free(cursor->driverdata);
@@ -589,6 +599,65 @@ Wayland_EmulateMouseWarpChanged(void *userdata, const char *name, const char *ol
     input->warp_emulation_prohibited = !SDL_GetStringBoolean(hint, !input->warp_emulation_prohibited);
 }
 
+#if 0 /* TODO RECONNECT: See waylandvideo.c for more information! */
+static void
+Wayland_RecreateCursor(SDL_Cursor *cursor, SDL_VideoData *vdata)
+{
+    Wayland_CursorData *cdata = (Wayland_CursorData *) cursor->driverdata;
+
+    /* Probably not a cursor we own */
+    if (cdata == NULL) {
+        return;
+    }
+
+    Wayland_FreeCursorData(cdata);
+
+    /* We're not currently freeing this, so... yolo? */
+    if (cdata->shm_data != NULL) {
+        void *old_data_pointer = cdata->shm_data;
+        int stride = cdata->w * 4;
+
+        create_buffer_from_shm(cdata, cdata->w, cdata->h, WL_SHM_FORMAT_ARGB8888);
+
+        SDL_memcpy(cdata->shm_data, old_data_pointer, stride * cdata->h);
+
+        cdata->surface = wl_compositor_create_surface(vdata->compositor);
+        wl_surface_set_user_data(cdata->surface, NULL);
+    }
+}
+
+void
+Wayland_RecreateCursors(void)
+{
+    SDL_Cursor *cursor;
+    SDL_Mouse *mouse = SDL_GetMouse();
+    SDL_VideoData *vdata = SDL_GetVideoDevice()->driverdata;
+
+    if (vdata && vdata->cursor_themes) {
+        SDL_free(vdata->cursor_themes);
+        vdata->cursor_themes = NULL;
+        vdata->num_cursor_themes = 0;
+    }
+
+    if (mouse == NULL) {
+        return;
+    }
+
+    for (cursor = mouse->cursors; cursor != NULL; cursor = cursor->next) {
+        Wayland_RecreateCursor(cursor, vdata);
+    }
+    if (mouse->def_cursor) {
+        Wayland_RecreateCursor(mouse->def_cursor, vdata);
+    }
+    if (mouse->cur_cursor) {
+        Wayland_RecreateCursor(mouse->cur_cursor, vdata);
+        if (mouse->cursor_shown) {
+            Wayland_ShowCursor(mouse->cur_cursor);
+        }
+    }
+}
+#endif /* 0 */
+
 void
 Wayland_InitMouse(void)
 {
diff --git a/src/video/wayland/SDL_waylandmouse.h b/src/video/wayland/SDL_waylandmouse.h
index 76b58b5c23a5..00a56af43517 100644
--- a/src/video/wayland/SDL_waylandmouse.h
+++ b/src/video/wayland/SDL_waylandmouse.h
@@ -27,5 +27,8 @@
 
 extern void Wayland_InitMouse(void);
 extern void Wayland_FiniMouse(SDL_VideoData *data);
+#if 0 /* TODO RECONNECT: See waylandvideo.c for more information! */
+extern void Wayland_RecreateCursors(void);
+#endif /* 0 */
 
 #endif
diff --git a/src/video/wayland/SDL_waylandvideo.c b/src/video/wayland/SDL_waylandvideo.c
index 318d96e0884f..eccdd0bbb34c 100644
--- a/src/video/wayland/SDL_waylandvideo.c
+++ b/src/video/wayland/SDL_waylandvideo.c
@@ -1027,7 +1027,7 @@ Wayland_GetDisplayDPI(_THIS, SDL_VideoDisplay * sdl_display, float * ddpi, float
     return driverdata->ddpi != 0.0f ? 0 : SDL_SetError("Couldn't get DPI");
 }
 
-void
+static void
 Wayland_VideoCleanup(_THIS)
 {
     SDL_VideoData *data = _this->driverdata;
@@ -1160,8 +1160,6 @@ Wayland_VideoReconnect(_THIS)
     SDL_GLContext current_ctx = SDL_GL_GetCurrentContext();
     SDL_Window *current_window = SDL_GL_GetCurrentWindow();
 
-    Wayland_FiniMouse(data);
-
     SDL_GL_MakeCurrent(NULL, NULL);
     Wayland_VideoCleanup(_this);
 
@@ -1188,6 +1186,8 @@ Wayland_VideoReconnect(_THIS)
         window = window->next;
     }
 
+    Wayland_RecreateCursors();
+
     if (current_window && current_ctx) {
         SDL_GL_MakeCurrent (current_window, current_ctx);
     }