2
0
Эх сурвалжийг харах

Add cursor mode GLFW_CURSOR_CAPTURED

This adds a cursor mode that provides a visible cursor confined to the
content area of the window.

Fixes #58
Camilla Löwy 5 жил өмнө
parent
commit
488008e0a2

+ 2 - 0
README.md

@@ -139,6 +139,8 @@ information on what to include when reporting a bug.
  - Added `GLFW_POINTING_HAND_CURSOR` alias for `GLFW_HAND_CURSOR` (#427)
  - Added `GLFW_MOUSE_PASSTHROUGH` window hint for letting mouse input pass
    through the window (#1236,#1568)
+ - Added `GLFW_CURSOR_CAPTURED` cursor mode to confine the cursor to the window
+   content area (#58)
  - Added `GLFW_PLATFORM_UNAVAILABLE` error for platform detection failures (#1958)
  - Added `GLFW_FEATURE_UNAVAILABLE` error for platform limitations (#1692)
  - Added `GLFW_FEATURE_UNIMPLEMENTED` error for incomplete backends (#1692)

+ 12 - 0
docs/input.dox

@@ -312,6 +312,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.
 
@@ -319,6 +329,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

+ 9 - 0
docs/news.dox

@@ -58,6 +58,14 @@ requesting a specific rendering backend when using
 contexts.
 
 
+@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_init_allocator Support for custom memory allocator
 
 GLFW now supports plugging a custom memory allocator at initialization with @ref
@@ -234,6 +242,7 @@ then GLFW will fail to initialize.
  - @ref GLFW_ANGLE_PLATFORM_TYPE_VULKAN
  - @ref GLFW_ANGLE_PLATFORM_TYPE_METAL
  - @ref GLFW_X11_XCB_VULKAN_SURFACE
+ - @ref GLFW_CURSOR_CAPTURED
 
 
 @section news_archive Release notes for earlier versions

+ 3 - 0
include/GLFW/glfw3.h

@@ -1134,6 +1134,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
@@ -4566,6 +4567,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

+ 8 - 0
src/cocoa_window.m

@@ -1619,8 +1619,16 @@ void _glfwSetCursorPosCocoa(_GLFWwindow* window, double x, double y)
 void _glfwSetCursorModeCocoa(_GLFWwindow* window, int mode)
 {
     @autoreleasepool {
+
+    if (mode == GLFW_CURSOR_CAPTURED)
+    {
+        _glfwInputError(GLFW_FEATURE_UNIMPLEMENTED,
+                        "Cocoa: Captured cursor mode not yet implemented");
+    }
+
     if (_glfwWindowFocusedCocoa(window))
         updateCursorMode(window);
+
     } // autoreleasepool
 }
 

+ 2 - 1
src/input.c

@@ -596,7 +596,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",

+ 13 - 2
src/win32_window.c

@@ -238,7 +238,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);
@@ -586,6 +587,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
             {
                 if (window->cursorMode == GLFW_CURSOR_DISABLED)
                     disableCursor(window);
+                else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
+                    captureCursor(window);
 
                 window->win32.frameAction = GLFW_FALSE;
             }
@@ -604,6 +607,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
 
             if (window->cursorMode == GLFW_CURSOR_DISABLED)
                 disableCursor(window);
+            else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
+                captureCursor(window);
 
             return 0;
         }
@@ -612,6 +617,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
         {
             if (window->cursorMode == GLFW_CURSOR_DISABLED)
                 enableCursor(window);
+            else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
+                releaseCursor();
 
             if (window->monitor && window->autoIconify)
                 _glfwIconifyWindowWin32(window);
@@ -981,6 +988,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
             //       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;
         }
@@ -995,6 +1004,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l
             //       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;
         }
@@ -2166,7 +2177,7 @@ void _glfwSetCursorModeWin32(_GLFWwindow* window, int mode)
                 disableRawMouseMotion(window);
         }
 
-        if (mode == GLFW_CURSOR_DISABLED)
+        if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED)
             captureCursor(window);
         else
             releaseCursor();

+ 1 - 0
src/wl_platform.h

@@ -269,6 +269,7 @@ typedef struct _GLFWwindowWayland
 
     struct zwp_relative_pointer_v1* relativePointer;
     struct zwp_locked_pointer_v1*   lockedPointer;
+    struct zwp_confined_pointer_v1* confinedPointer;
 
     struct zwp_idle_inhibitor_v1*          idleInhibitor;
 

+ 53 - 1
src/wl_window.c

@@ -1860,6 +1860,9 @@ void _glfwDestroyWindowWayland(_GLFWwindow* window)
     if (window->wl.lockedPointer)
         zwp_locked_pointer_v1_destroy(window->wl.lockedPointer);
 
+    if (window->wl.confinedPointer)
+        zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
+
     if (window->context.destroy)
         window->context.destroy(window);
 
@@ -2538,6 +2541,43 @@ static void lockPointer(_GLFWwindow* window)
                                        window);
 }
 
+static void confinedPointerHandleConfined(void* userData,
+                                          struct zwp_confined_pointer_v1* confinedPointer)
+{
+}
+
+static void confinedPointerHandleUnconfined(void* userData,
+                                            struct zwp_confined_pointer_v1* confinedPointer)
+{
+}
+
+static const struct zwp_confined_pointer_v1_listener confinedPointerListener =
+{
+    confinedPointerHandleConfined,
+    confinedPointerHandleUnconfined
+};
+
+static void confinePointer(_GLFWwindow* window)
+{
+    window->wl.confinedPointer =
+        zwp_pointer_constraints_v1_confine_pointer(
+            _glfw.wl.pointerConstraints,
+            window->wl.surface,
+            _glfw.wl.pointer,
+            NULL,
+            ZWP_POINTER_CONSTRAINTS_V1_LIFETIME_PERSISTENT);
+
+    zwp_confined_pointer_v1_add_listener(window->wl.confinedPointer,
+                                         &confinedPointerListener,
+                                         window);
+}
+
+static void unconfinePointer(_GLFWwindow* window)
+{
+    zwp_confined_pointer_v1_destroy(window->wl.confinedPointer);
+    window->wl.confinedPointer = NULL;
+}
+
 void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
 {
     if (!_glfw.wl.pointer)
@@ -2553,17 +2593,29 @@ void _glfwSetCursorWayland(_GLFWwindow* window, _GLFWcursor* cursor)
     // Update pointer lock to match cursor mode
     if (window->cursorMode == GLFW_CURSOR_DISABLED)
     {
+        if (window->wl.confinedPointer)
+            unconfinePointer(window);
         if (!window->wl.lockedPointer)
             lockPointer(window);
     }
+    else if (window->cursorMode == GLFW_CURSOR_CAPTURED)
+    {
+        if (window->wl.lockedPointer)
+            unlockPointer(window);
+        if (!window->wl.confinedPointer)
+            confinePointer(window);
+    }
     else if (window->cursorMode == GLFW_CURSOR_NORMAL ||
              window->cursorMode == GLFW_CURSOR_HIDDEN)
     {
         if (window->wl.lockedPointer)
             unlockPointer(window);
+        else if (window->wl.confinedPointer)
+            unconfinePointer(window);
     }
 
-    if (window->cursorMode == GLFW_CURSOR_NORMAL)
+    if (window->cursorMode == GLFW_CURSOR_NORMAL ||
+        window->cursorMode == GLFW_CURSOR_CAPTURED)
     {
         if (cursor)
             setCursorImage(window, &cursor->wl);

+ 9 - 3
src/x11_window.c

@@ -453,7 +453,8 @@ static char* convertLatin1toUTF8(const char* source)
 //
 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)
         {
@@ -1705,6 +1706,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);
@@ -1725,6 +1728,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);
@@ -2831,7 +2836,7 @@ void _glfwSetCursorModeX11(_GLFWwindow* window, int mode)
                 disableRawMouseMotion(window);
         }
 
-        if (mode == GLFW_CURSOR_DISABLED)
+        if (mode == GLFW_CURSOR_DISABLED || mode == GLFW_CURSOR_CAPTURED)
             captureCursor(window);
         else
             releaseCursor();
@@ -3003,7 +3008,8 @@ void _glfwDestroyCursorX11(_GLFWcursor* cursor)
 
 void _glfwSetCursorX11(_GLFWwindow* window, _GLFWcursor* cursor)
 {
-    if (window->cursorMode == GLFW_CURSOR_NORMAL)
+    if (window->cursorMode == GLFW_CURSOR_NORMAL ||
+        window->cursorMode == GLFW_CURSOR_CAPTURED)
     {
         updateCursorImage(window);
         XFlush(_glfw.x11.display);

+ 7 - 1
tests/cursor.c

@@ -172,7 +172,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;
@@ -197,6 +198,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;