SDL: windows: Treat absolute mouse as pen events when SDL_HINT_PEN_MOUSE_EVENTS=0.

From cb6272ed2d894d597f518d5a116a546e2fc5c7cf Mon Sep 17 00:00:00 2001
From: Susko3 <[EMAIL REDACTED]>
Date: Tue, 18 Feb 2025 16:46:41 +0000
Subject: [PATCH] windows: Treat absolute mouse as pen events when
 SDL_HINT_PEN_MOUSE_EVENTS=0.

Some caveats:

- the fake pen will leave proximity only when relative mode is disabled
- unsure if detecting proximity is even possible from raw mouse input

Fixes #12324.
---
 src/video/windows/SDL_windowsevents.c   | 15 ++++++++++++++-
 src/video/windows/SDL_windowsrawinput.c |  5 +++++
 src/video/windows/SDL_windowsvideo.h    |  1 +
 3 files changed, 20 insertions(+), 1 deletion(-)

diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 3f7e5c60085ca..3c8dd7c0d31c8 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -579,6 +579,11 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL
         return;
     }
 
+    SDL_Mouse *mouse = SDL_GetMouse();
+    if (!mouse) {
+        return;
+    }
+
     if (GetMouseMessageSource(rawmouse->ulExtraInformation) != SDL_MOUSE_EVENT_SOURCE_MOUSE ||
         (SDL_TouchDevicesAvailable() && (rawmouse->ulExtraInformation & 0x80) == 0x80)) {
         return;
@@ -648,7 +653,7 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL
                         }
                     }
                 }
-            } else {
+            } else if (mouse->pen_mouse_events) {
                 const int MAXIMUM_TABLET_RELATIVE_MOTION = 32;
                 if (SDL_abs(relX) > MAXIMUM_TABLET_RELATIVE_MOTION ||
                     SDL_abs(relY) > MAXIMUM_TABLET_RELATIVE_MOTION) {
@@ -656,6 +661,14 @@ static void WIN_HandleRawMouseInput(Uint64 timestamp, SDL_VideoData *data, HANDL
                 } else {
                     SDL_SendMouseMotion(timestamp, window, mouseID, true, (float)relX, (float)relY);
                 }
+            } else {
+                int screen_x = virtual_desktop ? GetSystemMetrics(SM_XVIRTUALSCREEN) : 0;
+                int screen_y = virtual_desktop ? GetSystemMetrics(SM_YVIRTUALSCREEN) : 0;
+
+                if (!data->raw_input_fake_pen_id) {
+                    data->raw_input_fake_pen_id = SDL_AddPenDevice(timestamp, "raw mouse input", window, NULL, (void *)(size_t)-1);
+                }
+                SDL_SendPenMotion(timestamp, data->raw_input_fake_pen_id, window, (float)(x + screen_x - window->x), (float)(y + screen_y - window->y));
             }
 
             data->last_raw_mouse_position.x = x;
diff --git a/src/video/windows/SDL_windowsrawinput.c b/src/video/windows/SDL_windowsrawinput.c
index 46da4a158b7ce..a6b79238545d7 100644
--- a/src/video/windows/SDL_windowsrawinput.c
+++ b/src/video/windows/SDL_windowsrawinput.c
@@ -118,6 +118,11 @@ static DWORD WINAPI WIN_RawInputThread(LPVOID param)
         WIN_PollRawInput(_this, poll_start);
     }
 
+    if (_this->internal->raw_input_fake_pen_id) {
+        SDL_RemovePenDevice(0, SDL_GetKeyboardFocus(), _this->internal->raw_input_fake_pen_id);
+        _this->internal->raw_input_fake_pen_id = 0;
+    }
+
     devices[0].dwFlags |= RIDEV_REMOVE;
     devices[0].hwndTarget = NULL;
     devices[1].dwFlags |= RIDEV_REMOVE;
diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h
index a1a5bc608a0d7..92217e6635292 100644
--- a/src/video/windows/SDL_windowsvideo.h
+++ b/src/video/windows/SDL_windowsvideo.h
@@ -594,6 +594,7 @@ struct SDL_VideoData
     bool raw_keyboard_flag_nohotkeys;
     bool pending_E1_key_sequence;
     Uint32 raw_input_enabled;
+    SDL_PenID raw_input_fake_pen_id;
 
     WIN_GameInputData *gameinput_context;