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
This commit is contained in:
Camilla Löwy 2019-12-03 17:58:20 +01:00 committed by Camilla Löwy
parent a46f829de8
commit 488008e0a2
11 changed files with 119 additions and 8 deletions

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
}

View File

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

View File

@ -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();

View File

@ -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;

View File

@ -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);

View File

@ -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);

View File

@ -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;