From f41d85a2099ac11a04dd79d8a96716c2d6447823 Mon Sep 17 00:00:00 2001
From: Camilla Berglund <elmindreda@elmindreda.org>
Date: Thu, 4 Apr 2013 16:48:58 +0200
Subject: [PATCH] Added initial XInput2 cursor motion.

---
 CMakeLists.txt     |  5 ++++
 src/x11_init.c     | 17 +++++++++++++
 src/x11_platform.h | 14 ++++++++++-
 src/x11_window.c   | 62 ++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 97 insertions(+), 1 deletion(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 56e31736..1c292ed8 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -166,6 +166,11 @@ if (_GLFW_X11)
         message(FATAL_ERROR "The RandR library and headers were not found")
     endif()
 
+    if (X11_Xinput_FOUND)
+        list(APPEND glfw_INCLUDE_DIRS ${X11_Xinput_INCLUDE_PATH})
+        list(APPEND glfw_LIBRARIES ${X11_Xinput_LIB})
+    endif()
+
     list(APPEND glfw_INCLUDE_DIRS ${X11_Xrandr_INCLUDE_PATH})
     list(APPEND glfw_LIBRARIES ${X11_Xrandr_LIB})
     set(GLFW_PKG_DEPS "${GLFW_PKG_DEPS} xrandr")
diff --git a/src/x11_init.c b/src/x11_init.c
index 9f131851..c3824487 100644
--- a/src/x11_init.c
+++ b/src/x11_init.c
@@ -474,6 +474,23 @@ static GLboolean initDisplay(void)
         }
     }
 
+    if (XQueryExtension(_glfw.x11.display,
+                        "XInputExtension",
+                        &_glfw.x11.xi2.majorOpcode,
+                        &_glfw.x11.xi2.eventBase,
+                        &_glfw.x11.xi2.errorBase))
+    {
+        _glfw.x11.xi2.versionMajor = 2;
+        _glfw.x11.xi2.versionMinor = 0;
+
+        if (XIQueryVersion(_glfw.x11.display,
+                           &_glfw.x11.xi2.versionMajor,
+                           &_glfw.x11.xi2.versionMinor) != BadRequest)
+        {
+            _glfw.x11.xi2.available = GL_TRUE;
+        }
+    }
+
     // Check if Xkb is supported on this display
     _glfw.x11.xkb.versionMajor = 1;
     _glfw.x11.xkb.versionMinor = 0;
diff --git a/src/x11_platform.h b/src/x11_platform.h
index e7c78011..81bdcc8f 100644
--- a/src/x11_platform.h
+++ b/src/x11_platform.h
@@ -44,6 +44,9 @@
 // The XRandR extension provides mode setting and gamma control
 #include <X11/extensions/Xrandr.h>
 
+// The XInput2 extension provides improved input events
+#include <X11/extensions/XInput2.h>
+
 // The Xkb extension provides improved keyboard support
 #include <X11/XKBlib.h>
 
@@ -94,7 +97,7 @@ typedef struct _GLFWwindowX11
     GLboolean       cursorGrabbed;    // True if cursor is currently grabbed
     GLboolean       cursorHidden;     // True if cursor is currently hidden
     GLboolean       cursorCentered;   // True if cursor was moved since last poll
-    int             cursorPosX, cursorPosY;
+    double          cursorPosX, cursorPosY;
 
 } _GLFWwindowX11;
 
@@ -153,6 +156,15 @@ typedef struct _GLFWlibraryX11
         int         versionMinor;
     } xkb;
 
+    struct {
+        GLboolean   available;
+        int         majorOpcode;
+        int         eventBase;
+        int         errorBase;
+        int         versionMajor;
+        int         versionMinor;
+    } xi2;
+
     // LUT for mapping X11 key codes to GLFW key codes
     int             keyCodeLUT[256];
 
diff --git a/src/x11_window.c b/src/x11_window.c
index 74fa3093..00e9d42b 100644
--- a/src/x11_window.c
+++ b/src/x11_window.c
@@ -213,6 +213,21 @@ static GLboolean createWindow(_GLFWwindow* window,
         XFree(hints);
     }
 
+    if (_glfw.x11.xi2.available)
+    {
+        // Select for XInput2 events
+
+        XIEventMask eventmask;
+        unsigned char mask[] = { 0 };
+
+        eventmask.deviceid = 2;
+        eventmask.mask_len = sizeof(mask);
+        eventmask.mask = mask;
+        XISetMask(mask, XI_Motion);
+
+        XISelectEvents(_glfw.x11.display, window->x11.handle, &eventmask, 1);
+    }
+
     _glfwPlatformSetWindowTitle(window, wndconfig->title);
 
     XRRSelectInput(_glfw.x11.display, window->x11.handle,
@@ -699,6 +714,53 @@ static void processEvent(XEvent *event)
         case DestroyNotify:
             return;
 
+        case GenericEvent:
+        {
+            if (event->xcookie.extension == _glfw.x11.xi2.majorOpcode &&
+                XGetEventData(_glfw.x11.display, &event->xcookie))
+            {
+                if (event->xcookie.evtype == XI_Motion)
+                {
+                    XIDeviceEvent* data = (XIDeviceEvent*) event->xcookie.data;
+
+                    window = _glfwFindWindowByHandle(data->event);
+                    if (window)
+                    {
+                        if (data->event_x != window->x11.cursorPosX ||
+                            data->event_y != window->x11.cursorPosY)
+                        {
+                            // The cursor was moved by something other than GLFW
+
+                            double x, y;
+
+                            if (window->cursorMode == GLFW_CURSOR_CAPTURED)
+                            {
+                                if (_glfw.focusedWindow != window)
+                                    break;
+
+                                x = data->event_x - window->x11.cursorPosX;
+                                y = data->event_y - window->x11.cursorPosY;
+                            }
+                            else
+                            {
+                                x = data->event_x;
+                                y = data->event_y;
+                            }
+
+                            window->x11.cursorPosX = data->event_x;
+                            window->x11.cursorPosY = data->event_y;
+                            window->x11.cursorCentered = GL_FALSE;
+
+                            _glfwInputCursorMotion(window, x, y);
+                        }
+                    }
+                }
+            }
+
+            XFreeEventData(_glfw.x11.display, &event->xcookie);
+            break;
+        }
+
         default:
         {
             switch (event->type - _glfw.x11.randr.eventBase)