SDL: Track unique button states between different mice

From 361b7190df20b8b22e0d4e9ba4a4d7525aa794be Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Fri, 22 Mar 2024 16:30:20 -0700
Subject: [PATCH] Track unique button states between different mice

We were already halfway doing this, but now we make that information available to the Windows driver so it can cache the button state per-mouse
---
 src/events/SDL_mouse.c                | 29 +++++++++++++++++----------
 src/events/SDL_mouse_c.h              |  3 +++
 src/video/windows/SDL_windowsevents.c | 16 +++++++++------
 src/video/windows/SDL_windowswindow.h |  2 +-
 4 files changed, 32 insertions(+), 18 deletions(-)

diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c
index 3346325a218ea..885179c903c7e 100644
--- a/src/events/SDL_mouse.c
+++ b/src/events/SDL_mouse.c
@@ -402,14 +402,21 @@ SDL_Mouse *SDL_GetMouse(void)
     return &SDL_mouse;
 }
 
-static Uint32 GetButtonState(SDL_Mouse *mouse, SDL_bool include_touch)
+Uint32 SDL_GetMouseButtonState(SDL_Mouse *mouse, SDL_MouseID mouseID, SDL_bool include_touch)
 {
     int i;
     Uint32 buttonstate = 0;
 
     for (i = 0; i < mouse->num_sources; ++i) {
-        if (include_touch || mouse->sources[i].mouseID != SDL_TOUCH_MOUSEID) {
-            buttonstate |= mouse->sources[i].buttonstate;
+        if (mouseID == SDL_GLOBAL_MOUSE_ID || mouseID == SDL_TOUCH_MOUSEID) {
+            if (include_touch || mouse->sources[i].mouseID != SDL_TOUCH_MOUSEID) {
+                buttonstate |= mouse->sources[i].buttonstate;
+            }
+        } else {
+            if (mouseID == mouse->sources[i].mouseID) {
+                buttonstate |= mouse->sources[i].buttonstate;
+                break;
+            }
         }
     }
     return buttonstate;
@@ -433,7 +440,7 @@ SDL_Window *SDL_GetMouseFocus(void)
 void SDL_ResetMouse(void)
 {
     SDL_Mouse *mouse = SDL_GetMouse();
-    Uint32 buttonState = GetButtonState(mouse, SDL_FALSE);
+    Uint32 buttonState = SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, SDL_FALSE);
     int i;
 
     for (i = 1; i <= sizeof(buttonState)*8; ++i) {
@@ -441,7 +448,7 @@ void SDL_ResetMouse(void)
             SDL_SendMouseButton(0, mouse->focus, mouse->mouseID, SDL_RELEASED, i);
         }
     }
-    SDL_assert(GetButtonState(mouse, SDL_FALSE) == 0);
+    SDL_assert(SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, SDL_FALSE) == 0);
 }
 #endif /* 0 */
 
@@ -530,7 +537,7 @@ int SDL_SendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_MouseID mouseI
 {
     if (window && !relative) {
         SDL_Mouse *mouse = SDL_GetMouse();
-        if (!SDL_UpdateMouseFocus(window, x, y, GetButtonState(mouse, SDL_TRUE), (mouseID != SDL_TOUCH_MOUSEID))) {
+        if (!SDL_UpdateMouseFocus(window, x, y, SDL_GetMouseButtonState(mouse, mouseID, SDL_TRUE), (mouseID != SDL_TOUCH_MOUSEID))) {
             return 0;
         }
     }
@@ -760,7 +767,7 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_
     }
 
     /* Ignore relative motion positioning the first touch */
-    if (mouseID == SDL_TOUCH_MOUSEID && !GetButtonState(mouse, SDL_TRUE)) {
+    if (mouseID == SDL_TOUCH_MOUSEID && !SDL_GetMouseButtonState(mouse, mouseID, SDL_TRUE)) {
         xrel = 0.0f;
         yrel = 0.0f;
     }
@@ -800,7 +807,7 @@ static int SDL_PrivateSendMouseMotion(Uint64 timestamp, SDL_Window *window, SDL_
         event.motion.which = mouseID;
         /* Set us pending (or clear during a normal mouse movement event) as having triggered */
         mouse->was_touch_mouse_events = (mouseID == SDL_TOUCH_MOUSEID);
-        event.motion.state = GetButtonState(mouse, SDL_TRUE);
+        event.motion.state = SDL_GetMouseButtonState(mouse, mouseID, SDL_TRUE);
         event.motion.x = mouse->x;
         event.motion.y = mouse->y;
         event.motion.xrel = xrel;
@@ -1126,7 +1133,7 @@ Uint32 SDL_GetMouseState(float *x, float *y)
     if (y) {
         *y = mouse->y;
     }
-    return GetButtonState(mouse, SDL_TRUE);
+    return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, SDL_TRUE);
 }
 
 Uint32 SDL_GetRelativeMouseState(float *x, float *y)
@@ -1141,7 +1148,7 @@ Uint32 SDL_GetRelativeMouseState(float *x, float *y)
     }
     mouse->xdelta = 0.0f;
     mouse->ydelta = 0.0f;
-    return GetButtonState(mouse, SDL_TRUE);
+    return SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, SDL_TRUE);
 }
 
 Uint32 SDL_GetGlobalMouseState(float *x, float *y)
@@ -1318,7 +1325,7 @@ int SDL_UpdateMouseCapture(SDL_bool force_release)
 
     if (!force_release) {
         if (SDL_GetMessageBoxCount() == 0 &&
-            (mouse->capture_desired || (mouse->auto_capture && GetButtonState(mouse, SDL_FALSE) != 0))) {
+            (mouse->capture_desired || (mouse->auto_capture && SDL_GetMouseButtonState(mouse, SDL_GLOBAL_MOUSE_ID, SDL_FALSE) != 0))) {
             if (!mouse->relative_mode) {
                 capture_window = mouse->focus;
             }
diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h
index 0603577fabd76..4dc0c11192445 100644
--- a/src/events/SDL_mouse_c.h
+++ b/src/events/SDL_mouse_c.h
@@ -154,6 +154,9 @@ extern void SDL_SetMouseFocus(SDL_Window *window);
 /* Update the mouse capture window */
 extern int SDL_UpdateMouseCapture(SDL_bool force_release);
 
+/* Get the current mouse button state for a mouse */
+Uint32 SDL_GetMouseButtonState(SDL_Mouse *mouse, SDL_MouseID mouseID, SDL_bool include_touch);
+
 /* You can set either a single scale, or a set of {speed, scale} values in sorted order */
 extern int SDL_SetMouseSystemScale(int num_values, const float *values);
 
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index d0efa67d497b1..d6e949418ad98 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -234,7 +234,9 @@ static void WIN_CheckWParamMouseButton(Uint64 timestamp, SDL_bool bwParamMousePr
  */
 static void WIN_CheckWParamMouseButtons(Uint64 timestamp, WPARAM wParam, SDL_WindowData *data, SDL_MouseID mouseID)
 {
-    if (wParam != data->mouse_button_flags) {
+    Uint64 unique_bits = wParam;
+
+    if (unique_bits != data->mouse_button_flags) {
         Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
 
         /* WM_LBUTTONDOWN and friends handle button swapping for us. No need to check SM_SWAPBUTTON here.  */
@@ -244,17 +246,19 @@ static void WIN_CheckWParamMouseButtons(Uint64 timestamp, WPARAM wParam, SDL_Win
         WIN_CheckWParamMouseButton(timestamp, (wParam & MK_XBUTTON1), mouseFlags, SDL_FALSE, data, SDL_BUTTON_X1, mouseID);
         WIN_CheckWParamMouseButton(timestamp, (wParam & MK_XBUTTON2), mouseFlags, SDL_FALSE, data, SDL_BUTTON_X2, mouseID);
 
-        data->mouse_button_flags = wParam;
+        data->mouse_button_flags = unique_bits;
     }
 }
 
 static void WIN_CheckRawMouseButtons(Uint64 timestamp, HANDLE hDevice, ULONG rawButtons, SDL_WindowData *data, SDL_MouseID mouseID)
 {
     // Add a flag to distinguish raw mouse buttons from wParam above
-    rawButtons |= 0x8000000;
+    Uint64 unique_bits = 0x8000000 | (uintptr_t)hDevice;
+    unique_bits <<= 32;
+    unique_bits |= rawButtons;
 
-    if (rawButtons != data->mouse_button_flags) {
-        Uint32 mouseFlags = SDL_GetMouseState(NULL, NULL);
+    if (unique_bits != data->mouse_button_flags) {
+        Uint32 mouseFlags = SDL_GetMouseButtonState(SDL_GetMouse(), mouseID, SDL_FALSE);
         SDL_bool swapButtons = GetSystemMetrics(SM_SWAPBUTTON) != 0;
         if (swapButtons && hDevice == NULL) {
             /* Touchpad, already has buttons swapped */
@@ -290,7 +294,7 @@ static void WIN_CheckRawMouseButtons(Uint64 timestamp, HANDLE hDevice, ULONG raw
         if (rawButtons & RI_MOUSE_BUTTON_5_UP) {
             WIN_CheckWParamMouseButton(timestamp, !(rawButtons & RI_MOUSE_BUTTON_5_UP), mouseFlags, swapButtons, data, SDL_BUTTON_X2, mouseID);
         }
-        data->mouse_button_flags = rawButtons;
+        data->mouse_button_flags = unique_bits;
     }
 }
 
diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h
index e498a92343086..b98eba628f141 100644
--- a/src/video/windows/SDL_windowswindow.h
+++ b/src/video/windows/SDL_windowswindow.h
@@ -52,7 +52,7 @@ struct SDL_WindowData
     HBITMAP hbm;
     WNDPROC wndproc;
     HHOOK keyboard_hook;
-    WPARAM mouse_button_flags;
+    Uint64 mouse_button_flags;
     LPARAM last_pointer_update;
     WCHAR high_surrogate;
     SDL_bool initializing;