SDL: video: Add SDL_SetWindowMouseRect.

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.)