diff --git a/README.md b/README.md index 67702c47..05f4d168 100644 --- a/README.md +++ b/README.md @@ -92,6 +92,8 @@ GLFW bundles a number of dependencies in the `deps/` directory. the default behavior - Changed static library to build as position independent code for easier use from the Rust language + - Changed `glfwGetCursorPos` to query the system directly for all cursor modes + except captured mode - Bugfix: The debug context attribute was set from `GL_ARB_debug_output` even when a debug context had not been requested - Bugfix: The particles example was not linked against the threading library diff --git a/docs/input.dox b/docs/input.dox index 422c480d..67027c58 100644 --- a/docs/input.dox +++ b/docs/input.dox @@ -228,10 +228,6 @@ double xpos, ypos; glfwGetCursorPos(window, &xpos, &ypos); @endcode -This function only returns cached cursor positions. It does not poll the -system for the current position. Whenever you poll state, you risk missing the -state change you are looking for. - @subsection input_cursor_mode Cursor modes diff --git a/docs/news.dox b/docs/news.dox index 16101cf9..4514ca2a 100644 --- a/docs/news.dox +++ b/docs/news.dox @@ -81,6 +81,13 @@ The support for EGL is now stable, successfully running on PandaBoards, Mesa, ANGLE, Wayland, AMD EGL and others. +@subsection news_31_getcursorpos Direct query for cursor position + +GLFW now queries the system cursor position directly when you call @ref +glfwGetCursorPos instead of returning cached data, except for when the window is +in captured cursor mode. + + @section news_30 New features in version 3.0 @subsection news_30_cmake CMake build system diff --git a/include/GLFW/glfw3.h b/include/GLFW/glfw3.h index 2cba521e..ef0280f3 100644 --- a/include/GLFW/glfw3.h +++ b/include/GLFW/glfw3.h @@ -2573,12 +2573,12 @@ GLFWAPI int glfwGetKey(GLFWwindow* window, int key); */ GLFWAPI int glfwGetMouseButton(GLFWwindow* window, int button); -/*! @brief Retrieves the last reported cursor position, relative to the client - * area of the window. +/*! @brief Retrieves the position of the cursor relative to the client area of + * the window. * - * This function returns the last reported position of the cursor, in screen - * coordinates, relative to the upper-left corner of the client area of the - * specified window. + * This function returns the position of the cursor, in screen coordinates, + * relative to the upper-left corner of the client area of the specified + * window. * * If the cursor is disabled (with `GLFW_CURSOR_DISABLED`) then the cursor * position is unbounded and limited only by the minimum and maximum values of diff --git a/src/cocoa_platform.h b/src/cocoa_platform.h index 6aafa325..a68ced09 100644 --- a/src/cocoa_platform.h +++ b/src/cocoa_platform.h @@ -63,6 +63,11 @@ typedef struct _GLFWwindowNS id view; unsigned int modifierFlags; + // The total sum of the distances the cursor has been warped + // since the last cursor motion event was processed + // This is kept to counteract Cocoa doing the same internally + double warpDeltaX, warpDeltaY; + } _GLFWwindowNS; diff --git a/src/cocoa_window.m b/src/cocoa_window.m index f84a8f69..d92915bc 100644 --- a/src/cocoa_window.m +++ b/src/cocoa_window.m @@ -156,25 +156,31 @@ static NSRect convertRectToBacking(_GLFWwindow* window, NSRect contentRect) - (void)windowDidResize:(NSNotification *)notification { + if (_glfw.focusedWindow == window && + window->cursorMode == GLFW_CURSOR_DISABLED) + { + centerCursor(window); + } + const NSRect contentRect = [window->ns.view frame]; const NSRect fbRect = convertRectToBacking(window, contentRect); _glfwInputFramebufferSize(window, fbRect.size.width, fbRect.size.height); _glfwInputWindowSize(window, contentRect.size.width, contentRect.size.height); _glfwInputWindowDamage(window); - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - centerCursor(window); } - (void)windowDidMove:(NSNotification *)notification { + if (_glfw.focusedWindow == window && + window->cursorMode == GLFW_CURSOR_DISABLED) + { + centerCursor(window); + } + int x, y; _glfwPlatformGetWindowPos(window, &x, &y); _glfwInputWindowPos(window, x, y); - - if (window->cursorMode == GLFW_CURSOR_DISABLED) - centerCursor(window); } - (void)windowDidMiniaturize:(NSNotification *)notification @@ -192,6 +198,12 @@ static NSRect convertRectToBacking(_GLFWwindow* window, NSRect contentRect) if (window->monitor) enterFullscreenMode(window); + if (_glfw.focusedWindow == window && + window->cursorMode == GLFW_CURSOR_DISABLED) + { + centerCursor(window); + } + _glfwInputWindowFocus(window, GL_TRUE); _glfwPlatformApplyCursorMode(window); } @@ -366,14 +378,21 @@ static int translateKey(unsigned int key) - (void)mouseMoved:(NSEvent *)event { if (window->cursorMode == GLFW_CURSOR_DISABLED) - _glfwInputCursorMotion(window, [event deltaX], [event deltaY]); + { + _glfwInputCursorMotion(window, + [event deltaX] - window->ns.warpDeltaX, + [event deltaY] - window->ns.warpDeltaY); + } else { const NSRect contentRect = [window->ns.view frame]; - const NSPoint p = [event locationInWindow]; + const NSPoint pos = [event locationInWindow]; - _glfwInputCursorMotion(window, p.x, contentRect.size.height - p.y); + _glfwInputCursorMotion(window, pos.x, contentRect.size.height - pos.y); } + + window->ns.warpDeltaX = 0; + window->ns.warpDeltaY = 0; } - (void)rightMouseDown:(NSEvent *)event @@ -563,11 +582,10 @@ static int translateKey(unsigned int key) NSPasteboard* pasteboard = [sender draggingPasteboard]; NSArray* files = [pasteboard propertyListForType:NSFilenamesPboardType]; - int height; - _glfwPlatformGetWindowSize(window, NULL, &height); + const NSRect contentRect = [window->ns.view frame]; _glfwInputCursorMotion(window, [sender draggingLocation].x, - height - [sender draggingLocation].y); + contentRect.size.height - [sender draggingLocation].y); const int count = [files count]; if (count) @@ -1098,10 +1116,27 @@ void _glfwPlatformPostEmptyEvent(void) [pool drain]; } +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + const NSRect contentRect = [window->ns.view frame]; + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; + + if (xpos) + *xpos = pos.x; + if (ypos) + *ypos = contentRect.size.height - pos.y - 1; +} + void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { updateModeCursor(window); + const NSRect contentRect = [window->ns.view frame]; + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; + + window->ns.warpDeltaX += x - pos.x; + window->ns.warpDeltaY += y - contentRect.size.height + pos.y; + if (window->monitor) { CGDisplayMoveCursorToPoint(window->monitor->ns.displayID, @@ -1109,7 +1144,6 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) } else { - const NSRect contentRect = [window->ns.view frame]; const NSPoint localPoint = NSMakePoint(x, contentRect.size.height - y - 1); const NSPoint globalPoint = [window->ns.object convertBaseToScreen:localPoint]; @@ -1123,10 +1157,7 @@ void _glfwPlatformApplyCursorMode(_GLFWwindow* window) updateModeCursor(window); if (window->cursorMode == GLFW_CURSOR_DISABLED) - { CGAssociateMouseAndMouseCursorPosition(false); - centerCursor(window); - } else CGAssociateMouseAndMouseCursorPosition(true); } @@ -1192,10 +1223,10 @@ void _glfwPlatformDestroyCursor(_GLFWcursor* cursor) void _glfwPlatformSetCursor(_GLFWwindow* window, _GLFWcursor* cursor) { - const NSPoint p = [window->ns.object mouseLocationOutsideOfEventStream]; + const NSPoint pos = [window->ns.object mouseLocationOutsideOfEventStream]; if (window->cursorMode == GLFW_CURSOR_NORMAL && - [window->ns.view mouse:p inRect:[window->ns.view frame]]) + [window->ns.view mouse:pos inRect:[window->ns.view frame]]) { if (cursor) [(NSCursor*) cursor->ns.object set]; diff --git a/src/input.c b/src/input.c index e3700700..74a715c9 100644 --- a/src/input.c +++ b/src/input.c @@ -59,20 +59,23 @@ static void setCursorMode(_GLFWwindow* window, int newMode) { if (oldMode == GLFW_CURSOR_DISABLED) { - window->cursorPosX = _glfw.cursorPosX; - window->cursorPosY = _glfw.cursorPosY; - - _glfwPlatformSetCursorPos(window, _glfw.cursorPosX, _glfw.cursorPosY); + _glfwPlatformSetCursorPos(window, + _glfw.cursorPosX, + _glfw.cursorPosY); } else if (newMode == GLFW_CURSOR_DISABLED) { int width, height; - _glfw.cursorPosX = window->cursorPosX; - _glfw.cursorPosY = window->cursorPosY; + _glfwPlatformGetCursorPos(window, + &_glfw.cursorPosX, + &_glfw.cursorPosY); + + window->cursorPosX = _glfw.cursorPosX; + window->cursorPosY = _glfw.cursorPosY; _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); + _glfwPlatformSetCursorPos(window, width / 2, height / 2); } _glfwPlatformApplyCursorMode(window); @@ -198,22 +201,13 @@ void _glfwInputCursorMotion(_GLFWwindow* window, double x, double y) window->cursorPosX += x; window->cursorPosY += y; - } - else - { - if (window->cursorPosX == x && window->cursorPosY == y) - return; - window->cursorPosX = x; - window->cursorPosY = y; + x = window->cursorPosX; + y = window->cursorPosY; } if (window->callbacks.cursorPos) - { - window->callbacks.cursorPos((GLFWwindow*) window, - window->cursorPosX, - window->cursorPosY); - } + window->callbacks.cursorPos((GLFWwindow*) window, x, y); } void _glfwInputCursorEnter(_GLFWwindow* window, int entered) @@ -332,10 +326,15 @@ GLFWAPI void glfwGetCursorPos(GLFWwindow* handle, double* xpos, double* ypos) _GLFW_REQUIRE_INIT(); - if (xpos) - *xpos = window->cursorPosX; - if (ypos) - *ypos = window->cursorPosY; + if (window->cursorMode == GLFW_CURSOR_DISABLED) + { + if (xpos) + *xpos = window->cursorPosX; + if (ypos) + *ypos = window->cursorPosY; + } + else + _glfwPlatformGetCursorPos(window, xpos, ypos); } GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) @@ -347,20 +346,17 @@ GLFWAPI void glfwSetCursorPos(GLFWwindow* handle, double xpos, double ypos) if (_glfw.focusedWindow != window) return; - // Don't do anything if the cursor position did not change - if (xpos == window->cursorPosX && ypos == window->cursorPosY) - return; - - // Set GLFW cursor position - window->cursorPosX = xpos; - window->cursorPosY = ypos; - - // Do not move physical cursor if it is disabled if (window->cursorMode == GLFW_CURSOR_DISABLED) - return; - - // Update physical cursor position - _glfwPlatformSetCursorPos(window, xpos, ypos); + { + // Only update the accumulated position if the cursor is disabled + window->cursorPosX = xpos; + window->cursorPosY = ypos; + } + else + { + // Update system cursor position + _glfwPlatformSetCursorPos(window, xpos, ypos); + } } GLFWAPI GLFWcursor* glfwCreateCursor(const GLFWimage* image, int xhot, int yhot) diff --git a/src/internal.h b/src/internal.h index 8852b617..c4733556 100644 --- a/src/internal.h +++ b/src/internal.h @@ -410,6 +410,11 @@ void _glfwPlatformTerminate(void); */ const char* _glfwPlatformGetVersionString(void); +/*! @copydoc glfwGetCursorPos + * @ingroup platform + */ +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos); + /*! @copydoc glfwSetCursorPos * @ingroup platform */ diff --git a/src/win32_platform.h b/src/win32_platform.h index 564c8611..1270350f 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -158,10 +158,10 @@ typedef struct _GLFWwindowWin32 DWORD dwStyle; DWORD dwExStyle; - GLboolean cursorCentered; GLboolean cursorInside; - GLboolean cursorHidden; - int oldCursorX, oldCursorY; + + // The last received cursor position, regardless of source + int cursorPosX, cursorPosY; } _GLFWwindowWin32; diff --git a/src/win32_window.c b/src/win32_window.c index 923900e8..c068fb36 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -55,15 +55,8 @@ static void hideCursor(_GLFWwindow* window) { POINT pos; - ReleaseCapture(); ClipCursor(NULL); - if (window->win32.cursorHidden) - { - ShowCursor(TRUE); - window->win32.cursorHidden = GL_FALSE; - } - if (GetCursorPos(&pos)) { if (WindowFromPoint(pos) == window->win32.handle) @@ -75,14 +68,15 @@ static void hideCursor(_GLFWwindow* window) // static void disableCursor(_GLFWwindow* window) { - if (!window->win32.cursorHidden) - { - ShowCursor(FALSE); - window->win32.cursorHidden = GL_TRUE; - } + POINT pos; updateClipRect(window); - SetCapture(window->win32.handle); + + if (GetCursorPos(&pos)) + { + if (WindowFromPoint(pos) == window->win32.handle) + SetCursor(NULL); + } } // Restores the mouse cursor @@ -91,15 +85,8 @@ static void restoreCursor(_GLFWwindow* window) { POINT pos; - ReleaseCapture(); ClipCursor(NULL); - if (window->win32.cursorHidden) - { - ShowCursor(TRUE); - window->win32.cursorHidden = GL_FALSE; - } - if (GetCursorPos(&pos)) { if (WindowFromPoint(pos) == window->win32.handle) @@ -439,34 +426,23 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, case WM_MOUSEMOVE: { - const int newCursorX = GET_X_LPARAM(lParam); - const int newCursorY = GET_Y_LPARAM(lParam); + const int x = GET_X_LPARAM(lParam); + const int y = GET_Y_LPARAM(lParam); - if (newCursorX != window->win32.oldCursorX || - newCursorY != window->win32.oldCursorY) + if (window->cursorMode == GLFW_CURSOR_DISABLED) { - int x, y; + if (_glfw.focusedWindow != window) + break; - if (window->cursorMode == GLFW_CURSOR_DISABLED) - { - if (_glfw.focusedWindow != window) - return 0; - - x = newCursorX - window->win32.oldCursorX; - y = newCursorY - window->win32.oldCursorY; - } - else - { - x = newCursorX; - y = newCursorY; - } - - window->win32.oldCursorX = newCursorX; - window->win32.oldCursorY = newCursorY; - window->win32.cursorCentered = GL_FALSE; - - _glfwInputCursorMotion(window, x, y); + _glfwInputCursorMotion(window, + x - window->win32.cursorPosX, + y - window->win32.cursorPosY); } + else + _glfwInputCursorMotion(window, x, y); + + window->win32.cursorPosX = x; + window->win32.cursorPosY = y; if (!window->win32.cursorInside) { @@ -1058,13 +1034,11 @@ void _glfwPlatformPollEvents(void) } // Did the cursor move in an focused window that has disabled the cursor - if (window->cursorMode == GLFW_CURSOR_DISABLED && - !window->win32.cursorCentered) + if (window->cursorMode == GLFW_CURSOR_DISABLED) { int width, height; _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); - window->win32.cursorCentered = GL_TRUE; + _glfwPlatformSetCursorPos(window, width / 2, height / 2); } } } @@ -1082,14 +1056,31 @@ void _glfwPlatformPostEmptyEvent(void) PostMessage(window->win32.handle, WM_NULL, 0, 0); } +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + POINT pos; + + if (GetCursorPos(&pos)) + { + ScreenToClient(window->win32.handle, &pos); + + if (xpos) + *xpos = pos.x; + if (ypos) + *ypos = pos.y; + } +} + void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) { POINT pos = { (int) xpos, (int) ypos }; + + // Store the new position so it can be recognized later + window->win32.cursorPosX = pos.x; + window->win32.cursorPosY = pos.y; + ClientToScreen(window->win32.handle, &pos); SetCursorPos(pos.x, pos.y); - - window->win32.oldCursorX = (int) xpos; - window->win32.oldCursorY = (int) ypos; } void _glfwPlatformApplyCursorMode(_GLFWwindow* window) diff --git a/src/x11_window.c b/src/x11_window.c index 09e63dcd..5b75b8aa 100644 --- a/src/x11_window.c +++ b/src/x11_window.c @@ -1066,32 +1066,28 @@ static void processEvent(XEvent *event) case MotionNotify: { - if (event->xmotion.x != window->x11.warpPosX || - event->xmotion.y != window->x11.warpPosY) + const int x = event->xmotion.x; + const int y = event->xmotion.y; + + if (x != window->x11.warpPosX || y != window->x11.warpPosY) { // The cursor was moved by something other than GLFW - int x, y; - if (window->cursorMode == GLFW_CURSOR_DISABLED) { if (_glfw.focusedWindow != window) break; - x = event->xmotion.x - window->x11.cursorPosX; - y = event->xmotion.y - window->x11.cursorPosY; + _glfwInputCursorMotion(window, + x - window->x11.cursorPosX, + y - window->x11.cursorPosY); } else - { - x = event->xmotion.x; - y = event->xmotion.y; - } - - _glfwInputCursorMotion(window, x, y); + _glfwInputCursorMotion(window, x, y); } - window->x11.cursorPosX = event->xmotion.x; - window->x11.cursorPosY = event->xmotion.y; + window->x11.cursorPosX = x; + window->x11.cursorPosY = y; break; } @@ -1744,6 +1740,23 @@ void _glfwPlatformPostEmptyEvent(void) XFlush(_glfw.x11.display); } +void _glfwPlatformGetCursorPos(_GLFWwindow* window, double* xpos, double* ypos) +{ + Window root, child; + int rootX, rootY, childX, childY; + unsigned int mask; + + XQueryPointer(_glfw.x11.display, window->x11.handle, + &root, &child, + &rootX, &rootY, &childX, &childY, + &mask); + + if (xpos) + *xpos = childX; + if (ypos) + *ypos = childY; +} + void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y) { // Store the new position so it can be recognized later