From 4b42c05ba1eaaaa9a4ef803acea8f13402271039 Mon Sep 17 00:00:00 2001 From: Ethan Lee 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 cf118c274..bbb282959 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 a48132086..8d8bed992 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 a79190266..4950eb8ab 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 + ]) + 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 0bf1421c9..673af9599 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 5c5cb29af..db67826a2 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 21b412cd9..65a38c894 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 f0132621e..5f4f4f55f 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 ee071e8da..fcad9ba51 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 8a732bb20..1a780c80a 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 12ada05d2..6dcbee022 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 7e22d1b7a..2098053af 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 fda2c47b3..b6acc670d 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 53d927f3c..0909ce547 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 7faa19526..e4e288758 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 #endif +#if SDL_VIDEO_DRIVER_X11_XFIXES +#include +#endif #if SDL_VIDEO_DRIVER_X11_XRANDR #include #endif diff --git a/src/video/x11/SDL_x11events.c b/src/video/x11/SDL_x11events.c index 664055614..135d22dd3 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 a87712a5c..50983df19 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 3c273cf46..1dae060e7 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 476daea38..92eff8318 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 18d5f4a27..160967463 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 bdadbc702..9cf88fb59 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 a2fbaac6a..bf01e1a2a 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 000000000..45d0c6177 --- /dev/null +++ b/src/video/x11/SDL_x11xfixes.c @@ -0,0 +1,193 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2017 Sam Lantinga + + 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, + bounds.x, y1, + bounds.x + bounds.w, y1, + BarrierPositiveY, + 0, NULL); + /** Create the bottom barrier */ + wdata->barrier[3] = X11_XFixesCreatePointerBarrier(data->display, wdata->xwindow, + bounds.x, y2, + bounds.x + bounds.w, y2, + BarrierNegativeY, + 0, NULL); + + X11_XFlush(data->display); + + /* Lets remember current active confined window. */ + data->active_cursor_confined_window = window; + + /* User activated the confinement for this window. We use this later to reactivate + * the confinement if it got deactivated by FocusOut or UnmapNotify */ + wdata->pointer_barrier_active = SDL_TRUE; + } else { + X11_DestroyPointerBarrier(_this, window); + + /* Only set barrier inactive when user specified NULL and not handled by focus out. */ + if (flags != X11_BARRIER_HANDLED_BY_EVENT) { + wdata->pointer_barrier_active = SDL_FALSE; + } + } + return 0; +} + +void +X11_DestroyPointerBarrier(_THIS, SDL_Window * window) +{ + int i; + SDL_VideoData *data = (SDL_VideoData *) _this->driverdata; + if (window) { + SDL_WindowData *wdata = (SDL_WindowData *) window->driverdata; + + for (i = 0; i < 4; i++) { + if (wdata->barrier[i] > 0) { + X11_XFixesDestroyPointerBarrier(data->display, wdata->barrier[i]); + wdata->barrier[i] = 0; + } + } + X11_XFlush(data->display); + } + data->active_cursor_confined_window = NULL; +} + +#endif /* SDL_VIDEO_DRIVER_X11 && SDL_VIDEO_DRIVER_X11_XFIXES */ + +/* vi: set ts=4 sw=4 expandtab: */ diff --git a/src/video/x11/SDL_x11xfixes.h b/src/video/x11/SDL_x11xfixes.h new file mode 100644 index 000000000..e9a464366 --- /dev/null +++ b/src/video/x11/SDL_x11xfixes.h @@ -0,0 +1,41 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2017 Sam Lantinga + + 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" + +#ifndef SDL_x11xfixes_h_ +#define SDL_x11xfixes_h_ + +#if SDL_VIDEO_DRIVER_X11_XFIXES + +#define X11_BARRIER_HANDLED_BY_EVENT 1 + +extern void X11_InitXfixes(_THIS); +extern int X11_XfixesIsInitialized(void); +extern int X11_SetWindowMouseRect(_THIS, SDL_Window * window, const SDL_Rect * rect); +extern int X11_ConfineCursorWithFlags(_THIS, SDL_Window * window, const SDL_Rect * rect, int flags); +extern void X11_DestroyPointerBarrier(_THIS, SDL_Window * window); + +#endif /* SDL_VIDEO_DRIVER_X11_XFIXES */ + +#endif /* SDL_x11xfixes_h_ */ + +/* vi: set ts=4 sw=4 expandtab: */