From 4b42c05ba1eaaaa9a4ef803acea8f13402271039 Mon Sep 17 00:00:00 2001
From: Ethan Lee <[EMAIL REDACTED]>
Date: Mon, 8 Nov 2021 13:52:48 -0500
Subject: [PATCH] video: Add SDL_SetWindowMouseRect.
This API and implementation comes from the Unreal Engine branch of SDL, which
originally called this "SDL_ConfineCursor".
Some minor cleanup and changes for consistency with the rest of SDL_video, but
there are two major changes:
1. The coordinate system has been changed so that `rect` is _window_ relative
and not _screen_ relative, making it easier to implement without having
global access to the display.
2. The UE version unset all rects when passing `NULL` as a parameter for
`window`, this has been removed as it was an unused feature anyhow.
Currently this is only implemented for X, but can be supported on Wayland and
Windows at minimum too.
---
CMakeLists.txt | 2 +-
cmake/sdlchecks.cmake | 13 +-
configure.ac | 31 +++++
include/SDL_config.h.cmake | 2 +
include/SDL_config.h.in | 2 +
include/SDL_test_common.h | 1 +
include/SDL_video.h | 17 +++
src/dynapi/SDL_dynapi_overrides.h | 1 +
src/dynapi/SDL_dynapi_procs.h | 1 +
src/test/SDL_test_common.c | 35 +++++-
src/video/SDL_sysvideo.h | 1 +
src/video/SDL_video.c | 10 ++
src/video/x11/SDL_x11dyn.c | 4 +
src/video/x11/SDL_x11dyn.h | 3 +
src/video/x11/SDL_x11events.c | 32 +++++
src/video/x11/SDL_x11mouse.c | 8 ++
src/video/x11/SDL_x11sym.h | 8 ++
src/video/x11/SDL_x11video.c | 13 ++
src/video/x11/SDL_x11video.h | 3 +
src/video/x11/SDL_x11window.c | 14 +++
src/video/x11/SDL_x11window.h | 5 +
src/video/x11/SDL_x11xfixes.c | 193 ++++++++++++++++++++++++++++++
src/video/x11/SDL_x11xfixes.h | 41 +++++++
23 files changed, 437 insertions(+), 3 deletions(-)
create mode 100644 src/video/x11/SDL_x11xfixes.c
create mode 100644 src/video/x11/SDL_x11xfixes.h
diff --git a/CMakeLists.txt b/CMakeLists.txt
index cf118c2749..bbb2829599 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -392,7 +392,7 @@ set_option(SDL_RPATH "Use an rpath when linking SDL" ${UNIX_SYS})
set_option(SDL_CLOCK_GETTIME "Use clock_gettime() instead of gettimeofday()" OFF)
set_option(SDL_X11 "Use X11 video driver" ${UNIX_SYS})
dep_option(SDL_X11_SHARED "Dynamically load X11 support" ON "SDL_X11" OFF)
-set(SDL_X11_OPTIONS Xcursor Xinerama XInput Xrandr Xscrnsaver XShape Xvm)
+set(SDL_X11_OPTIONS Xcursor Xinerama XInput Xfixes Xrandr Xscrnsaver XShape Xvm)
foreach(_SUB ${SDL_X11_OPTIONS})
string(TOUPPER "SDL_X11_${_SUB}" _OPT)
dep_option(${_OPT} "Enable ${_SUB} support" ON "SDL_X11" OFF)
diff --git a/cmake/sdlchecks.cmake b/cmake/sdlchecks.cmake
index a48132086d..8d8bed9925 100644
--- a/cmake/sdlchecks.cmake
+++ b/cmake/sdlchecks.cmake
@@ -387,7 +387,7 @@ endmacro()
# - HAVE_SDL_LOADSO opt
macro(CheckX11)
if(SDL_X11)
- foreach(_LIB X11 Xext Xcursor Xinerama Xi Xrandr Xrender Xss Xxf86vm)
+ foreach(_LIB X11 Xext Xcursor Xinerama Xi Xfixes Xrandr Xrender Xss Xxf86vm)
FindLibraryAndSONAME("${_LIB}")
endforeach()
@@ -412,6 +412,7 @@ macro(CheckX11)
check_include_file(X11/extensions/Xinerama.h HAVE_XINERAMA_H)
check_include_file(X11/extensions/XInput2.h HAVE_XINPUT2_H)
check_include_file(X11/extensions/Xrandr.h HAVE_XRANDR_H)
+ check_include_file(X11/extensions/Xfixes.h HAVE_XFIXES_H)
check_include_file(X11/extensions/Xrender.h HAVE_XRENDER_H)
check_include_file(X11/extensions/scrnsaver.h HAVE_XSS_H)
check_include_file(X11/extensions/shape.h HAVE_XSHAPE_H)
@@ -524,6 +525,16 @@ macro(CheckX11)
endif()
endif()
+ if(SDL_X11_XFIXES AND HAVE_XFIXES_H)
+ if(HAVE_X11_SHARED AND XFIXES_LIB)
+ set(SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES "\"${XFIXES_LIB_SONAME}\"")
+ else()
+ list(APPEND EXTRA_LIBS ${XFIXES_LIB})
+ endif()
+ set(SDL_VIDEO_DRIVER_X11_XFIXES 1)
+ set(HAVE_VIDEO_X11_XFIXES TRUE)
+ endif()
+
if(SDL_X11_XRANDR AND HAVE_XRANDR_H)
if(HAVE_X11_SHARED AND XRANDR_LIB)
set(SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR "\"${XRANDR_LIB_SONAME}\"")
diff --git a/configure.ac b/configure.ac
index a79190266c..4950eb8abb 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1749,6 +1749,7 @@ CheckX11()
xcursor_lib='/opt/X11/lib/libXcursor.1.dylib'
xinerama_lib='/opt/X11/lib/libXinerama.1.dylib'
xinput_lib='/opt/X11/lib/libXi.6.dylib'
+ xfixes_lib='/opt/X11/lib/libXfixes.3.dylib'
xrandr_lib='/opt/X11/lib/libXrandr.2.dylib'
xrender_lib='/opt/X11/lib/libXrender.1.dylib'
xss_lib='/opt/X11/lib/libXss.1.dylib'
@@ -1760,6 +1761,7 @@ CheckX11()
xcursor_lib='libXcursor.so'
xinerama_lib='libXinerama.so'
xinput_lib='libXi.so'
+ xfixes_lib='libXfixes.so'
xrandr_lib='libXrandr.so'
xrender_lib='libXrender.so'
xss_lib='libXss.so'
@@ -1771,6 +1773,7 @@ CheckX11()
xcursor_lib=[`find_lib "libXcursor.so.*" "$X_LIBS -L/usr/X11/$base_libdir -L/usr/X11R6/$base_libdir" | sed 's/.*\/\(.*\)/\1/; q'`]
xinerama_lib=[`find_lib "libXinerama.so.*" "$X_LIBS -L/usr/X11/$base_libdir -L/usr/X11R6/$base_libdir" | sed 's/.*\/\(.*\)/\1/; q'`]
xinput_lib=[`find_lib "libXi.so.*" "$X_LIBS -L/usr/X11/$base_libdir -L/usr/X11R6/$base_libdir" | sed 's/.*\/\(.*\)/\1/; q'`]
+ xfixes_lib=[`find_lib "libXfixes.so.*" "$X_LIBS -L/usr/X11/$base_libdir -L/usr/X11R6/$base_libdir" | sed 's/.*\/\(.*\)/\1/; q'`]
xrandr_lib=[`find_lib "libXrandr.so.*" "$X_LIBS -L/usr/X11/$base_libdir -L/usr/X11R6/$base_libdir" | sed 's/.*\/\(.*\)/\1/; q'`]
xrender_lib=[`find_lib "libXrender.so.*" "$X_LIBS -L/usr/X11/$base_libdir -L/usr/X11R6/$base_libdir" | sed 's/.*\/\(.*\)/\1/; q'`]
xss_lib=[`find_lib "libXss.so.*" "$X_LIBS -L/usr/X11/$base_libdir -L/usr/X11R6/$base_libdir" | sed 's/.*\/\(.*\)/\1/; q'`]
@@ -1958,6 +1961,34 @@ XITouchClassInfo *t;
],[])
AC_MSG_RESULT($have_xinput2_multitouch)
fi
+ AC_ARG_ENABLE(video-x11-xfixes,
+[AS_HELP_STRING([--enable-video-x11-xfixes], [enable X11 Xfixes support [default=yes]])],
+ , enable_video_x11_xfixes=yes)
+ if test x$enable_video_x11_xfixes = xyes; then
+ definitely_enable_video_x11_xfixes=no
+ AC_CHECK_HEADER(X11/extensions/Xfixes.h,
+ have_xfixes_h_hdr=yes,
+ have_xfixes_h_hdr=no,
+ [#include <X11/Xlib.h>
+ ])
+ if test x$have_xfixes_h_hdr = xyes; then
+ if test x$enable_x11_shared = xyes && test x$xfixes_lib != x ; then
+ echo "-- dynamic libXfixes -> $xfixes_lib"
+ AC_DEFINE_UNQUOTED(SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES, "$xfixes_lib", [ ])
+ definitely_enable_video_x11_xfixes=yes
+ else
+ AC_CHECK_LIB(Xfixes, XFixesCreatePointerBarrier, have_xfixes_lib=yes)
+ if test x$have_xfixes_lib = xyes ; then
+ EXTRA_LDFLAGS="$EXTRA_LDFLAGS -lXfixes"
+ definitely_enable_video_x11_xfixes=yes
+ fi
+ fi
+ fi
+ fi
+ if test x$definitely_enable_video_x11_xfixes = xyes; then
+ AC_DEFINE(SDL_VIDEO_DRIVER_X11_XFIXES, 1, [ ])
+ SUMMARY_video_x11="${SUMMARY_video_x11} xfixes"
+ fi
AC_ARG_ENABLE(video-x11-xrandr,
[AS_HELP_STRING([--enable-video-x11-xrandr], [enable X11 Xrandr extension for fullscreen [default=yes]])],
, enable_video_x11_xrandr=yes)
diff --git a/include/SDL_config.h.cmake b/include/SDL_config.h.cmake
index 0bf1421c9a..673af9599f 100644
--- a/include/SDL_config.h.cmake
+++ b/include/SDL_config.h.cmake
@@ -413,6 +413,7 @@
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR @SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR@
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XINERAMA @SDL_VIDEO_DRIVER_X11_DYNAMIC_XINERAMA@
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 @SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2@
+#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES @SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES@
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR @SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR@
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS @SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS@
#cmakedefine SDL_VIDEO_DRIVER_X11_DYNAMIC_XVIDMODE @SDL_VIDEO_DRIVER_X11_DYNAMIC_XVIDMODE@
@@ -421,6 +422,7 @@
#cmakedefine SDL_VIDEO_DRIVER_X11_XINERAMA @SDL_VIDEO_DRIVER_X11_XINERAMA@
#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2 @SDL_VIDEO_DRIVER_X11_XINPUT2@
#cmakedefine SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH @SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH@
+#cmakedefine SDL_VIDEO_DRIVER_X11_XFIXES @SDL_VIDEO_DRIVER_X11_XFIXES@
#cmakedefine SDL_VIDEO_DRIVER_X11_XRANDR @SDL_VIDEO_DRIVER_X11_XRANDR@
#cmakedefine SDL_VIDEO_DRIVER_X11_XSCRNSAVER @SDL_VIDEO_DRIVER_X11_XSCRNSAVER@
#cmakedefine SDL_VIDEO_DRIVER_X11_XSHAPE @SDL_VIDEO_DRIVER_X11_XSHAPE@
diff --git a/include/SDL_config.h.in b/include/SDL_config.h.in
index 5c5cb29af7..db67826a21 100644
--- a/include/SDL_config.h.in
+++ b/include/SDL_config.h.in
@@ -385,6 +385,7 @@
#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR
#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINERAMA
#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2
+#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES
#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR
#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS
#undef SDL_VIDEO_DRIVER_X11_DYNAMIC_XVIDMODE
@@ -393,6 +394,7 @@
#undef SDL_VIDEO_DRIVER_X11_XINERAMA
#undef SDL_VIDEO_DRIVER_X11_XINPUT2
#undef SDL_VIDEO_DRIVER_X11_XINPUT2_SUPPORTS_MULTITOUCH
+#undef SDL_VIDEO_DRIVER_X11_XFIXES
#undef SDL_VIDEO_DRIVER_X11_XRANDR
#undef SDL_VIDEO_DRIVER_X11_XSCRNSAVER
#undef SDL_VIDEO_DRIVER_X11_XSHAPE
diff --git a/include/SDL_test_common.h b/include/SDL_test_common.h
index 21b412cd92..65a38c894f 100644
--- a/include/SDL_test_common.h
+++ b/include/SDL_test_common.h
@@ -73,6 +73,7 @@ typedef struct
int window_minH;
int window_maxW;
int window_maxH;
+ SDL_Rect confine;
int logical_w;
int logical_h;
float scale;
diff --git a/include/SDL_video.h b/include/SDL_video.h
index f0132621ea..5f4f4f55fe 100644
--- a/include/SDL_video.h
+++ b/include/SDL_video.h
@@ -1497,6 +1497,23 @@ 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 ee071e8dad..fcad9ba510 100644
--- a/src/dynapi/SDL_dynapi_overrides.h
+++ b/src/dynapi/SDL_dynapi_overrides.h
@@ -842,3 +842,4 @@
#define SDL_hid_get_product_string SDL_hid_get_product_string_REAL
#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
diff --git a/src/dynapi/SDL_dynapi_procs.h b/src/dynapi/SDL_dynapi_procs.h
index 8a732bb203..1a780c80aa 100644
--- a/src/dynapi/SDL_dynapi_procs.h
+++ b/src/dynapi/SDL_dynapi_procs.h
@@ -911,3 +911,4 @@ SDL_DYNAPI_PROC(int,SDL_hid_get_manufacturer_string,(SDL_hid_device *a, wchar_t
SDL_DYNAPI_PROC(int,SDL_hid_get_product_string,(SDL_hid_device *a, wchar_t *b, size_t c),(a,b,c),return)
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)
diff --git a/src/test/SDL_test_common.c b/src/test/SDL_test_common.c
index 12ada05d2e..6dcbee0221 100644
--- a/src/test/SDL_test_common.c
+++ b/src/test/SDL_test_common.c
@@ -37,7 +37,8 @@ static const char *video_usage[] = {
"[--scale N]", "[--depth N]", "[--refresh R]", "[--vsync]", "[--noframe]",
"[--resizable]", "[--minimize]", "[--maximize]", "[--grab]", "[--keyboard-grab]",
"[--shown]", "[--hidden]", "[--input-focus]", "[--mouse-focus]",
- "[--flash-on-focus-loss]", "[--allow-highdpi]", "[--usable-bounds]"
+ "[--flash-on-focus-loss]", "[--allow-highdpi]", "[--confine-cursor X,Y,W,H]",
+ "[--usable-bounds]"
};
static const char *audio_usage[] = {
@@ -296,6 +297,34 @@ SDLTest_CommonArg(SDLTest_CommonState * state, int index)
state->window_y = SDL_atoi(y);
return 2;
}
+ if (SDL_strcasecmp(argv[index], "--confine-cursor") == 0) {
+ char *x, *y, *w, *h;
+ ++index;
+ if (!argv[index]) {
+ return -1;
+ }
+ x = argv[index];
+ y = argv[index];
+ #define SEARCHARG(dim) \
+ while (*dim && *dim != ',') { \
+ ++dim; \
+ } \
+ if (!*dim) { \
+ return -1; \
+ } \
+ *dim++ = '\0';
+ SEARCHARG(y)
+ w = y;
+ SEARCHARG(w)
+ h = w;
+ SEARCHARG(h)
+ #undef SEARCHARG
+ state->confine.x = SDL_atoi(x);
+ state->confine.y = SDL_atoi(y);
+ state->confine.w = SDL_atoi(w);
+ state->confine.h = SDL_atoi(h);
+ return 2;
+ }
if (SDL_strcasecmp(argv[index], "--usable-bounds") == 0) {
/* !!! FIXME: this is a bit of a hack, but I don't want to add a
!!! FIXME: flag to the public structure in 2.0.x */
@@ -1289,6 +1318,10 @@ SDLTest_CommonInit(SDLTest_CommonState * state)
SDL_ShowWindow(state->windows[i]);
+ if (!SDL_RectEmpty(&state->confine)) {
+ SDL_SetWindowMouseRect(state->windows[i], &state->confine);
+ }
+
if (!state->skip_renderer
&& (state->renderdriver
|| !(state->window_flags & (SDL_WINDOW_OPENGL | SDL_WINDOW_VULKAN | SDL_WINDOW_METAL)))) {
diff --git a/src/video/SDL_sysvideo.h b/src/video/SDL_sysvideo.h
index 7e22d1b7a8..2098053af5 100644
--- a/src/video/SDL_sysvideo.h
+++ b/src/video/SDL_sysvideo.h
@@ -243,6 +243,7 @@ struct SDL_VideoDevice
void (*DestroyWindowFramebuffer) (_THIS, SDL_Window * window);
void (*OnWindowEnter) (_THIS, SDL_Window * window);
int (*FlashWindow) (_THIS, SDL_Window * window, SDL_FlashOperation operation);
+ int (*SetWindowMouseRect)(_THIS, SDL_Window * window, const SDL_Rect * rect);
/* * * */
/*
diff --git a/src/video/SDL_video.c b/src/video/SDL_video.c
index fda2c47b32..b6acc670d8 100644
--- a/src/video/SDL_video.c
+++ b/src/video/SDL_video.c
@@ -2832,6 +2832,16 @@ SDL_GetGrabbedWindow(void)
}
}
+int
+SDL_SetWindowMouseRect(SDL_Window * window, const SDL_Rect * rect)
+{
+ CHECK_WINDOW_MAGIC(window, -1);
+ if (!_this->SetWindowMouseRect) {
+ return SDL_Unsupported();
+ }
+ return _this->SetWindowMouseRect(_this, window, rect);
+}
+
int
SDL_FlashWindow(SDL_Window * window, SDL_FlashOperation operation)
{
diff --git a/src/video/x11/SDL_x11dyn.c b/src/video/x11/SDL_x11dyn.c
index 53d927f3c4..0909ce5471 100644
--- a/src/video/x11/SDL_x11dyn.c
+++ b/src/video/x11/SDL_x11dyn.c
@@ -53,6 +53,9 @@ typedef struct
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2
#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2 NULL
#endif
+#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES
+#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES NULL
+#endif
#ifndef SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR
#define SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR NULL
#endif
@@ -69,6 +72,7 @@ static x11dynlib x11libs[] = {
{NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XCURSOR},
{NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XINERAMA},
{NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XINPUT2},
+ {NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XFIXES},
{NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XRANDR},
{NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XSS},
{NULL, SDL_VIDEO_DRIVER_X11_DYNAMIC_XVIDMODE}
diff --git a/src/video/x11/SDL_x11dyn.h b/src/video/x11/SDL_x11dyn.h
index 7faa19526c..e4e2887584 100644
--- a/src/video/x11/SDL_x11dyn.h
+++ b/src/video/x11/SDL_x11dyn.h
@@ -58,6 +58,9 @@
#if SDL_VIDEO_DRIVER_X11_XINPUT2
#include <X11/extensions/XInput2.h>
#endif
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+#include <X11/extensions/Xfixes.h>
+#endif
#if SDL_VIDEO_DRIVER_X11_XRANDR
#include <X11/extensions/Xrandr.h>
#endif
diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c
index 664055614a..135d22dd36 100644
--- a/src/video/x11/SDL_x11events.c
+++ b/src/video/x11/SDL_x11events.c
@@ -31,6 +31,7 @@
#include "SDL_x11video.h"
#include "SDL_x11touch.h"
#include "SDL_x11xinput2.h"
+#include "SDL_x11xfixes.h"
#include "../../core/unix/SDL_poll.h"
#include "../../events/SDL_events_c.h"
#include "../../events/SDL_mouse_c.h"
@@ -869,6 +870,16 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
mouse->last_x = xevent->xcrossing.x;
mouse->last_y = xevent->xcrossing.y;
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ {
+ /* Only create the barriers if we have input focus */
+ SDL_WindowData* windowdata = (SDL_WindowData *) data->window->driverdata;
+ if ((data->pointer_barrier_active == SDL_TRUE) && windowdata->window->flags & SDL_WINDOW_INPUT_FOCUS) {
+ X11_ConfineCursorWithFlags(_this, windowdata->window, &windowdata->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
+ }
+ }
+#endif
+
if (!mouse->relative_mode) {
SDL_SendMouseMotion(data->window, 0, 0, xevent->xcrossing.x, xevent->xcrossing.y);
}
@@ -974,6 +985,13 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
data->pending_focus = PENDING_FOCUS_OUT;
data->pending_focus_time = SDL_GetTicks() + PENDING_FOCUS_TIME;
}
+
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ /* Disable confinement if it is activated. */
+ if (data->pointer_barrier_active == SDL_TRUE) {
+ X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
+ }
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
}
break;
@@ -1059,6 +1077,13 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
} else {
X11_DispatchUnmapNotify(data);
}
+
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ /* Disable confiment if the window gets hidden. */
+ if (data->pointer_barrier_active == SDL_TRUE) {
+ X11_ConfineCursorWithFlags(_this, data->window, NULL, X11_BARRIER_HANDLED_BY_EVENT);
+ }
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
}
break;
@@ -1068,6 +1093,13 @@ X11_DispatchEvent(_THIS, XEvent *xevent)
printf("window %p: MapNotify!\n", data);
#endif
X11_DispatchMapNotify(data);
+
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ /* Enable confiment if it was activated. */
+ if (data->pointer_barrier_active == SDL_TRUE) {
+ X11_ConfineCursorWithFlags(_this, data->window, &data->barrier_rect, X11_BARRIER_HANDLED_BY_EVENT);
+ }
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
}
break;
diff --git a/src/video/x11/SDL_x11mouse.c b/src/video/x11/SDL_x11mouse.c
index a87712a5cc..50983df193 100644
--- a/src/video/x11/SDL_x11mouse.c
+++ b/src/video/x11/SDL_x11mouse.c
@@ -321,7 +321,15 @@ static void
X11_WarpMouse(SDL_Window * window, int x, int y)
{
SDL_WindowData *data = (SDL_WindowData *) window->driverdata;
+
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ /* If we have no barrier, we need to warp */
+ if (data->pointer_barrier_active == SDL_FALSE) {
+ WarpMouseInternal(data->xwindow, x, y);
+ }
+#else
WarpMouseInternal(data->xwindow, x, y);
+#endif
}
static int
diff --git a/src/video/x11/SDL_x11sym.h b/src/video/x11/SDL_x11sym.h
index 3c273cf466..1dae060e71 100644
--- a/src/video/x11/SDL_x11sym.h
+++ b/src/video/x11/SDL_x11sym.h
@@ -158,6 +158,14 @@ SDL_X11_SYM(SDL_X11_XESetEventToWireRetType,XESetEventToWire,(Display* a,int b,S
SDL_X11_SYM(void,XRefreshKeyboardMapping,(XMappingEvent *a),(a),)
SDL_X11_SYM(int,XQueryTree,(Display* a,Window b,Window* c,Window* d,Window** e,unsigned int* f),(a,b,c,d,e,f),return)
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+SDL_X11_MODULE(XFIXES)
+SDL_X11_SYM(PointerBarrier, XFixesCreatePointerBarrier, (Display* a, Window b, int c, int d, int e, int f, int g, int h, int *i),(a,b,c,d,e,f,g,h,i),return)
+SDL_X11_SYM(void, XFixesDestroyPointerBarrier, (Display* a, PointerBarrier b), (a,b),)
+SDL_X11_SYM(int, XIBarrierReleasePointer,(Display* a, int b, PointerBarrier c, BarrierEventID d), (a,b,c,d), return)
+SDL_X11_SYM(Status, XFixesQueryVersion,(Display* a, int* b, int* c), (a,b,c), return)
+#endif
+
#if SDL_VIDEO_DRIVER_X11_SUPPORTS_GENERIC_EVENTS
SDL_X11_SYM(Bool,XGetEventData,(Display* a,XGenericEventCookie* b),(a,b),return)
SDL_X11_SYM(void,XFreeEventData,(Display* a,XGenericEventCookie* b),(a,b),)
diff --git a/src/video/x11/SDL_x11video.c b/src/video/x11/SDL_x11video.c
index 476daea38f..92eff83186 100644
--- a/src/video/x11/SDL_x11video.c
+++ b/src/video/x11/SDL_x11video.c
@@ -36,6 +36,7 @@
#include "SDL_x11shape.h"
#include "SDL_x11touch.h"
#include "SDL_x11xinput2.h"
+#include "SDL_x11xfixes.h"
#if SDL_VIDEO_OPENGL_EGL
#include "SDL_x11opengles.h"
@@ -187,6 +188,10 @@ X11_CreateDevice(int devindex)
data->global_mouse_changed = SDL_TRUE;
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ data->active_cursor_confined_window = NULL;
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
+
data->display = x11_display;
data->request_display = X11_XOpenDisplay(display);
if (data->request_display == NULL) {
@@ -256,6 +261,10 @@ X11_CreateDevice(int devindex)
device->AcceptDragAndDrop = X11_AcceptDragAndDrop;
device->FlashWindow = X11_FlashWindow;
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ device->SetWindowMouseRect = X11_SetWindowMouseRect;
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
+
device->shape_driver.CreateShaper = X11_CreateShaper;
device->shape_driver.SetWindowShape = X11_SetWindowShape;
device->shape_driver.ResizeWindowShape = X11_ResizeWindowShape;
@@ -447,6 +456,10 @@ X11_VideoInit(_THIS)
X11_InitXinput2(_this);
+#ifdef SDL_VIDEO_DRIVER_X11_XFIXES
+ X11_InitXfixes(_this);
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
+
if (X11_InitKeyboard(_this) != 0) {
return -1;
}
diff --git a/src/video/x11/SDL_x11video.h b/src/video/x11/SDL_x11video.h
index 18d5f4a27b..1609674632 100644
--- a/src/video/x11/SDL_x11video.h
+++ b/src/video/x11/SDL_x11video.h
@@ -85,6 +85,9 @@ typedef struct SDL_VideoData
int windowlistlength;
XID window_group;
Window clipboard_window;
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ SDL_Window *active_cursor_confined_window;
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
/* This is true for ICCCM2.0-compliant window managers */
SDL_bool net_wm;
diff --git a/src/video/x11/SDL_x11window.c b/src/video/x11/SDL_x11window.c
index bdadbc7025..9cf88fb593 100644
--- a/src/video/x11/SDL_x11window.c
+++ b/src/video/x11/SDL_x11window.c
@@ -32,6 +32,7 @@
#include "SDL_x11mouse.h"
#include "SDL_x11shape.h"
#include "SDL_x11xinput2.h"
+#include "SDL_x11xfixes.h"
#if SDL_VIDEO_OPENGL_EGL
#include "SDL_x11opengles.h"
@@ -330,6 +331,12 @@ SetupWindowData(_THIS, SDL_Window * window, Window w, BOOL created)
}
}
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ data->pointer_barrier_active = SDL_FALSE;
+ SDL_memset(&data->barrier, 0, sizeof(data->barrier));
+ SDL_memset(&data->barrier_rect, 0, sizeof(SDL_Rect));
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
+
/* All done! */
window->driverdata = data;
return 0;
@@ -1781,6 +1788,13 @@ X11_DestroyWindow(_THIS, SDL_Window * window)
X11_XFlush(display);
}
SDL_free(data);
+
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ /* If the pointer barriers are active for this, deactivate it.*/
+ if (videodata->active_cursor_confined_window == window) {
+ X11_DestroyPointerBarrier(_this, window);
+ }
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
}
window->driverdata = NULL;
}
diff --git a/src/video/x11/SDL_x11window.h b/src/video/x11/SDL_x11window.h
index a2fbaac6ad..bf01e1a2a5 100644
--- a/src/video/x11/SDL_x11window.h
+++ b/src/video/x11/SDL_x11window.h
@@ -73,6 +73,11 @@ typedef struct
#if SDL_VIDEO_OPENGL_EGL
EGLSurface egl_surface;
#endif
+#if SDL_VIDEO_DRIVER_X11_XFIXES
+ SDL_bool pointer_barrier_active;
+ PointerBarrier barrier[4];
+ SDL_Rect barrier_rect;
+#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */
} SDL_WindowData;
extern void X11_SetNetWMState(_THIS, Window xwindow, Uint32 flags);
diff --git a/src/video/x11/SDL_x11xfixes.c b/src/video/x11/SDL_x11xfixes.c
new file mode 100644
index 0000000000..45d0c6177c
--- /dev/null
+++ b/src/video/x11/SDL_x11xfixes.c
@@ -0,0 +1,193 @@
+/*
+ Simple DirectMedia Layer
+ Copyright (C) 1997-2017 Sam Lantinga <slouken@libsdl.org>
+
+ This software is provided 'as-is', without any express or implied
+ warranty. In no event will the authors be held liable for any damages
+ arising from the use of this software.
+
+ Permission is granted to anyone to use this software for any purpose,
+ including commercial applications, and to alter it and redistribute it
+ freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+ 3. This notice may not be removed or altered from any source distribution.
+*/
+
+#include "../../SDL_internal.h"
+
+#if SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XFIXES
+
+#include "SDL_x11video.h"
+#include "SDL_x11xfixes.h"
+#include "../../events/SDL_mouse_c.h"
+#include "../../events/SDL_touch_c.h"
+
+static int xfixes_initialized = 0;
+
+static int
+query_xfixes_version(Display *display, int major, int minor)
+{
+ /* We don't care if this fails, so long as it sets major/minor on it's way out the door. */
+ X11_XFixesQueryVersion(display, &major, &minor);
+ return ((major * 1000) + minor);
+}
+
+static SDL_bool
+xfixes_version_atleast(const int version, const int wantmajor, const int wantminor)
+{
+ return (version >= ((wantmajor * 1000) + wantminor));
+}
+
+void
+X11_InitXfixes(_THIS)
+{
+ SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+
+ int version = 0;
+ int event, error;
+ int fixes_opcode;
+
+ if (!SDL_X11_HAVE_XFIXES ||
+ !X11_XQueryExtension(data->display, "XFIXES", &fixes_opcode, &event, &error)) {
+ return;
+ }
+
+ /* We need at least 5.0 for barriers. */
+ version = query_xfixes_version(data->display, 5, 0);
+ if (!xfixes_version_atleast(version, 5, 0)) {
+ return; /* X server does not support the version we want at all. */
+ }
+
+ xfixes_initialized = 1;
+}
+
+int
+X11_XfixesIsInitialized()
+{
+ return xfixes_initialized;
+}
+
+int
+X11_SetWindowMouseRect(_THIS, SDL_Window * window, const SDL_Rect * rect)
+{
+ return X11_ConfineCursorWithFlags(_this, window, rect, 0);
+}
+
+int
+X11_ConfineCursorWithFlags(_THIS, SDL_Window * window, const SDL_Rect * rect, int flags)
+{
+ /* Yaakuro: For some reason Xfixes when confining inside a rect where the
+ * edges exactly match, a rectangle the cursor 'slips' out of the barrier.
+ * To prevent that the lines for the barriers will span the whole screen.
+ */
+ SDL_VideoData *data = (SDL_VideoData *) _this->driverdata;
+ SDL_WindowData *wdata;
+
+ if (!X11_XfixesIsInitialized()) {
+ return SDL_Unsupported();
+ }
+
+ /* If there is already a set of barriers active, disable them. */
+ if (data->active_cursor_confined_window) {
+ X11_DestroyPointerBarrier(_this, data->active_cursor_confined_window);
+ }
+
+ SDL_assert(window != NULL);
+ 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*/
+ if (rect) {
+ int x1, y1, x2, y2;
+ SDL_Rect bounds;
+ SDL_GetWindowPosition(window, &bounds.x, &bounds.y);
+ SDL_GetWindowSize(window, &bounds.w, &bounds.h);
+
+ /** Negative values are not allowed. Clip values relative to the specified window. */
+ x1 = bounds.x + SDL_max(rect->x, 0);
+ y1 = bounds.y + SDL_max(rect->y, 0);
+ x2 = SDL_min(bounds.x + rect->x + rect->w, bounds.x + bounds.w);
+ y2 = SDL_min(bounds.y + rect->y + rect->h, bounds.y + bounds.h);
+
+ if ((wdata->barrier_rect.x != rect->x) ||
+ (wdata->barrier_rect.y != rect->y) ||
+ (wdata->barrier_rect.w != rect->w) ||
+ (wdata->barrier_rect.h != rect->h)) {
+ wdata->barrier_rect = *rect;
+ }
+
+ /* Use the display bounds to ensure the barriers don't have corner gaps */
+ SDL_GetDisplayBounds(SDL_GetWindowDisplayIndex(window), &bounds);
+
+ /** Create the left barrier */
+ wdata->barrier[0] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
+ x1, bounds.y,
+ x1, bounds.y + bounds.h,
+ BarrierPositiveX,
+ 0, NULL);
+ /** Create the right barrier */
+ wdata->barrier[1] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
+ x2, bounds.y,
+ x2, bounds.y + bounds.h,
+ BarrierNegativeX,
+ 0, NULL);
+ /** Create the top barrier */
+ wdata->barrier[2] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow,
+
(Patch may be truncated, please check the link at the top of this post.)