From fd79607eb077d364b5e46b1e48436cf29c86eee9 Mon Sep 17 00:00:00 2001
From: Sam Lantinga <[EMAIL REDACTED]>
Date: Mon, 8 Nov 2021 21:34:48 -0800
Subject: [PATCH] Added SDL_GetWindowMouseRect()
Also guarantee that we won't get mouse movement outside the confining area, even if the OS implementation allows it (e.g. macOS)
---
include/SDL_video.h | 47 +++++++++++++++++----------
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/events/SDL_mouse.c | 28 +++++++++++++---
src/video/SDL_sysvideo.h | 4 ++-
src/video/SDL_video.c | 25 ++++++++++++--
src/video/cocoa/SDL_cocoawindow.h | 3 +-
src/video/cocoa/SDL_cocoawindow.m | 21 +++---------
src/video/windows/SDL_windowswindow.c | 17 +++-------
src/video/windows/SDL_windowswindow.h | 3 +-
src/video/x11/SDL_x11events.c | 4 +--
src/video/x11/SDL_x11xfixes.c | 21 +++++++++---
src/video/x11/SDL_x11xfixes.h | 2 +-
13 files changed, 111 insertions(+), 66 deletions(-)
diff --git a/include/SDL_video.h b/include/SDL_video.h
index 5f4f4f55fe..fc462d76d8 100644
--- a/include/SDL_video.h
+++ b/include/SDL_video.h
@@ -1381,6 +1381,36 @@ extern DECLSPEC SDL_bool SDLCALL SDL_GetWindowMouseGrab(SDL_Window * window);
*/
extern DECLSPEC SDL_Window * SDLCALL SDL_GetGrabbedWindow(void);
+/**
+ * Confines the cursor to the specified area of a window.
+ *
+ * Note that this does NOT grab the cursor, it only defines the area a cursor
+ * is restricted to when the window has mouse focus.
+ *
+ * \param window The window that will be associated with the barrier.
+ * \param rect A rectangle area in window-relative coordinates. If NULL the
+ * barrier for the specified window will be destroyed.
+ *
+ * \returns 0 on success or a negative error code on failure; call
+ * SDL_GetError() for more information.
+ *
+ * \sa SDL_GetWindowMouseRect
+ * \sa SDL_SetWindowGrab
+ */
+extern DECLSPEC int SDLCALL SDL_SetWindowMouseRect(SDL_Window * window, const SDL_Rect * rect);
+
+/**
+ * Get the mouse confinement rectangle of a window.
+ *
+ * \param window The window to query
+ *
+ * \returns A pointer to the mouse confinement rectangle of a window,
+ * or NULL if there isn't one.
+ *
+ * \sa SDL_SetWindowMouseRect
+ */
+extern DECLSPEC const SDL_Rect * SDLCALL SDL_GetWindowMouseRect(SDL_Window * window);
+
/**
* Set the brightness (gamma multiplier) for a given window's display.
*
@@ -1497,23 +1527,6 @@ extern DECLSPEC int SDLCALL SDL_SetWindowModalFor(SDL_Window * modal_window, SDL
*/
extern DECLSPEC int SDLCALL SDL_SetWindowInputFocus(SDL_Window * window);
-/**
- * Confines the cursor in the specified rect area of a window.
- *
- * Note that this does NOT grab the cursor, it only defines the area a cursor
- * is restricted to when the window has mouse focus.
- *
- * \param window The window that will be associated with the barrier.
- * \param rect A rectangle area in window-relative coordinates. If NULL the
- * barrier for the specified window will be destroyed.
- *
- * \returns 0 on success or a negative error code on failure; call
- * SDL_GetError() for more information.
- *
- * \sa SDL_SetWindowGrab
- */
-extern DECLSPEC int SDLCALL SDL_SetWindowMouseRect(SDL_Window * window, const SDL_Rect * rect);
-
/**
* Set the gamma ramp for the display that owns a given window.
*
diff --git a/src/dynapi/SDL_dynapi_overrides.h b/src/dynapi/SDL_dynapi_overrides.h
index fcad9ba510..20d9e702ed 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -843,3 +843,4 @@
#define SDL_hid_get_serial_number_string SDL_hid_get_serial_number_string_REAL
#define SDL_hid_get_indexed_string SDL_hid_get_indexed_string_REAL
#define SDL_SetWindowMouseRect SDL_SetWindowMouseRect_REAL
+#define SDL_GetWindowMouseRect SDL_GetWindowMouseRect_REAL
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 1a780c80aa..d2c1d13d27 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -912,3 +912,4 @@ SDL_DYNAPI_PROC(int,SDL_hid_get_product_string,(SDL_hid_device *a, wchar_t *b, s
SDL_DYNAPI_PROC(int,SDL_hid_get_serial_number_string,(SDL_hid_device *a, wchar_t *b, size_t c),(a,b,c),return)
SDL_DYNAPI_PROC(int,SDL_hid_get_indexed_string,(SDL_hid_device *a, int b, wchar_t *c, size_t d),(a,b,c,d),return)
SDL_DYNAPI_PROC(int,SDL_SetWindowMouseRect,(SDL_Window *a, const SDL_Rect *b),(a,b),return)
+SDL_DYNAPI_PROC(const SDL_Rect*,SDL_GetWindowMouseRect,(SDL_Window *a),(a),return)
diff --git a/src/events/SDL_mouse.c b/src/events/SDL_mouse.c
index fb8e362274..45fb092f76 100644
--- a/src/events/SDL_mouse.c
+++ b/src/events/SDL_mouse.c
@@ -416,24 +416,42 @@ SDL_PrivateSendMouseMotion(SDL_Window * window, SDL_MouseID mouseID, int relativ
/* make sure that the pointers find themselves inside the windows,
unless we have the mouse captured. */
if (window && ((window->flags & SDL_WINDOW_MOUSE_CAPTURE) == 0)) {
- int x_max = 0, y_max = 0;
+ int x_min = 0, x_max = 0;
+ int y_min = 0, y_max = 0;
+ const SDL_Rect *confine = SDL_GetWindowMouseRect(window);
SDL_GetWindowSize(window, &x_max, &y_max);
--x_max;
--y_max;
+ if (confine) {
+ SDL_Rect window_rect;
+ SDL_Rect mouse_rect;
+
+ window_rect.x = 0;
+ window_rect.y = 0;
+ window_rect.w = x_max + 1;
+ window_rect.h = y_max + 1;
+ if (SDL_IntersectRect(confine, &window_rect, &mouse_rect)) {
+ x_min = mouse_rect.x;
+ y_min = mouse_rect.y;
+ x_max = x_min + mouse_rect.w - 1;
+ y_min = y_min + mouse_rect.h - 1;
+ }
+ }
+
if (mouse->x > x_max) {
mouse->x = x_max;
}
- if (mouse->x < 0) {
- mouse->x = 0;
+ if (mouse->x < x_min) {
+ mouse->x = x_min;
}
if (mouse->y > y_max) {
mouse->y = y_max;
}
- if (mouse->y < 0) {
- mouse->y = 0;
+ if (mouse->y < y_min) {
+ mouse->y = y_min;
}
}
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index ae29dd3fc0..d57731eda4 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -103,6 +103,8 @@ struct SDL_Window
SDL_bool is_destroying;
SDL_bool is_dropping; /* drag/drop in progress, expecting SDL_SendDropComplete(). */
+ SDL_Rect mouse_rect;
+
SDL_WindowShaper *shaper;
SDL_HitTest hit_test;
@@ -235,7 +237,7 @@ struct SDL_VideoDevice
int (*SetWindowGammaRamp) (_THIS, SDL_Window * window, const Uint16 * ramp);
int (*GetWindowGammaRamp) (_THIS, SDL_Window * window, Uint16 * ramp);
void* (*GetWindowICCProfile) (_THIS, SDL_Window * window, size_t* size);
- int (*SetWindowMouseRect)(_THIS, SDL_Window * window, const SDL_Rect * rect);
+ void (*SetWindowMouseRect)(_THIS, SDL_Window * window);
void (*SetWindowMouseGrab) (_THIS, SDL_Window * window, SDL_bool grabbed);
void (*SetWindowKeyboardGrab) (_THIS, SDL_Window * window, SDL_bool grabbed);
void (*DestroyWindow) (_THIS, SDL_Window * window);
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index b6acc670d8..516c183f03 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -2836,10 +2836,29 @@ int
SDL_SetWindowMouseRect(SDL_Window * window, const SDL_Rect * rect)
{
CHECK_WINDOW_MAGIC(window, -1);
- if (!_this->SetWindowMouseRect) {
- return SDL_Unsupported();
+
+ if (rect) {
+ SDL_memcpy(&window->mouse_rect, rect, sizeof(*rect));
+ } else {
+ SDL_zero(window->mouse_rect);
+ }
+
+ if (_this->SetWindowMouseRect) {
+ _this->SetWindowMouseRect(_this, window);
+ }
+ return 0;
+}
+
+const SDL_Rect *
+SDL_GetWindowMouseRect(SDL_Window * window)
+{
+ CHECK_WINDOW_MAGIC(window, NULL);
+
+ if (SDL_RectEmpty(&window->mouse_rect)) {
+ return NULL;
+ } else {
+ return &window->mouse_rect;
}
- return _this->SetWindowMouseRect(_this, window, rect);
}
int
diff --git a/src/video/cocoa/SDL_cocoawindow.h b/src/video/cocoa/SDL_cocoawindow.h
index f7b3626aa4..489db169ff 100644
--- a/src/video/cocoa/SDL_cocoawindow.h
+++ b/src/video/cocoa/SDL_cocoawindow.h
@@ -124,7 +124,6 @@ struct SDL_WindowData
SDL_bool created;
SDL_bool inWindowFullscreenTransition;
NSInteger flash_request;
- SDL_Rect mouse_rect;
Cocoa_WindowListener *listener;
struct SDL_VideoData *videodata;
#if SDL_VIDEO_OPENGL_EGL
@@ -155,7 +154,7 @@ extern void Cocoa_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDispl
extern int Cocoa_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp);
extern void* Cocoa_GetWindowICCProfile(_THIS, SDL_Window * window, size_t * size);
extern int Cocoa_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
-extern int Cocoa_SetWindowMouseRect(_THIS, SDL_Window * window, const SDL_Rect * rect);
+extern void Cocoa_SetWindowMouseRect(_THIS, SDL_Window * window);
extern void Cocoa_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
extern void Cocoa_DestroyWindow(_THIS, SDL_Window * window);
extern SDL_bool Cocoa_GetWindowWMInfo(_THIS, SDL_Window * window, struct SDL_SysWMinfo *info);
diff --git a/src/video/cocoa/SDL_cocoawindow.m b/src/video/cocoa/SDL_cocoawindow.m
index 6f82109900..248607106a 100644
--- a/src/video/cocoa/SDL_cocoawindow.m
+++ b/src/video/cocoa/SDL_cocoawindow.m
@@ -335,7 +335,7 @@ static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r)
return SDL_FALSE;
}
- if ((window->flags & SDL_WINDOW_MOUSE_GRABBED) || (data->mouse_rect.w > 0 && data->mouse_rect.h > 0)) {
+ if ((window->flags & SDL_WINDOW_MOUSE_GRABBED) || (window->mouse_rect.w > 0 && window->mouse_rect.h > 0)) {
return SDL_TRUE;
}
return SDL_FALSE;
@@ -344,9 +344,7 @@ static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r)
static SDL_bool
AdjustCoordinatesForGrab(SDL_Window * window, int x, int y, CGPoint *adjusted)
{
- SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
-
- if (data->mouse_rect.w > 0 && data->mouse_rect.h > 0) {
+ if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) {
SDL_Rect window_rect;
SDL_Rect mouse_rect;
@@ -355,7 +353,7 @@ static void ConvertNSRect(NSScreen *screen, BOOL fullscreen, NSRect *r)
window_rect.w = window->w;
window_rect.h = window->h;
- if (SDL_IntersectRect(&data->mouse_rect, &window_rect, &mouse_rect)) {
+ if (SDL_IntersectRect(&window->mouse_rect, &window_rect, &mouse_rect)) {
int left = window->x + mouse_rect.x;
int right = left + mouse_rect.w - 1;
int top = window->y + mouse_rect.y;
@@ -2093,17 +2091,9 @@ - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
return 0;
}
-int
-Cocoa_SetWindowMouseRect(_THIS, SDL_Window * window, const SDL_Rect * rect)
+void
+Cocoa_SetWindowMouseRect(_THIS, SDL_Window * window)
{
- SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
-
- if (rect) {
- SDL_memcpy(&data->mouse_rect, rect, sizeof(*rect));
- } else {
- SDL_zero(data->mouse_rect);
- }
-
/* Move the cursor to the nearest point in the mouse rect */
if (ShouldAdjustCoordinatesForGrab(window)) {
int x, y;
@@ -2115,7 +2105,6 @@ - (BOOL)acceptsFirstMouse:(NSEvent *)theEvent
CGDisplayMoveCursorToPoint(kCGDirectMainDisplay, cgpoint);
}
}
- return 0;
}
void
diff --git a/src/video/windows/SDL_windowswindow.c b/src/video/windows/SDL_windowswindow.c
index 2e1a52578d..7518a2ad80 100644
--- a/src/video/windows/SDL_windowswindow.c
+++ b/src/video/windows/SDL_windowswindow.c
@@ -814,19 +814,10 @@ void WIN_UngrabKeyboard(SDL_Window *window)
}
}
-int
-WIN_SetWindowMouseRect(_THIS, SDL_Window * window, const SDL_Rect * rect)
+void
+WIN_SetWindowMouseRect(_THIS, SDL_Window * window)
{
- SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
-
- if (rect) {
- SDL_memcpy(&data->mouse_rect, rect, sizeof(*rect));
- } else {
- SDL_zero(data->mouse_rect);
- }
WIN_UpdateClipCursor(window);
-
- return 0;
}
void
@@ -1014,7 +1005,7 @@ WIN_UpdateClipCursor(SDL_Window *window)
}
if ((mouse->relative_mode || (window->flags & SDL_WINDOW_MOUSE_GRABBED) ||
- (data->mouse_rect.w > 0 && data->mouse_rect.h > 0)) &&
+ (window->mouse_rect.w > 0 && window->mouse_rect.h > 0)) &&
(window->flags & SDL_WINDOW_INPUT_FOCUS)) {
if (mouse->relative_mode && !mouse->relative_mode_warp) {
if (GetWindowRect(data->hwnd, &rect)) {
@@ -1039,7 +1030,7 @@ WIN_UpdateClipCursor(SDL_Window *window)
if (GetClientRect(data->hwnd, &rect)) {
ClientToScreen(data->hwnd, (LPPOINT) & rect);
ClientToScreen(data->hwnd, (LPPOINT) & rect + 1);
- if (data->mouse_rect.w > 0 && data->mouse_rect.h > 0) {
+ if (window->mouse_rect.w > 0 && window->mouse_rect.h > 0) {
RECT mouse_rect, intersection;
mouse_rect.left = rect.left + data->mouse_rect.x;
diff --git a/src/video/windows/SDL_windowswindow.h b/src/video/windows/SDL_windowswindow.h
index 3c341d5073..b3715cca38 100644
--- a/src/video/windows/SDL_windowswindow.h
+++ b/src/video/windows/SDL_windowswindow.h
@@ -51,7 +51,6 @@ typedef struct
Uint32 last_updated_clipcursor;
SDL_bool windowed_mode_was_maximized;
SDL_bool in_window_deactivation;
- SDL_Rect mouse_rect;
RECT cursor_clipped_rect;
SDL_Point last_raw_mouse_position;
SDL_bool mouse_tracked;
@@ -82,7 +81,7 @@ extern void WIN_SetWindowFullscreen(_THIS, SDL_Window * window, SDL_VideoDisplay
extern int WIN_SetWindowGammaRamp(_THIS, SDL_Window * window, const Uint16 * ramp);
extern void* WIN_GetWindowICCProfile(_THIS, SDL_Window * window, size_t * size);
extern int WIN_GetWindowGammaRamp(_THIS, SDL_Window * window, Uint16 * ramp);
-extern int WIN_SetWindowMouseRect(_THIS, SDL_Window * window, const SDL_Rect * rect);
+extern void WIN_SetWindowMouseRect(_THIS, SDL_Window * window);
extern void WIN_SetWindowMouseGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
extern void WIN_SetWindowKeyboardGrab(_THIS, SDL_Window * window, SDL_bool grabbed);
extern void WIN_DestroyWindow(_THIS, SDL_Window * window);
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index 57c498d3fc..1bf10d5670 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -1079,7 +1079,7 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
}
#if SDL_VIDEO_DRIVER_X11_XFIXES
- /* Disable confiment if the window gets hidden. */
+ /* Disable confinement if the window gets hidden. */
if (data->pointer_barrier_active == SDL_TRUE) {
X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
}
@@ -1095,7 +1095,7 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
X11_DispatchMapNotify(data);
#if SDL_VIDEO_DRIVER_X11_XFIXES
- /* Enable confiment if it was activated. */
+ /* Enable confinement if it was activated. */
if (data->pointer_barrier_active == SDL_TRUE) {
X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
}
diff --git a/src/video/x11/SDL_x11xfixes.c b/src/video/x11/SDL_x11xfixes.c
index 45d0c6177c..40850dd69b 100644
--- a/src/video/x11/SDL_x11xfixes.c
+++ b/src/video/x11/SDL_x11xfixes.c
@@ -73,10 +73,23 @@ X11_XfixesIsInitialized()
return xfixes_initialized;
}
-int
-X11_SetWindowMouseRect(_THIS, SDL_Window * window, const SDL_Rect * rect)
+void
+X11_SetWindowMouseRect(_THIS, SDL_Window * window)
{
- return X11_ConfineCursorWithFlags(_this, window, rect, 0);
+ if (SDL_RectEmpty(window->mouse_rect)) {
+ X11_ConfineCursorWithFlags(_this, window, NULL, 0);
+ } else {
+ if (window->flags & SDL_WINDOW_INPUT_FOCUS) {
+ X11_ConfineCursorWithFlags(_this, window, &window->mouse_rect, 0);
+ } else {
+ /* Save the state for when we get focus again */
+ SDL_WindowData *wdata = (SDL_WindowData *) window->driverdata;
+
+ SDL_memcpy(&wdata->barrier_rect, &window->mouse_rect, sizeof(wdata->barrier_rect));
+
+ wdata->pointer_barrier_active = SDL_TRUE;
+ }
+ }
}
int
@@ -102,7 +115,7 @@ X11_ConfineCursorWithFlags(_THIS, SDL_Window * window, const SDL_Rect * rect, in
wdata = (SDL_WindowData *) window->driverdata;
/* If user did not specify an area to confine, destroy the barrier that was/is assigned to
- * this window it was assigned*/
+ * this window it was assigned */
if (rect) {
int x1, y1, x2, y2;
SDL_Rect bounds;
diff --git a/src/video/x11/SDL_x11xfixes.h b/src/video/x11/SDL_x11xfixes.h
index e9a464366a..5f1c169183 100644
--- a/src/video/x11/SDL_x11xfixes.h
+++ b/src/video/x11/SDL_x11xfixes.h
@@ -30,7 +30,7 @@
extern void X11_InitXfixes(_THIS);
extern int X11_XfixesIsInitialized(void);
-extern int X11_SetWindowMouseRect(_THIS, SDL_Window * window, const SDL_Rect * rect);
+extern void X11_SetWindowMouseRect(_THIS, SDL_Window * window);
extern int X11_ConfineCursorWithFlags(_THIS, SDL_Window * window, const SDL_Rect * rect, int flags);
extern void X11_DestroyPointerBarrier(_THIS, SDL_Window * window);