SDL: wayland: Implement global mouse coordinate emulation

From 785b7c66365c600fd988964432549cae0040a486 Mon Sep 17 00:00:00 2001
From: Frank Praznik <[EMAIL REDACTED]>
Date: Mon, 8 May 2023 12:34:58 -0400
Subject: [PATCH] wayland: Implement global mouse coordinate emulation

Wayland doesn't support getting the true global cursor position, but it can be faked well enough for what most applications use it for: querying the global cursor coordinates and transforming them to the window-relative coordinates manually.

The global position is derived by taking the cursor position relative to the toplevel window, and offsetting it by the origin of the output the window is currently considered to be on. The cursor position and button state when the cursor is outside an application window are unknown, but this gives 'correct' coordinates when the window has focus, which is good enough for most applications.
---
 src/video/wayland/SDL_waylandmouse.c | 30 ++++++++++++++++++++++++++++
 1 file changed, 30 insertions(+)

diff --git a/src/video/wayland/SDL_waylandmouse.c b/src/video/wayland/SDL_waylandmouse.c
index 51ca06160913..ed8d51cfe88e 100644
--- a/src/video/wayland/SDL_waylandmouse.c
+++ b/src/video/wayland/SDL_waylandmouse.c
@@ -581,6 +581,35 @@ static void SDLCALL Wayland_EmulateMouseWarpChanged(void *userdata, const char *
     input->warp_emulation_prohibited = !SDL_GetStringBoolean(hint, !input->warp_emulation_prohibited);
 }
 
+/* Wayland doesn't support getting the true global cursor position, but it can
+ * be faked well enough for what most applications use it for: querying the
+ * global cursor coordinates and transforming them to the window-relative
+ * coordinates manually.
+ *
+ * The global position is derived by taking the cursor position relative to the
+ * toplevel window, and offsetting it by the origin of the output the window is
+ * currently considered to be on. The cursor position and button state when the
+ * cursor is outside an application window are unknown, but this gives 'correct'
+ * coordinates when the window has focus, which is good enough for most
+ * applications.
+ */
+static Uint32 SDLCALL Wayland_GetGlobalMouseState(float *x, float *y)
+{
+    SDL_Window *focus = SDL_GetMouseFocus();
+    Uint32 ret = 0;
+
+    if (focus) {
+        int off_x, off_y;
+
+        ret = SDL_GetMouseState(x, y);
+        SDL_RelativeToGlobalForWindow(focus, focus->x, focus->y, &off_x, &off_y);
+        *x += off_x;
+        *y += off_y;
+    }
+
+    return ret;
+}
+
 #if 0  /* TODO RECONNECT: See waylandvideo.c for more information! */
 static void Wayland_RecreateCursor(SDL_Cursor *cursor, SDL_VideoData *vdata)
 {
@@ -651,6 +680,7 @@ void Wayland_InitMouse(void)
     mouse->FreeCursor = Wayland_FreeCursor;
     mouse->WarpMouse = Wayland_WarpMouse;
     mouse->SetRelativeMouseMode = Wayland_SetRelativeMouseMode;
+    mouse->GetGlobalMouseState = Wayland_GetGlobalMouseState;
 
     input->relative_mode_override = SDL_FALSE;
     input->cursor_visible = SDL_TRUE;