diff --git a/README.md b/README.md index 3cd9884c..dd5f9f1c 100644 --- a/README.md +++ b/README.md @@ -66,6 +66,8 @@ GLFW bundles a number of dependencies in the `deps/` directory. ## Changelog + - Added `GLFW_CURSOR_CAPTURED` cursor mode for confining the cursor to the + client area of a window - Made all client API functions dynamically loaded - Changed minimum required CMake version to 2.8.12 - Replaced GLU with [linmath.h](https://github.com/datenwolf/linmath.h) in diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 66232096..2c0753d2 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -642,6 +642,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 diff --git a/src/cocoa_window.m b/src/cocoa_window.m index b7486ad3..4f27ef2c 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -1193,6 +1193,11 @@ void _glfwPlatformApplyCursorMode(_GLFWwindow* window) CGAssociateMouseAndMouseCursorPosition(false); else CGAssociateMouseAndMouseCursorPosition(true); + + if (window->cursorMode == GLFW_CURSOR_CAPTURED) + { + // TODO: Implement cursor capture + } } int _glfwPlatformCreateCursor(_GLFWcursor* cursor, diff --git a/src/input.c b/src/input.c index eefbf086..e6ae3412 100644 --- a/src/input.c +++ b/src/input.c @@ -44,7 +44,8 @@ static void setCursorMode(_GLFWwindow* window, int newMode) if (newMode != GLFW_CURSOR_NORMAL && newMode != GLFW_CURSOR_HIDDEN && - newMode != GLFW_CURSOR_DISABLED) + newMode != GLFW_CURSOR_DISABLED && + newMode != GLFW_CURSOR_CAPTURED) { _glfwInputError(GLFW_INVALID_ENUM, "Invalid cursor mode"); return; diff --git a/src/win32_window.c b/src/win32_window.c index bd24cfb1..bf3c4408 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -110,6 +110,20 @@ static void disableCursor(_GLFWwindow* window) } } +// Capture the mouse cursor +// +static void captureCursor(_GLFWwindow* window) +{ + if (window->win32.cursorHidden) + { + ShowCursor(TRUE); + window->win32.cursorHidden = GL_FALSE; + } + + updateClipRect(window); + SetCapture(window->win32.handle); +} + // Restores the mouse cursor // static void restoreCursor(_GLFWwindow* window) @@ -510,8 +524,11 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { if (_glfw.cursorWindow == window) { - if (window->cursorMode == GLFW_CURSOR_DISABLED) + if (window->cursorMode == GLFW_CURSOR_DISABLED || + window->cursorMode == GLFW_CURSOR_CAPTURED) + { updateClipRect(window); + } } if (!window->win32.iconified && wParam == SIZE_MINIMIZED) @@ -541,8 +558,11 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, { if (_glfw.cursorWindow == window) { - if (window->cursorMode == GLFW_CURSOR_DISABLED) + if (window->cursorMode == GLFW_CURSOR_DISABLED || + window->cursorMode == GLFW_CURSOR_CAPTURED) + { updateClipRect(window); + } } // NOTE: This cannot use LOWORD/HIWORD recommended by MSDN, as @@ -1106,6 +1126,9 @@ void _glfwPlatformApplyCursorMode(_GLFWwindow* window) case GLFW_CURSOR_DISABLED: disableCursor(window); break; + case GLFW_CURSOR_CAPTURED: + captureCursor(window); + break; } } diff --git a/src/x11_window.c b/src/x11_window.c index d5c91f69..b9343caa 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -520,6 +520,16 @@ static void disableCursor(_GLFWwindow* window) window->x11.handle, _glfw.x11.cursor, CurrentTime); } +// Capture the mouse cursor +// +static void captureCursor(_GLFWwindow* window) +{ + XGrabPointer(_glfw.x11.display, window->x11.handle, True, + ButtonPressMask | ButtonReleaseMask | PointerMotionMask, + GrabModeAsync, GrabModeAsync, + window->x11.handle, None, CurrentTime); +} + // Restores the mouse cursor // static void restoreCursor(_GLFWwindow* window) @@ -1324,6 +1334,8 @@ static void processEvent(XEvent *event) if (window->cursorMode == GLFW_CURSOR_DISABLED) disableCursor(window); + else if (window->cursorMode == GLFW_CURSOR_CAPTURED) + captureCursor(window); _glfwInputWindowFocus(window, GL_TRUE); return; @@ -1342,8 +1354,11 @@ static void processEvent(XEvent *event) if (window->x11.ic) XUnsetICFocus(window->x11.ic); - if (window->cursorMode == GLFW_CURSOR_DISABLED) + if (window->cursorMode == GLFW_CURSOR_DISABLED || + window->cursorMode == GLFW_CURSOR_CAPTURED) + { restoreCursor(window); + } if (window->monitor && window->autoIconify) _glfwPlatformIconifyWindow(window); @@ -1889,6 +1904,9 @@ void _glfwPlatformApplyCursorMode(_GLFWwindow* window) case GLFW_CURSOR_DISABLED: disableCursor(window); break; + case GLFW_CURSOR_CAPTURED: + captureCursor(window); + break; } } diff --git a/tests/peter.c b/tests/peter.c new file mode 100644 index 00000000..a2de88b2 --- /dev/null +++ b/tests/peter.c @@ -0,0 +1,155 @@ +//======================================================================== +// Cursor mode test +// Copyright (c) Camilla Berglund +// +// This software is provided 'as-is', without any express or implied +// warranty. In no event will the authors be held liable for any damages +// arising from the use of this software. +// +// Permission is granted to anyone to use this software for any purpose, +// including commercial applications, and to alter it and redistribute it +// freely, subject to the following restrictions: +// +// 1. The origin of this software must not be misrepresented; you must not +// claim that you wrote the original software. If you use this software +// in a product, an acknowledgment in the product documentation would +// be appreciated but is not required. +// +// 2. Altered source versions must be plainly marked as such, and must not +// be misrepresented as being the original software. +// +// 3. This notice may not be removed or altered from any source +// distribution. +// +//======================================================================== +// +// This test allows you to switch between the various cursor modes +// +//======================================================================== + +#include + +#include +#include + +static GLboolean reopen = GL_FALSE; +static double cursor_x; +static double cursor_y; + +static void error_callback(int error, const char* description) +{ + fprintf(stderr, "Error: %s\n", description); +} + +static void cursor_position_callback(GLFWwindow* window, double x, double y) +{ + printf("%0.3f: Cursor position: %f %f (%f %f)\n", + glfwGetTime(), + x, y, x - cursor_x, y - cursor_y); + + cursor_x = x; + cursor_y = y; +} + +static void key_callback(GLFWwindow* window, int key, int scancode, int action, int mods) +{ + if (action != GLFW_PRESS) + return; + + switch (key) + { + case GLFW_KEY_C: + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_CAPTURED); + break; + + case GLFW_KEY_D: + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_DISABLED); + printf("(( cursor is disabled ))\n"); + break; + + case GLFW_KEY_H: + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_HIDDEN); + printf("(( cursor is hidden ))\n"); + break; + + case GLFW_KEY_N: + glfwSetInputMode(window, GLFW_CURSOR, GLFW_CURSOR_NORMAL); + printf("(( cursor is normal ))\n"); + break; + + case GLFW_KEY_R: + reopen = GL_TRUE; + break; + } +} + +static void framebuffer_size_callback(GLFWwindow* window, int width, int height) +{ + glViewport(0, 0, width, height); +} + +static GLFWwindow* open_window(void) +{ + GLFWwindow* window = glfwCreateWindow(640, 480, "Peter Detector", NULL, NULL); + if (!window) + return NULL; + + glfwMakeContextCurrent(window); + glfwSwapInterval(1); + + glfwGetCursorPos(window, &cursor_x, &cursor_y); + printf("Cursor position: %f %f\n", cursor_x, cursor_y); + + glfwSetFramebufferSizeCallback(window, framebuffer_size_callback); + glfwSetCursorPosCallback(window, cursor_position_callback); + glfwSetKeyCallback(window, key_callback); + + return window; +} + +int main(void) +{ + GLFWwindow* window; + + glfwSetErrorCallback(error_callback); + + if (!glfwInit()) + exit(EXIT_FAILURE); + + window = open_window(); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + glClearColor(0.f, 0.f, 0.f, 0.f); + + while (!glfwWindowShouldClose(window)) + { + glClear(GL_COLOR_BUFFER_BIT); + + glfwSwapBuffers(window); + glfwWaitEvents(); + + if (reopen) + { + glfwDestroyWindow(window); + window = open_window(); + if (!window) + { + glfwTerminate(); + exit(EXIT_FAILURE); + } + + reopen = GL_FALSE; + } + + // Workaround for an issue with msvcrt and mintty + fflush(stdout); + } + + glfwTerminate(); + exit(EXIT_SUCCESS); +} +