From 345cab1e3625d4797483bc6850195506114c371f Mon Sep 17 00:00:00 2001
From: expikr <[EMAIL REDACTED]>
Date: Thu, 19 Dec 2024 09:25:06 +0800
Subject: [PATCH] streamline cursor clipping logic on windows (#11237)
This commit does the following:
- add logic in the `WM_MOUSEMOVE` case of the Window to conditionally call `WIN_UpdateClipCursor` upon receiving cursor motion if SDL is expecting the mouse to be clipped in some way (Fixes #7890)
- remove Windows-specific periodic refresh of cursor clipping and its `SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL` hint (superceded by the above bullet point)
- streamline the processing logic within `WIN_UpdateClipCursor` for better readability of each branch, and avoid calling the Platform API until it is absolutely necessary.
- move `relative_mouse_center` field from Windows-specific per-window `SDL_WindowData` to the global `SDL_Mouse` struct, and the corresponding hint callbacks to `SDL_mouse.c` instead of `SDL_windowswindow.c`
---
include/SDL3/SDL_hints.h | 17 ---
src/events/SDL_mouse.c | 30 ++--
src/events/SDL_mouse_c.h | 2 +-
src/video/windows/SDL_windowsevents.c | 60 ++++----
src/video/windows/SDL_windowswindow.c | 195 +++++++++++++-------------
src/video/windows/SDL_windowswindow.h | 6 +-
6 files changed, 144 insertions(+), 166 deletions(-)
diff --git a/include/SDL3/SDL_hints.h b/include/SDL3/SDL_hints.h
index 868a93ee3e14f..1be6d0a1e69f4 100644
--- a/include/SDL3/SDL_hints.h
+++ b/include/SDL3/SDL_hints.h
@@ -2614,23 +2614,6 @@ extern "C" {
*/
#define SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE "SDL_MOUSE_RELATIVE_CURSOR_VISIBLE"
-/**
- * Controls how often SDL issues cursor confinement commands to the operating
- * system while relative mode is active, in case the desired confinement state
- * became out-of-sync due to interference from other running programs.
- *
- * The variable can be integers representing milliseconds between each
- * refresh. A value of zero means SDL will not automatically refresh the
- * confinement. The default value varies depending on the operating system,
- * this variable might not have any effects on inapplicable platforms such as
- * those without a cursor.
- *
- * This hint can be set anytime.
- *
- * \since This hint is available since SDL 3.1.3.
- */
-#define SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL "SDL_MOUSE_RELATIVE_CLIP_INTERVAL"
-
/**
* A variable controlling whether mouse events should generate synthetic touch
* events.
diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c
index 657bad9a6c237..dea5fc0d03b6a 100644
--- a/src/events/SDL_mouse.c
+++ b/src/events/SDL_mouse.c
@@ -65,17 +65,6 @@ static void SDLCALL SDL_MouseDoubleClickTimeChanged(void *userdata, const char *
}
}
-static void SDLCALL SDL_MouseRelativeClipIntervalChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
-{
- SDL_Mouse *mouse = (SDL_Mouse *)userdata;
-
- if (hint && *hint) {
- mouse->relative_mode_clip_interval = SDL_atoi(hint);
- } else {
- mouse->relative_mode_clip_interval = 3000;
- }
-}
-
static void SDLCALL SDL_MouseDoubleClickRadiusChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
@@ -113,6 +102,13 @@ static void SDLCALL SDL_MouseRelativeSpeedScaleChanged(void *userdata, const cha
}
}
+static void SDLCALL SDL_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
+{
+ SDL_Mouse *mouse = (SDL_Mouse *)userdata;
+
+ mouse->relative_mode_center = SDL_GetStringBoolean(hint, true);
+}
+
static void SDLCALL SDL_MouseRelativeSystemScaleChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
{
SDL_Mouse *mouse = (SDL_Mouse *)userdata;
@@ -226,6 +222,9 @@ bool SDL_PreInitMouse(void)
SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE,
SDL_MouseRelativeSystemScaleChanged, mouse);
+ SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER,
+ SDL_MouseRelativeModeCenterChanged, mouse);
+
SDL_AddHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE,
SDL_MouseWarpEmulationChanged, mouse);
@@ -249,9 +248,6 @@ bool SDL_PreInitMouse(void)
SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE,
SDL_MouseRelativeCursorVisibleChanged, mouse);
- SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL,
- SDL_MouseRelativeClipIntervalChanged, mouse);
-
mouse->was_touch_mouse_events = false; // no touch to mouse movement event pending
mouse->cursor_shown = true;
@@ -1055,6 +1051,9 @@ void SDL_QuitMouse(void)
SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_SYSTEM_SCALE,
SDL_MouseRelativeSystemScaleChanged, mouse);
+ SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER,
+ SDL_MouseRelativeModeCenterChanged, mouse);
+
SDL_RemoveHintCallback(SDL_HINT_MOUSE_EMULATE_WARP_WITH_RELATIVE,
SDL_MouseWarpEmulationChanged, mouse);
@@ -1073,9 +1072,6 @@ void SDL_QuitMouse(void)
SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_CURSOR_VISIBLE,
SDL_MouseRelativeCursorVisibleChanged, mouse);
- SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_CLIP_INTERVAL,
- SDL_MouseRelativeClipIntervalChanged, mouse);
-
for (int i = SDL_mouse_count; i--; ) {
SDL_RemoveMouse(SDL_mice[i].instance_id, false);
}
diff --git a/src/events/SDL_mouse_c.h b/src/events/SDL_mouse_c.h
index d77d0a7e10bf4..5bc9a53755791 100644
--- a/src/events/SDL_mouse_c.h
+++ b/src/events/SDL_mouse_c.h
@@ -98,11 +98,11 @@ typedef struct
bool relative_mode_warp;
bool relative_mode_warp_motion;
bool relative_mode_cursor_visible;
+ bool relative_mode_center;
bool warp_emulation_hint;
bool warp_emulation_active;
bool warp_emulation_prohibited;
Uint64 last_center_warp_time_ns;
- int relative_mode_clip_interval;
bool enable_normal_speed_scale;
float normal_speed_scale;
bool enable_relative_speed_scale;
diff --git a/src/video/windows/SDL_windowsevents.c b/src/video/windows/SDL_windowsevents.c
index 6878bbe1c9ad5..15057e02adcb3 100644
--- a/src/video/windows/SDL_windowsevents.c
+++ b/src/video/windows/SDL_windowsevents.c
@@ -350,8 +350,6 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus)
WIN_UpdateWindowICCProfile(data->window, true);
} else {
- RECT rect;
-
data->in_window_deactivation = true;
SDL_SetKeyboardFocus(NULL);
@@ -361,10 +359,7 @@ static void WIN_UpdateFocus(SDL_Window *window, bool expect_focus)
}
WIN_ResetDeadKeys();
- if (GetClipCursor(&rect) && SDL_memcmp(&rect, &data->cursor_clipped_rect, sizeof(rect)) == 0) {
- ClipCursor(NULL);
- SDL_zero(data->cursor_clipped_rect);
- }
+ WIN_UnclipCursorForWindow(window);
data->in_window_deactivation = false;
}
@@ -1078,6 +1073,10 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_NCACTIVATE:
{
// Don't immediately clip the cursor in case we're clicking minimize/maximize buttons
+ // This is the only place that this flag is set. This causes all subsequent calls to
+ // WIN_UpdateClipCursor for this window to be no-ops in this frame's message-pumping.
+ // This flag is unset at the end of message pumping each frame for every window, and
+ // should never be carried over between frames.
data->skip_update_clipcursor = true;
/* Update the focus here, since it's possible to get WM_ACTIVATE and WM_SETFOCUS without
@@ -1120,7 +1119,18 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
case WM_MOUSEMOVE:
{
- /* SDL_Mouse *mouse = SDL_GetMouse(); */
+ SDL_Window *window = data->window;
+
+ if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
+ bool wish_clip_cursor = (
+ window->flags & (SDL_WINDOW_MOUSE_RELATIVE_MODE | SDL_WINDOW_MOUSE_GRABBED) ||
+ (window->mouse_rect.w > 0 && window->mouse_rect.h > 0)
+ );
+ if (wish_clip_cursor) {
+ data->skip_update_clipcursor = false;
+ WIN_UpdateClipCursor(window);
+ }
+ }
if (!data->mouse_tracked) {
TRACKMOUSEEVENT trackMouseEvent;
@@ -1138,7 +1148,7 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
// Only generate mouse events for real mouse
if (GetMouseMessageSource((ULONG)GetMessageExtraInfo()) != SDL_MOUSE_EVENT_SOURCE_TOUCH &&
lParam != data->last_pointer_update) {
- SDL_SendMouseMotion(WIN_GetEventTimestamp(), data->window, SDL_GLOBAL_MOUSE_ID, false, (float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam));
+ SDL_SendMouseMotion(WIN_GetEventTimestamp(), window, SDL_GLOBAL_MOUSE_ID, false, (float)GET_X_LPARAM(lParam), (float)GET_Y_LPARAM(lParam));
}
}
} break;
@@ -2068,28 +2078,6 @@ LRESULT CALLBACK WIN_WindowProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lPara
}
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
-static void WIN_UpdateClipCursorForWindows(void)
-{
- SDL_VideoDevice *_this = SDL_GetVideoDevice();
- SDL_Window *window;
- Uint64 now = SDL_GetTicks();
- const int CLIPCURSOR_UPDATE_INTERVAL_MS = SDL_GetMouse()->relative_mode_clip_interval;
-
- if (_this) {
- for (window = _this->windows; window; window = window->next) {
- SDL_WindowData *data = window->internal;
- if (data) {
- if (data->skip_update_clipcursor) {
- data->skip_update_clipcursor = false;
- WIN_UpdateClipCursor(window);
- } else if (CLIPCURSOR_UPDATE_INTERVAL_MS > 0 && now >= (data->last_updated_clipcursor + CLIPCURSOR_UPDATE_INTERVAL_MS)) {
- WIN_UpdateClipCursor(window);
- }
- }
- }
- }
-}
-
static void WIN_UpdateMouseCapture(void)
{
SDL_Window *focusWindow = SDL_GetKeyboardFocus();
@@ -2284,7 +2272,17 @@ void WIN_PumpEvents(SDL_VideoDevice *_this)
}
// Update the clipping rect in case someone else has stolen it
- WIN_UpdateClipCursorForWindows();
+ if (_this) {
+ SDL_Window *window = _this->windows;
+ while (window) {
+ SDL_WindowData *data = window->internal;
+ if (data && data->skip_update_clipcursor) {
+ data->skip_update_clipcursor = false;
+ WIN_UpdateClipCursor(window);
+ }
+ window = window->next;
+ }
+ }
// Update mouse capture
WIN_UpdateMouseCapture();
diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c
index d1c8a513915d2..b7a05f6c45b83 100644
--- a/src/video/windows/SDL_windowswindow.c
+++ b/src/video/windows/SDL_windowswindow.c
@@ -386,12 +386,6 @@ bool WIN_SetWindowPositionInternal(SDL_Window *window, UINT flags, SDL_WindowRec
return result;
}
-static void SDLCALL WIN_MouseRelativeModeCenterChanged(void *userdata, const char *name, const char *oldValue, const char *hint)
-{
- SDL_WindowData *data = (SDL_WindowData *)userdata;
- data->mouse_relative_mode_center = SDL_GetStringBoolean(hint, true);
-}
-
static SDL_WindowEraseBackgroundMode GetEraseBackgroundModeHint(void)
{
const char *hint = SDL_GetHint(SDL_HINT_WINDOWS_ERASE_BACKGROUND_MODE);
@@ -443,6 +437,14 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwn
data->dwma_border_color = DWMWA_COLOR_DEFAULT;
data->hint_erase_background_mode = GetEraseBackgroundModeHint();
+
+ // WIN_WarpCursor() jitters by +1, and remote desktop warp wobble is +/- 1
+ LONG remote_desktop_adjustment = GetSystemMetrics(SM_REMOTESESSION) ? 2 : 0;
+ data->cursor_ctrlock_rect.left = 0 - remote_desktop_adjustment;
+ data->cursor_ctrlock_rect.top = 0;
+ data->cursor_ctrlock_rect.right = 1 + remote_desktop_adjustment;
+ data->cursor_ctrlock_rect.bottom = 1;
+
if (SDL_GetHintBoolean("SDL_WINDOW_RETAIN_CONTENT", false)) {
data->copybits_flag = 0;
} else {
@@ -453,8 +455,6 @@ static bool SetupWindowData(SDL_VideoDevice *_this, SDL_Window *window, HWND hwn
SDL_Log("SetupWindowData: initialized data->scaling_dpi to %d", data->scaling_dpi);
#endif
- SDL_AddHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, WIN_MouseRelativeModeCenterChanged, data);
-
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
// Associate the data with the window
if (!SetProp(hwnd, TEXT("SDL_WindowData"), data)) {
@@ -626,7 +626,6 @@ static void CleanupWindowData(SDL_VideoDevice *_this, SDL_Window *window)
SDL_WindowData *data = window->internal;
if (data) {
- SDL_RemoveHintCallback(SDL_HINT_MOUSE_RELATIVE_MODE_CENTER, WIN_MouseRelativeModeCenterChanged, data);
#if !defined(SDL_PLATFORM_XBOXONE) && !defined(SDL_PLATFORM_XBOXSERIES)
if (data->drop_target) {
@@ -1588,107 +1587,109 @@ static BOOL GetClientScreenRect(HWND hwnd, RECT *rect)
ClientToScreen(hwnd, (LPPOINT)rect + 1); // POINT( right , bottom )
}
+void WIN_UnclipCursorForWindow(SDL_Window *window) {
+ SDL_WindowData *data = window->internal;
+ RECT rect;
+ if (GetClipCursor(&rect) && SDL_memcmp(&rect, &data->cursor_clipped_rect, sizeof(rect)) == 0) {
+ ClipCursor(NULL);
+ SDL_zero(data->cursor_clipped_rect);
+ }
+}
+
void WIN_UpdateClipCursor(SDL_Window *window)
{
- SDL_VideoDevice *videodevice = SDL_GetVideoDevice();
SDL_WindowData *data = window->internal;
- SDL_Mouse *mouse = SDL_GetMouse();
- RECT rect, clipped_rect;
-
- if (data->in_title_click || data->focus_click_pending) {
- return;
- }
- if (data->skip_update_clipcursor) {
+ if (data->in_title_click || data->focus_click_pending || data->skip_update_clipcursor) {
return;
}
- if (!GetClipCursor(&clipped_rect)) {
+
+ SDL_Rect mouse_rect = window->mouse_rect;
+ bool win_mouse_rect = (mouse_rect.w > 0 && mouse_rect.h > 0);
+ bool win_have_focus = (window->flags & SDL_WINDOW_INPUT_FOCUS);
+ bool win_is_grabbed = (window->flags & SDL_WINDOW_MOUSE_GRABBED);
+ bool win_in_relmode = (window->flags & SDL_WINDOW_MOUSE_RELATIVE_MODE);
+ bool cursor_confine = win_in_relmode || win_is_grabbed || win_mouse_rect;
+
+ // This is verbatim translation of the old logic,
+ // but I don't quite get what it's trying to do.
+ // A clean-room implementation according to MSDN
+ // documentation of GetClipCursor is provided in
+ // a commented-out block below.
+ if (!win_have_focus || !cursor_confine) {
+ SDL_VideoDevice *videodevice = SDL_GetVideoDevice();
+ RECT current;
+ if (!GetClipCursor(¤t)) {
+ return;
+ }
+ if (videodevice && (
+ current.left != videodevice->desktop_bounds.x ||
+ current.top != videodevice->desktop_bounds.y
+ )) {
+ POINT first, second;
+ first.x = current.left;
+ first.y = current.top;
+ second.x = current.right - 1;
+ second.y = current.bottom - 1;
+ if (!PtInRect(&data->cursor_clipped_rect, first) ||
+ !PtInRect(&data->cursor_clipped_rect, second)) {
+ return;
+ }
+ }
+ ClipCursor(NULL);
+ SDL_zero(data->cursor_clipped_rect);
return;
}
- if ((mouse->relative_mode || (window->flags & SDL_WINDOW_MOUSE_GRABBED) ||
- (window->mouse_rect.w > 0 && window->mouse_rect.h > 0)) &&
- (window->flags & SDL_WINDOW_INPUT_FOCUS)) {
- if (mouse->relative_mode && !mouse->relative_mode_warp && data->mouse_relative_mode_center) {
- if (GetClientScreenRect(data->hwnd, &rect)) {
- // WIN_WarpCursor() jitters by +1, and remote desktop warp wobble is +/- 1
- LONG remote_desktop_adjustment = GetSystemMetrics(SM_REMOTESESSION) ? 2 : 0;
- LONG cx, cy;
-
- cx = (rect.left + rect.right) / 2;
- cy = (rect.top + rect.bottom) / 2;
+ // if (!win_have_focus || !cursor_confine) {
+ // RECT current;
+ // SDL_VideoDevice *videodevice = SDL_GetVideoDevice();
+ // if (GetClipCursor(¤t) && (!videodevice ||
+ // current.left != videodevice->desktop_bounds.x ||
+ // current.top != videodevice->desktop_bounds.y ||
+ // current.right != videodevice->desktop_bounds.x + videodevice->desktop_bounds.w ||
+ // current.bottom != videodevice->desktop_bounds.y + videodevice->desktop_bounds.h )) {
+ // ClipCursor(NULL);
+ // SDL_zero(data->cursor_clipped_rect);
+ // }
+ // return;
+ // }
- // Make an absurdly small clip rect
- rect.left = cx - remote_desktop_adjustment;
- rect.right = cx + 1 + remote_desktop_adjustment;
- rect.top = cy;
- rect.bottom = cy + 1;
+ SDL_Mouse *mouse = SDL_GetMouse();
+ bool lock_to_ctr = (mouse->relative_mode_center && mouse->relative_mode && !mouse->relative_mode_warp);
- if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
- if (ClipCursor(&rect)) {
- data->cursor_clipped_rect = rect;
- }
- }
- }
- } else {
- if (GetClientScreenRect(data->hwnd, &rect)) {
- if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) {
- SDL_Rect mouse_rect_win_client;
- RECT mouse_rect, intersection;
-
- // mouse_rect_win_client is the mouse rect in Windows client space
- mouse_rect_win_client = window->mouse_rect;
-
- // mouse_rect is the rect in Windows screen space
- mouse_rect.left = rect.left + mouse_rect_win_client.x;
- mouse_rect.top = rect.top + mouse_rect_win_client.y;
- mouse_rect.right = mouse_rect.left + mouse_rect_win_client.w;
- mouse_rect.bottom = mouse_rect.top + mouse_rect_win_client.h;
- if (IntersectRect(&intersection, &rect, &mouse_rect)) {
- SDL_memcpy(&rect, &intersection, sizeof(rect));
- } else if (window->flags & SDL_WINDOW_MOUSE_GRABBED) {
- // Mouse rect was invalid, just do the normal grab
- } else {
- SDL_zero(rect);
- }
- }
- if (SDL_memcmp(&rect, &clipped_rect, sizeof(rect)) != 0) {
- if (!WIN_IsRectEmpty(&rect)) {
- if (ClipCursor(&rect)) {
- data->cursor_clipped_rect = rect;
- }
- } else {
- ClipCursor(NULL);
- SDL_zero(data->cursor_clipped_rect);
- }
- }
- }
+ RECT client;
+ if (!GetClientScreenRect(data->hwnd, &client)) {
+ return;
+ }
+
+ RECT target = client;
+ if (lock_to_ctr) {
+ LONG cx = (client.left + client.right ) / 2;
+ LONG cy = (client.top + client.bottom) / 2;
+ target = data->cursor_ctrlock_rect;
+ target.left += cx;
+ target.right += cx;
+ target.top += cy;
+ target.bottom += cy;
+ } else if (win_mouse_rect) {
+ RECT custom, overlap;
+ custom.left = client.left + mouse_rect.x;
+ custom.top = client.top + mouse_rect.y;
+ custom.right = client.left + mouse_rect.x + mouse_rect.w;
+ custom.bottom = client.top + mouse_rect.y + mouse_rect.h;
+ if (IntersectRect(&overlap, &client, &custom)) {
+ target = overlap;
+ } else if (!win_is_grabbed) {
+ WIN_UnclipCursorForWindow(window);
+ return;
}
- } else {
- bool unclip_cursor = false;
-
- // If the cursor is clipped to the screen, clear the clip state
- if (!videodevice ||
- (clipped_rect.left == videodevice->desktop_bounds.x &&
- clipped_rect.top == videodevice->desktop_bounds.y)) {
- unclip_cursor = true;
- } else {
- POINT first, second;
+ }
- first.x = clipped_rect.left;
- first.y = clipped_rect.top;
- second.x = clipped_rect.right - 1;
- second.y = clipped_rect.bottom - 1;
- if (PtInRect(&data->cursor_clipped_rect, first) &&
- PtInRect(&data->cursor_clipped_rect, second)) {
- unclip_cursor = true;
- }
- }
- if (unclip_cursor) {
- ClipCursor(NULL);
- SDL_zero(data->cursor_clipped_rect);
- }
+ if (GetClipCursor(&client) &&
+ 0 != SDL_memcmp(&target, &client, sizeof(client)) &&
+ ClipCursor(&target)) {
+ data->cursor_clipped_rect = target; // ClipCursor may fail if rect beyond screen
}
- data->last_updated_clipcursor = SDL_GetTicks();
}
bool WIN_SetWindowHitTest(SDL_Window *window, bool enabled)
diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h
index 9814d10acf455..ddc3ddd6d9797 100644
--- a/src/video/windows/SDL_windowswindow.h
+++ b/src/video/windows/SDL_windowswindow.h
@@ -79,11 +79,10 @@ struct SDL_WindowData
bool in_title_click;
Uint8 focus_click_pending;
bool skip_update_clipcursor;
- Uint64 last_updated_clipcursor;
- bool mouse_relative_mode_center;
bool windowed_mode_was_maximized;
bool in_window_deactivation;
- RECT cursor_clipped_rect;
+ RECT cursor_clipped_rect; // last successfully committed clipping rect for this window
+ RECT cursor_ctrlock_rect; // this is Windows-specific, but probably does not need to be per-window
UINT windowed_mode_corner_rounding;
COLORREF dwma_border_color;
bool mouse_tracked;
@@ -128,6 +127,7 @@ extern bool WIN_SetWindowKeyboardGrab(SDL_VideoDevice *_this, SDL_Window *window
extern void WIN_DestroyWindow(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_OnWindowEnter(SDL_VideoDevice *_this, SDL_Window *window);
extern void WIN_UpdateClipCursor(SDL_Window *window);
+extern void WIN_UnclipCursorForWindow(SDL_Window *window);
extern bool WIN_SetWindowHitTest(SDL_Window *window, bool enabled);
extern void WIN_AcceptDragAndDrop(SDL_Window *window, bool accept);
extern bool WIN_FlashWindow(SDL_VideoDevice *_this, SDL_Window *window, SDL_FlashOperation operation);