diff --git a/README.md b/README.md index 0573dcd93..32ea9cc2c 100644 --- a/README.md +++ b/README.md @@ -219,6 +219,7 @@ information on what to include when reporting a bug. - [Win32] Bugfix: The HID device notification was not unregistered (#1170) - [Win32] Bugfix: `glfwCreateWindow` activated window even with `GLFW_FOCUSED` hint set to false (#1179,#1180) +- [Win32] Bugfix: Disabled cursor mode did not work over remote desktop (#1276) - [X11] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125) - [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X @@ -458,6 +459,7 @@ skills. - Santi Zupancic - Jonas Ådahl - Lasse Öörni + - Pokechu22 - All the unmentioned and anonymous contributors in the GLFW community, for bug reports, patches, feedback, testing and encouragement diff --git a/src/win32_platform.h b/src/win32_platform.h index 598151468..a096eb341 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -279,6 +279,10 @@ typedef struct _GLFWwindowWin32 // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; + // Cached center positions + int centerX, centerY; + // An invisible cursor, needed for special cases (see WM_INPUT handler) + HCURSOR blankCursor; } _GLFWwindowWin32; diff --git a/src/win32_window.c b/src/win32_window.c index d467f2ae3..6b1ff29bd 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -230,9 +230,7 @@ static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area) // static void centerCursor(_GLFWwindow* window) { - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); + _glfwPlatformSetCursorPos(window, window->win32.centerX, window->win32.centerY); } // Updates the cursor image according to its cursor mode @@ -247,7 +245,7 @@ static void updateCursorImage(_GLFWwindow* window) SetCursor(LoadCursorW(NULL, IDC_ARROW)); } else - SetCursor(NULL); + SetCursor(window->win32.blankCursor); } // Updates the cursor clip rect @@ -856,11 +854,32 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, data = _glfw.win32.rawInput; if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) { - dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; - dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; + // As per https://github.com/Microsoft/DirectXTK/commit/ef56b63f3739381e451f7a5a5bd2c9779d2a7555 + // MOUSE_MOVE_ABSOLUTE is a range from 0 through 65535, based on the screen size. + // As far as I can tell, absolute mode only occurs over RDP though. + const int width = GetSystemMetrics((data->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) ? SM_CXVIRTUALSCREEN : SM_CXSCREEN); + const int height = GetSystemMetrics((data->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) ? SM_CYVIRTUALSCREEN : SM_CYSCREEN); + POINT pos; + pos.x = (int) ((data->data.mouse.lLastX / 65535.0f) * width); + pos.y = (int) ((data->data.mouse.lLastY / 65535.0f) * height); + ScreenToClient(window->win32.handle, &pos); + + // One other unfortunate thing is that re-centering the cursor will still fire an + // input event; assume that any motion to the center is our re-centering and ignore it + if (pos.x == window->win32.centerX && pos.y == window->win32.centerY) + break; + + dx = pos.x - window->win32.lastCursorPosX; + dy = pos.y - window->win32.lastCursorPosY; } else { + if (data->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) + { + _glfwInputError(GLFW_PLATFORM_ERROR, + "Win32: Unexpected raw input combination MOUSE_VIRTUAL_DESKTOP but not MOUSE_MOVE_ABSOLUTE"); + break; + } dx = data->data.mouse.lLastX; dy = data->data.mouse.lLastY; } @@ -923,6 +942,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, const GLFWbool maximized = wParam == SIZE_MAXIMIZED || (window->win32.maximized && wParam != SIZE_RESTORED); + int width, height; + _glfwPlatformGetWindowSize(window, &width, &height); if (_glfw.win32.disabledCursorWindow == window) updateClipRect(window); @@ -949,6 +970,9 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, window->win32.iconified = iconified; window->win32.maximized = maximized; + + window->win32.centerX = width / 2; + window->win32.centerY = height / 2; return 0; } @@ -1176,6 +1200,19 @@ static int createNativeWindow(_GLFWwindow* window, window->win32.transparent = GLFW_TRUE; } + // HACK: Create a transparent cursor as using the NULL cursor breaks + // using SetCursorPos when connected over RDP + int cursorWidth = GetSystemMetrics(SM_CXCURSOR); + int cursorHeight = GetSystemMetrics(SM_CYCURSOR); + unsigned char* andMask = calloc(cursorWidth * cursorHeight / 8, sizeof(unsigned char)); + memset(andMask, 0xFF, cursorWidth * cursorHeight / 8); + unsigned char* xorMask = calloc(cursorWidth * cursorHeight / 8, sizeof(unsigned char)); + // Cursor creation might fail, but that's fine as we get NULL in that case, + // which serves as an acceptable fallback blank cursor (other than on RDP) + window->win32.blankCursor = CreateCursor(NULL, 0, 0, cursorWidth, cursorHeight, andMask, xorMask); + free(andMask); + free(xorMask); + return GLFW_TRUE; } @@ -1313,6 +1350,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window) if (window->win32.smallIcon) DestroyIcon(window->win32.smallIcon); + + if (window->win32.blankCursor) + DestroyCursor(window->win32.blankCursor); } void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) @@ -1750,15 +1790,12 @@ void _glfwPlatformPollEvents(void) window = _glfw.win32.disabledCursorWindow; if (window) { - int width, height; - _glfwPlatformGetWindowSize(window, &width, &height); - // NOTE: Re-center the cursor only if it has moved since the last call, // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE - if (window->win32.lastCursorPosX != width / 2 || - window->win32.lastCursorPosY != height / 2) + if (window->win32.lastCursorPosX != window->win32.centerX || + window->win32.lastCursorPosY != window->win32.centerY) { - _glfwPlatformSetCursorPos(window, width / 2, height / 2); + centerCursor(window); } } } @@ -1806,7 +1843,11 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double xpos, double ypos) window->win32.lastCursorPosY = pos.y; ClientToScreen(window->win32.handle, &pos); - SetCursorPos(pos.x, pos.y); + if (!SetCursorPos(pos.x, pos.y)) + { + _glfwInputErrorWin32(GLFW_PLATFORM_ERROR, + "Win32: Failed to set cursor position"); + } } void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) diff --git a/tests/cursor.c b/tests/cursor.c index 4cc74ad51..6f51d1493 100644 --- a/tests/cursor.c +++ b/tests/cursor.c @@ -205,6 +205,14 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action, case GLFW_KEY_6: glfwSetCursor(window, standard_cursors[5]); break; + + case GLFW_KEY_M: + { + int width, height; + glfwGetWindowSize(window, &width, &height); + glfwSetCursorPos(window, width / 2, height / 2); + break; + } } }