From 99b55dd21bc64a1b1bc470b53f07df4cb6282ca1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Camilla=20L=C3=B6wy?= Date: Tue, 3 Dec 2019 17:58:20 +0100 Subject: [PATCH] Add GLFW_CURSOR_CAPTURED This adds a cursor mode that provides a visible cursor confined to the content area of the window. Related to #58. --- README.md | 2 ++ docs/input.dox | 12 ++++++++++++ docs/news.dox | 9 +++++++++ include/GLFW/glfw3.h | 3 +++ src/input.c | 3 ++- src/win32_window.c | 15 +++++++++++++-- src/x11_window.c | 6 +++++- tests/cursor.c | 8 +++++++- 8 files changed, 53 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index daa1be9c..205e31d4 100644 --- a/README.md +++ b/README.md @@ -123,6 +123,8 @@ information on what to include when reporting a bug. - Added `GLFW_RESIZE_EW_CURSOR` alias for `GLFW_HRESIZE_CURSOR` (#427) - Added `GLFW_RESIZE_NS_CURSOR` alias for `GLFW_VRESIZE_CURSOR` (#427) - Added `GLFW_POINTING_HAND_CURSOR` alias for `GLFW_HAND_CURSOR` (#427) + - Added `GLFW_CURSOR_CAPTURED` cursor mode to confine the cursor to the window + content area (#58) - Disabled tests and examples by default when built as a CMake subdirectory - Bugfix: The CMake config-file package used an absolute path and was not relocatable (#1470) diff --git a/docs/input.dox b/docs/input.dox index 331f9718..e5cf3f7a 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -300,6 +300,16 @@ glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); This mode puts no limit on the motion of the cursor. +If you wish the cursor to be visible but confined to the content area of the +window, set the cursor mode to `GLFW_CURSOR_CAPTURED`. + +@code +glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_CAPTURED); +@endcode + +The cursor will behave normally inside the content area but will not be able to +leave unless the window loses focus. + To exit out of either of these special modes, restore the `GLFW_CURSOR_NORMAL` cursor mode. @@ -307,6 +317,8 @@ cursor mode. glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); @endcode +If the cursor was disabled, this will move it back to its last visible position. + @anchor GLFW_RAW_MOUSE_MOTION @subsection raw_mouse_motion Raw mouse motion diff --git a/docs/news.dox b/docs/news.dox index f18537af..0c2967aa 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -27,6 +27,14 @@ are still available. For more information see @ref cursor_standard. +@subsubsection captured_cursor_34 Captured cursor mode + +GLFW now supports confining the cursor to the window content area with the @ref +GLFW_CURSOR_CAPTURED cursor mode. + +For more information see @ref cursor_mode. + + @subsubsection features_34_win32_keymenu Support for keyboard access to Windows window menu GLFW now provides the @@ -71,6 +79,7 @@ add_subdirectory(path/to/glfw) - @ref GLFW_NOT_ALLOWED_CURSOR - @ref GLFW_CURSOR_UNAVAILABLE - @ref GLFW_WIN32_KEYBOARD_MENU + - @ref GLFW_CURSOR_CAPTURED @section news_archive Release notes for earlier versions diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 8b5b87dd..0defc1de 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -1038,6 +1038,7 @@ extern "C" { #define GLFW_CURSOR_NORMAL 0x00034001 #define GLFW_CURSOR_HIDDEN 0x00034002 #define GLFW_CURSOR_DISABLED 0x00034003 +#define GLFW_CURSOR_CAPTURED 0x00034004 #define GLFW_ANY_RELEASE_BEHAVIOR 0 #define GLFW_RELEASE_BEHAVIOR_FLUSH 0x00035001 @@ -4139,6 +4140,8 @@ GLFWAPI int glfwGetInputMode(GLFWwindow* window, int mode); * - `GLFW_CURSOR_DISABLED` hides and grabs the cursor, providing virtual * and unlimited cursor movement. This is useful for implementing for * example 3D camera controls. + * - `GLFW_CURSOR_CAPTURED` makes the cursor visible and confines it to the + * content area of the window. * * If the mode is `GLFW_STICKY_KEYS`, the value must be either `GLFW_TRUE` to * enable sticky keys, or `GLFW_FALSE` to disable it. If sticky keys are diff --git a/src/input.c b/src/input.c index f6163093..80811317 100644 --- a/src/input.c +++ b/src/input.c @@ -506,7 +506,8 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value) { if (value != GLFW_CURSOR_NORMAL && value != GLFW_CURSOR_HIDDEN && - value != GLFW_CURSOR_DISABLED) + value != GLFW_CURSOR_DISABLED && + value != GLFW_CURSOR_CAPTURED) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid cursor mode 0x%08X", diff --git a/src/win32_window.c b/src/win32_window.c index 3e6c5153..2473579d 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -242,7 +242,8 @@ static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) // static void updateCursorImage(_GLFWwindow* window) { - if (window->cursorMode == GLFW_CURSOR_NORMAL) + if (window->cursorMode == GLFW_CURSOR_NORMAL || + window->cursorMode == GLFW_CURSOR_CAPTURED) { if (window->cursor) SetCursor(window->cursor->win32.handle); @@ -650,6 +651,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); window->win32.frameAction = GLFW_FALSE; } @@ -668,6 +671,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); return 0; } @@ -676,6 +681,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + releaseCursor(); if (window->monitor && window->autoIconify) _glfwPlatformIconifyWindow(window); @@ -959,6 +966,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // resizing the window or using the window menu if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + releaseCursor(); break; } @@ -973,6 +982,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, // resizing the window or using the menu if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); break; } @@ -2050,7 +2061,7 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) disableRawMouseMotion(window); } - if (mode == GLFW_CURSOR_DISABLED) + if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED) captureCursor(window); else releaseCursor(); diff --git a/src/x11_window.c b/src/x11_window.c index 08cb3d3c..38d2c79c 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1786,6 +1786,8 @@ static void processEvent(XEvent *event) if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); if (window->x11.ic) XSetICFocus(window->x11.ic); @@ -1806,6 +1808,8 @@ static void processEvent(XEvent *event) if (window->cursorMode == GLFW_CURSOR_DISABLED) enableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + releaseCursor(); if (window->x11.ic) XUnsetICFocus(window->x11.ic); @@ -2800,7 +2804,7 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) disableRawMouseMotion(window); } - if (mode == GLFW_CURSOR_DISABLED) + if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED) captureCursor(window); else releaseCursor(); diff --git a/tests/cursor.c b/tests/cursor.c index f72c0f91..f72e5cc0 100644 --- a/tests/cursor.c +++ b/tests/cursor.c @@ -171,7 +171,8 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, case GLFW_KEY_ESCAPE: { - if (glfwGetInputMode(window, GLFW_CURSOR) != GLFW_CURSOR_DISABLED) + const int mode = glfwGetInputMode(window, GLFW_CURSOR); + if (mode != GLFW_CURSOR_DISABLED && mode != GLFW_CURSOR_CAPTURED) { glfwSetWindowShouldClose(window, GLFW_TRUE); break; @@ -196,6 +197,11 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, printf("(( cursor is hidden ))\n"); break; + case GLFW_KEY_C: + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_CAPTURED); + printf("(( cursor is captured ))\n"); + break; + case GLFW_KEY_R: if (!glfwRawMouseMotionSupported()) break;