From a570d0a1294ad9af54df416fd7e16d9d7ea36c62 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Thu, 12 Jan 2017 20:31:59 +0100 Subject: [PATCH] X11: Use XI_RawMotion for disabled cursor motion Related to #125. --- README.md | 1 + src/x11_init.c | 26 ++++++++++++++ src/x11_platform.h | 20 +++++++++++ src/x11_window.c | 89 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 129 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 1a6a5644..903d82dc 100644 --- a/README.md +++ b/README.md @@ -168,6 +168,7 @@ information on what to include when reporting a bug. function on macOS 10.12+ - [Cocoa] Bugfix: Running in AppSandbox would emit warnings (#816,#882) - [Cocoa] Bugfix: Windows created after the first were not cascaded (#195) +- [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [EGL] Added support for `EGL_KHR_get_all_proc_addresses` (#871) - [EGL] Bugfix: The test for `EGL_RGB_BUFFER` was invalid diff --git a/src/x11_init.c b/src/x11_init.c index 19b97acf..22600c96 100644 --- a/src/x11_init.c +++ b/src/x11_init.c @@ -477,6 +477,32 @@ static GLFWbool initExtensions(void) &_glfw.x11.vidmode.errorBase); } + _glfw.x11.xi.handle = dlopen("libXi.so", RTLD_LAZY | RTLD_GLOBAL); + if (_glfw.x11.xi.handle) + { + _glfw.x11.xi.QueryVersion = (PFN_XIQueryVersion) + dlsym(_glfw.x11.xi.handle, "XIQueryVersion"); + _glfw.x11.xi.SelectEvents = (PFN_XISelectEvents) + dlsym(_glfw.x11.xi.handle, "XISelectEvents"); + + if (XQueryExtension(_glfw.x11.display, + "XInputExtension", + &_glfw.x11.xi.majorOpcode, + &_glfw.x11.xi.eventBase, + &_glfw.x11.xi.errorBase)) + { + _glfw.x11.xi.major = 2; + _glfw.x11.xi.minor = 0; + + if (XIQueryVersion(_glfw.x11.display, + &_glfw.x11.xi.major, + &_glfw.x11.xi.minor) == Success) + { + _glfw.x11.xi.available = GLFW_TRUE; + } + } + } + // Check for RandR extension if (XRRQueryExtension(_glfw.x11.display, &_glfw.x11.randr.eventBase, diff --git a/src/x11_platform.h b/src/x11_platform.h index 4319c528..a88e133c 100644 --- a/src/x11_platform.h +++ b/src/x11_platform.h @@ -47,6 +47,9 @@ // The Xinerama extension provides legacy monitor indices #include +// The XInput extension provides raw mouse motion input +#include + typedef XID xcb_window_t; typedef XID xcb_visualid_t; typedef struct xcb_connection_t xcb_connection_t; @@ -61,6 +64,11 @@ typedef Bool (* PFN_XF86VidModeGetGammaRampSize)(Display*,int,int*); #define XF86VidModeSetGammaRamp _glfw.x11.vidmode.SetGammaRamp #define XF86VidModeGetGammaRampSize _glfw.x11.vidmode.GetGammaRampSize +typedef Status (* PFN_XIQueryVersion)(Display*,int*,int*); +typedef int (* PFN_XISelectEvents)(Display*,Window,XIEventMask*,int); +#define XIQueryVersion _glfw.x11.xi.QueryVersion +#define XISelectEvents _glfw.x11.xi.SelectEvents + typedef VkFlags VkXlibSurfaceCreateFlagsKHR; typedef VkFlags VkXcbSurfaceCreateFlagsKHR; @@ -269,6 +277,18 @@ typedef struct _GLFWlibraryX11 PFN_XF86VidModeGetGammaRampSize GetGammaRampSize; } vidmode; + struct { + GLFWbool available; + void* handle; + int majorOpcode; + int eventBase; + int errorBase; + int major; + int minor; + PFN_XIQueryVersion QueryVersion; + PFN_XISelectEvents SelectEvents; + } xi; + } _GLFWlibraryX11; // X11-specific per-monitor data diff --git a/src/x11_window.c b/src/x11_window.c index 2de04436..d65ba9ab 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -920,14 +920,48 @@ static void processEvent(XEvent *event) } } - if (event->type != GenericEvent) + if (event->type == GenericEvent) { - window = findWindowByHandle(event->xany.window); - if (window == NULL) + if (_glfw.x11.xi.available) { - // This is an event for a window that has already been destroyed - return; + _GLFWwindow* window = _glfw.x11.disabledCursorWindow; + + if (window && + event->xcookie.extension == _glfw.x11.xi.majorOpcode && + XGetEventData(_glfw.x11.display, &event->xcookie) && + event->xcookie.evtype == XI_RawMotion) + { + XIRawEvent* re = event->xcookie.data; + if (re->valuators.mask_len) + { + const double* values = re->raw_values; + double xpos = window->virtualCursorPosX; + double ypos = window->virtualCursorPosY; + + if (XIMaskIsSet(re->valuators.mask, 0)) + { + xpos += *values; + values++; + } + + if (XIMaskIsSet(re->valuators.mask, 1)) + ypos += *values; + + _glfwInputCursorPos(window, xpos, ypos); + } + } + + XFreeEventData(_glfw.x11.display, &event->xcookie); } + + return; + } + + window = findWindowByHandle(event->xany.window); + if (window == NULL) + { + // This is an event for a window that has already been destroyed + return; } switch (event->type) @@ -1170,6 +1204,8 @@ static void processEvent(XEvent *event) { if (_glfw.x11.disabledCursorWindow != window) return; + if (_glfw.x11.xi.available) + return; const int dx = x - window->x11.lastCursorPosX; const int dy = y - window->x11.lastCursorPosY; @@ -2157,6 +2193,8 @@ void _glfwPlatformSetWindowFloating(_GLFWwindow* window, GLFWbool enabled) void _glfwPlatformPollEvents(void) { + _GLFWwindow* window; + #if defined(__linux__) _glfwDetectJoystickConnectionLinux(); #endif @@ -2168,8 +2206,20 @@ void _glfwPlatformPollEvents(void) processEvent(&event); } - if (_glfw.x11.disabledCursorWindow) - centerCursor(_glfw.x11.disabledCursorWindow); + window = _glfw.x11.disabledCursorWindow; + if (window) + { + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); + + // NOTE: Re-center the cursor only if it has moved since the last call, + // to avoid breaking glfwWaitEvents with MotionNotify + if (window->x11.lastCursorPosX != width / 2 || + window->x11.lastCursorPosY != height / 2) + { + _glfwPlatformSetCursorPos(window, width / 2, height / 2); + } + } XFlush(_glfw.x11.display); } @@ -2239,6 +2289,19 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) { if (mode == GLFW_CURSOR_DISABLED) { + if (_glfw.x11.xi.available) + { + XIEventMask em; + unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 }; + + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + XISetMask(mask, XI_RawMotion); + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); + } + _glfw.x11.disabledCursorWindow = window; _glfwPlatformGetCursorPos(window, &_glfw.x11.restoreCursorPosX, @@ -2253,6 +2316,18 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) } else if (_glfw.x11.disabledCursorWindow == window) { + if (_glfw.x11.xi.available) + { + XIEventMask em; + unsigned char mask[] = { 0 }; + + em.deviceid = XIAllMasterDevices; + em.mask_len = sizeof(mask); + em.mask = mask; + + XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1); + } + _glfw.x11.disabledCursorWindow = NULL; XUngrabPointer(_glfw.x11.display, CurrentTime); _glfwPlatformSetCursorPos(window,