diff --git a/glfw/cocoa_window.m b/glfw/cocoa_window.m index 7a2654eb8..178ace68f 100644 --- a/glfw/cocoa_window.m +++ b/glfw/cocoa_window.m @@ -1863,6 +1863,15 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window, float opacity) [window->ns.object setAlphaValue:opacity]; } +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED) +{ +} + +bool _glfwPlatformRawMouseMotionSupported(void) +{ + return false; +} + void _glfwDispatchRenderFrame(CGDirectDisplayID displayID) { _GLFWwindow *w = _glfw.windowListHead; diff --git a/glfw/glfw3.h b/glfw/glfw3.h index 8c0135b66..c231502e9 100644 --- a/glfw/glfw3.h +++ b/glfw/glfw3.h @@ -1076,6 +1076,7 @@ extern "C" { #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 #define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 @@ -4076,11 +4077,13 @@ GLFWAPI void glfwPostEmptyEvent(void); * * This function returns the value of an input option for the specified window. * The mode must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS. + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * @param[in] window The window to query. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED and @ref * GLFW_INVALID_ENUM. @@ -4099,7 +4102,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * * This function sets an input mode option for the specified window. The mode * must be one of @ref GLFW_CURSOR, @ref GLFW_STICKY_KEYS, - * @ref GLFW_STICKY_MOUSE_BUTTONS or @ref GLFW_LOCK_KEY_MODS. + * @ref GLFW_STICKY_MOUSE_BUTTONS, @ref GLFW_LOCK_KEY_MODS or + * @ref GLFW_RAW_MOUSE_MOTION. * * If the mode is `GLFW_CURSOR`, the value must be one of the following cursor * modes: @@ -4131,9 +4135,16 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * GLFW_MOD_CAPS_LOCK bit set when the event was generated with Caps Lock on, * and the @ref GLFW_MOD_NUM_LOCK bit when Num Lock was on. * + * If the mode is `GLFW_RAW_MOUSE_MOTION`, the value must be either `GLFW_TRUE` + * to enable raw (unscaled and unaccelerated) mouse motion when the cursor is + * disabled, or `false` to disable it. If raw motion is not supported, + * attempting to set this will emit @ref GLFW_PLATFORM_ERROR. Call @ref + * glfwRawMouseMotionSupported to check for support. + * * @param[in] window The window whose input mode to set. * @param[in] mode One of `GLFW_CURSOR`, `GLFW_STICKY_KEYS`, - * `GLFW_STICKY_MOUSE_BUTTONS` or `GLFW_LOCK_KEY_MODS`. + * `GLFW_STICKY_MOUSE_BUTTONS`, `GLFW_LOCK_KEY_MODS` or + * `GLFW_RAW_MOUSE_MOTION`. * @param[in] value The new value of the specified input mode. * * @errors Possible errors include @ref GLFW_NOT_INITIALIZED, @ref @@ -4149,6 +4160,35 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); */ GLFWAPI void glfwSetInputMode(GLFWwindow* window, int mode, int value); +/*! @brief Returns whether raw mouse motion is supported. + * + * This function returns whether raw mouse motion is supported on the current + * system. This status does not change after GLFW has been initialized so you + * only need to check this once. If you attempt to enable raw motion on + * a system that does not support it, @ref GLFW_PLATFORM_ERROR will be emitted. + * + * Raw mouse motion is closer to the actual motion of the mouse across + * a surface. It is not affected by the scaling and acceleration applied to + * the motion of the desktop cursor. That processing is suitable for a cursor + * while raw motion is better for controlling for example a 3D camera. Because + * of this, raw mouse motion is only provided when the cursor is disabled. + * + * @return `true` if raw mouse motion is supported on the current machine, + * or `false` otherwise. + * + * @errors Possible errors include @ref GLFW_NOT_INITIALIZED. + * + * @thread_safety This function must only be called from the main thread. + * + * @sa @ref raw_mouse_motion + * @sa @ref glfwSetInputMode + * + * @since Added in version 3.3. + * + * @ingroup input + */ +GLFWAPI int glfwRawMouseMotionSupported(void); + /*! @brief Returns the layout-specific name of the specified printable key. * * This function returns the name of the specified printable key, encoded as diff --git a/glfw/input.c b/glfw/input.c index 6b451aaff..d5121c75f 100644 --- a/glfw/input.c +++ b/glfw/input.c @@ -675,6 +675,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* handle, int mode) return window->stickyMouseButtons; case GLFW_LOCK_KEY_MODS: return window->lockKeyMods; + case GLFW_RAW_MOUSE_MOTION: + return window->rawMouseMotion; } _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); @@ -754,10 +756,32 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) { window->lockKeyMods = value ? true : false; } + else if (mode == GLFW_RAW_MOUSE_MOTION) + { + if (!_glfwPlatformRawMouseMotionSupported()) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Raw mouse motion is not supported on this system"); + return; + } + + value = value ? true : false; + if (window->rawMouseMotion == value) + return; + + window->rawMouseMotion = value; + _glfwPlatformSetRawMouseMotion(window, value); + } else _glfwInputError(GLFW_INVALID_ENUM, "Invalid input mode 0x%08X", mode); } +GLFWAPI int glfwRawMouseMotionSupported(void) +{ + _GLFW_REQUIRE_INIT_OR_RETURN(false); + return _glfwPlatformRawMouseMotionSupported(); +} + GLFWAPI const char* glfwGetKeyName(int key, int native_key) { _GLFW_REQUIRE_INIT_OR_RETURN(NULL); diff --git a/glfw/internal.h b/glfw/internal.h index fb7b402bc..e48651db1 100644 --- a/glfw/internal.h +++ b/glfw/internal.h @@ -429,6 +429,7 @@ struct _GLFWwindow char keys[GLFW_KEY_LAST + 1]; // Virtual cursor position when cursor is disabled double virtualCursorPosX, virtualCursorPosY; + bool rawMouseMotion; _GLFWcontext context; @@ -631,6 +632,8 @@ const char* _glfwPlatformGetVersionString(void); void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos); void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode); +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled); +bool _glfwPlatformRawMouseMotionSupported(void); int _glfwPlatformCreateCursor(_GLFWcursor* cursor, const GLFWimage* image, int xhot, int yhot, int count); int _glfwPlatformCreateStandardCursor(_GLFWcursor* cursor, GLFWCursorShape shape); diff --git a/glfw/null_window.c b/glfw/null_window.c index ceed84cdc..b12d4ca19 100644 --- a/glfw/null_window.c +++ b/glfw/null_window.c @@ -208,6 +208,15 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window UNUSED, float opacity UNU { } +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED) +{ +} + +bool _glfwPlatformRawMouseMotionSupported(void) +{ + return false; +} + void _glfwPlatformShowWindow(_GLFWwindow* window UNUSED) { } diff --git a/glfw/wl_window.c b/glfw/wl_window.c index 64e5799a0..476301280 100644 --- a/glfw/wl_window.c +++ b/glfw/wl_window.c @@ -1237,6 +1237,15 @@ void _glfwPlatformSetWindowOpacity(_GLFWwindow* window UNUSED, float opacity UNU { } +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window UNUSED, bool enabled UNUSED) +{ +} + +bool _glfwPlatformRawMouseMotionSupported(void) +{ + return false; +} + void _glfwPlatformPollEvents(void) { wl_display_dispatch_pending(_glfw.wl.display); diff --git a/glfw/x11_window.c b/glfw/x11_window.c index fd1c50d0f..6912b6b28 100644 --- a/glfw/x11_window.c +++ b/glfw/x11_window.c @@ -455,22 +455,41 @@ static void updateCursorImage(_GLFWwindow* window) } } +// Enable XI2 raw mouse motion events +// +static void enableRawMouseMotion(_GLFWwindow* window UNUSED) +{ + 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); +} + +// Disable XI2 raw mouse motion events +// +static void disableRawMouseMotion(_GLFWwindow* window UNUSED) +{ + 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); +} + // Apply disabled cursor mode to a focused window // static void disableCursor(_GLFWwindow* window) { - 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); - } + if (window->rawMouseMotion) + enableRawMouseMotion(window); _glfw.x11.disabledCursorWindow = window; _glfwPlatformGetCursorPos(window, @@ -490,17 +509,8 @@ static void disableCursor(_GLFWwindow* window) // static void enableCursor(_GLFWwindow* 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); - } + if (window->rawMouseMotion) + disableRawMouseMotion(window); _glfw.x11.disabledCursorWindow = NULL; XUngrabPointer(_glfw.x11.display, CurrentTime); @@ -1122,6 +1132,7 @@ static void processEvent(XEvent *event) _GLFWwindow* window = _glfw.x11.disabledCursorWindow; if (window && + window->rawMouseMotion && event->xcookie.extension == _glfw.x11.xi.majorOpcode && XGetEventData(_glfw.x11.display, &event->xcookie) && event->xcookie.evtype == XI_RawMotion) @@ -1363,7 +1374,7 @@ static void processEvent(XEvent *event) { if (_glfw.x11.disabledCursorWindow != window) return; - if (_glfw.x11.xi.available) + if (window->rawMouseMotion) return; const int dx = x - window->x11.lastCursorPosX; @@ -2629,6 +2640,25 @@ _glfwDispatchX11Events(void) { return dispatched; } +void _glfwPlatformSetRawMouseMotion(_GLFWwindow *window, bool enabled) +{ + if (!_glfw.x11.xi.available) + return; + + if (_glfw.x11.disabledCursorWindow != window) + return; + + if (enabled) + enableRawMouseMotion(window); + else + disableRawMouseMotion(window); +} + +bool _glfwPlatformRawMouseMotionSupported(void) +{ + return _glfw.x11.xi.available; +} + void _glfwPlatformPollEvents(void) { _glfwDispatchX11Events(); diff --git a/kitty/glfw-wrapper.h b/kitty/glfw-wrapper.h index 156e2ecb0..84865eb41 100644 --- a/kitty/glfw-wrapper.h +++ b/kitty/glfw-wrapper.h @@ -839,6 +839,7 @@ #define GLFW_STICKY_KEYS 0x00033002 #define GLFW_STICKY_MOUSE_BUTTONS 0x00033003 #define GLFW_LOCK_KEY_MODS 0x00033004 +#define GLFW_RAW_MOUSE_MOTION 0x00033005 #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002