From 2f41dd7b5c6b79f61fae855f69b8366cb9b2aba5 Mon Sep 17 00:00:00 2001
From: "Ryan C. Gordon" <[EMAIL REDACTED]>
Date: Fri, 7 Nov 2025 10:34:17 -0500
Subject: [PATCH] pen: Better pen position precision on Windows 8 and later.
Fixes #12084.
---
src/video/windows/SDL_windowsevents.c | 31 ++++++++++++++++++++++-----
src/video/windows/SDL_windowsvideo.c | 1 +
src/video/windows/SDL_windowsvideo.h | 1 +
3 files changed, 28 insertions(+), 5 deletions(-)
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 5bbcbf7b1dce8..bde2a53bdeebd 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -1334,12 +1334,33 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
SDL_SendPenTouch(timestamp, pen, window, (pen_info.penFlags & PEN_FLAG_INVERTED) != 0, false);
}
- POINT position;
- position.x = (LONG) GET_X_LPARAM(lParam);
- position.y = (LONG) GET_Y_LPARAM(lParam);
- ScreenToClient(data->hwnd, &position);
+ const POINTER_INFO *pointer_info = &pen_info.pointerInfo;
+ RECT tablet_bounds, tablet_mapping;
+ float fx, fy;
+
+ // try to get a more-precise position than is stored in lParam...GetPointerDeviceRects is available starting in Windows 8.
+ // we might need to cache this somewhere (and if we cache it, we will need to update it if the display changes)...for now we'll see if GetPointerDeviceRect is fast enough.
+ if (!data->videodata->GetPointerDeviceRects || !data->videodata->GetPointerDeviceRects(pointer_info->sourceDevice, &tablet_bounds, &tablet_mapping)) {
+ POINT position = { (LONG) GET_X_LPARAM(lParam), (LONG) GET_Y_LPARAM(lParam) };
+ ScreenToClient(data->hwnd, &position);
+ fx = (float) position.x;
+ fy = (float) position.y;
+ } else {
+ int ix, iy;
+ SDL_GetWindowPosition(window, &ix, &iy);
+ const SDL_FPoint window_pos = { (float) ix, (float) iy };
+
+ const float facX = pointer_info->ptHimetricLocationRaw.x / (float) (tablet_bounds.right );
+ const float facY = pointer_info->ptHimetricLocationRaw.y / (float) (tablet_bounds.bottom);
+
+ const float w = tablet_mapping.right - tablet_mapping.left;
+ const float h = tablet_mapping.bottom - tablet_mapping.top;
+
+ fx = (tablet_mapping.left + (facX * w)) - window_pos.x;
+ fy = (tablet_mapping.top + (facY * h)) - window_pos.y;
+ }
- SDL_SendPenMotion(timestamp, pen, window, (float) position.x, (float) position.y);
+ SDL_SendPenMotion(timestamp, pen, window, fx, fy);
SDL_SendPenButton(timestamp, pen, window, 1, (pen_info.penFlags & PEN_FLAG_BARREL) != 0);
SDL_SendPenButton(timestamp, pen, window, 2, (pen_info.penFlags & PEN_FLAG_ERASER) != 0);
diff --git a/src/video/windows/SDL_windowsvideo.c b/src/video/windows/SDL_windowsvideo.c
index 6b459138f7fa2..3db7614b46391 100644
--- a/src/video/windows/SDL_windowsvideo.c
+++ b/src/video/windows/SDL_windowsvideo.c
@@ -267,6 +267,7 @@ static SDL_VideoDevice *WIN_CreateDevice(void)
data->DisplayConfigGetDeviceInfo = (LONG (WINAPI *)(DISPLAYCONFIG_DEVICE_INFO_HEADER*))SDL_LoadFunction(data->userDLL, "DisplayConfigGetDeviceInfo");
data->GetPointerType = (BOOL (WINAPI *)(UINT32, POINTER_INPUT_TYPE *))SDL_LoadFunction(data->userDLL, "GetPointerType");
data->GetPointerPenInfo = (BOOL (WINAPI *)(UINT32, POINTER_PEN_INFO *))SDL_LoadFunction(data->userDLL, "GetPointerPenInfo");
+ data->GetPointerDeviceRects = (BOOL (WINAPI *)(HANDLE, RECT *, RECT *))SDL_LoadFunction(data->userDLL, "GetPointerDeviceRects");
/* *INDENT-ON* */ // clang-format on
} else {
SDL_ClearError();
diff --git a/src/video/windows/SDL_windowsvideo.h b/src/video/windows/SDL_windowsvideo.h
index 4ed1a42b58065..a1a5bc608a0d7 100644
--- a/src/video/windows/SDL_windowsvideo.h
+++ b/src/video/windows/SDL_windowsvideo.h
@@ -552,6 +552,7 @@ struct SDL_VideoData
/* *INDENT-OFF* */ // clang-format off
BOOL (WINAPI *GetPointerType)(UINT32 pointerId, POINTER_INPUT_TYPE *pointerType);
BOOL (WINAPI *GetPointerPenInfo)(UINT32 pointerId, POINTER_PEN_INFO *penInfo);
+ BOOL (WINAPI *GetPointerDeviceRects)(HANDLE device, RECT *pointerDeviceRect, RECT *displayRect);
/* *INDENT-ON* */ // clang-format on
// DPI functions