From c8521b7fda997a9d8c0a1e03ede3cf9f942d3d2e Mon Sep 17 00:00:00 2001 From: Hilderin <81109165+Hilderin@users.noreply.github.com> Date: Sun, 28 Jan 2024 12:08:27 -0500 Subject: [PATCH] Win 32: Fix disabled cursor mode when connected over RDP Fixes #1276 Based on PR #1279 by @Pokechu22 Co-authored-by: Pokechu22 <8334194+Pokechu22@users.noreply.github.com> --- CONTRIBUTORS.md | 2 ++ README.md | 1 + src/win32_init.c | 48 ++++++++++++++++++++++++++++++++++++++++++++ src/win32_platform.h | 5 +++++ src/win32_window.c | 38 +++++++++++++++++++++++++++++++---- 5 files changed, 90 insertions(+), 4 deletions(-) diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 6741d82f..5346cf69 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -279,6 +279,8 @@ video tutorials. - Jonas Ådahl - Lasse Öörni - Leonard König + - Pokechu22 + - Guillaume Lebrun - All the unmentioned and anonymous contributors in the GLFW community, for bug reports, patches, feedback, testing and encouragement diff --git a/README.md b/README.md index 886ca66f..7515f327 100644 --- a/README.md +++ b/README.md @@ -236,6 +236,7 @@ information on what to include when reporting a bug. - [Win32] Bugfix: `glfwWaitEventsTimeout` did not return for some sent messages (#2408) - [Win32] Bugfix: Fix pkg-config for dynamic library on Windows (#2386, #2420) - [Win32] Bugfix: XInput could reportedly provide invalid DPad bit masks (#2291) + - [Win32] Bugfix: Disabled cursor mode doesn't work right when connected over RDP (#1276) - [Win32] Bugfix: Rapid clipboard calls could fail due to Clipboard History - [Cocoa] Added support for `VK_EXT_metal_surface` (#1619) - [Cocoa] Added locating the Vulkan loader at runtime in an application bundle diff --git a/src/win32_init.c b/src/win32_init.c index 4cb01adb..4cd17ec1 100644 --- a/src/win32_init.c +++ b/src/win32_init.c @@ -430,6 +430,47 @@ static GLFWbool createHelperWindow(void) return GLFW_TRUE; } +// Creates the blank cursor +// +static void createBlankCursor(void) +{ + // 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)); + unsigned char* xorMask = calloc(cursorWidth * cursorHeight / 8, sizeof(unsigned char)); + + if (andMask != NULL && xorMask != NULL) { + + memset(andMask, 0xFF, (size_t)(cursorWidth * cursorHeight / 8)); + + // 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) + _glfw.win32.blankCursor = CreateCursor(NULL, 0, 0, cursorWidth, cursorHeight, andMask, xorMask); + + free(andMask); + free(xorMask); + } + +} + +// Initialize for remote sessions +// +static void initRemoteSession(void) +{ + //Check if the current progress was started with Remote Desktop. + _glfw.win32.isRemoteSession = GetSystemMetrics(SM_REMOTESESSION) > 0; + + // With Remote desktop, we need to create a blank cursor because of the cursor is Set to NULL + // if cannot be moved to center in capture mode. If not Remote Desktop win32.blankCursor stays NULL + // and will perform has before (normal). + if (_glfw.win32.isRemoteSession) + { + createBlankCursor(); + } + +} ////////////////////////////////////////////////////////////////////////// ////// GLFW internal API ////// @@ -699,12 +740,19 @@ int _glfwInitWin32(void) if (!createHelperWindow()) return GLFW_FALSE; + //Some hacks are needed to support Remote Desktop... + initRemoteSession(); + _glfwPollMonitorsWin32(); return GLFW_TRUE; } void _glfwTerminateWin32(void) { + + if (_glfw.win32.blankCursor) + DestroyCursor(_glfw.win32.blankCursor); + if (_glfw.win32.deviceNotificationHandle) UnregisterDeviceNotification(_glfw.win32.deviceNotificationHandle); diff --git a/src/win32_platform.h b/src/win32_platform.h index 82b34bb9..5a817d05 100644 --- a/src/win32_platform.h +++ b/src/win32_platform.h @@ -430,6 +430,7 @@ typedef struct _GLFWwindowWin32 // The last received cursor position, regardless of source int lastCursorPosX, lastCursorPosY; + // The last received high surrogate when decoding pairs of UTF-16 messages WCHAR highSurrogate; } _GLFWwindowWin32; @@ -457,6 +458,10 @@ typedef struct _GLFWlibraryWin32 RAWINPUT* rawInput; int rawInputSize; UINT mouseTrailSize; + // Indicate if the process was started behind Remote Destop + BOOL isRemoteSession; + // An invisible cursor, needed for special cases (see WM_INPUT handler) + HCURSOR blankCursor; struct { HINSTANCE instance; diff --git a/src/win32_window.c b/src/win32_window.c index f7feb32d..6697f4e4 100644 --- a/src/win32_window.c +++ b/src/win32_window.c @@ -232,7 +232,10 @@ static void updateCursorImage(_GLFWwindow* window) SetCursor(LoadCursorW(NULL, IDC_ARROW)); } else - SetCursor(NULL); + //Connected via Remote Desktop, NULL cursor will present SetCursorPos the move the cursor. + //using a blank cursor fix that. + //When not via Remote Desktop, win32.blankCursor should be NULL + SetCursor(_glfw.win32.blankCursor); } // Sets the cursor clip rect to the window content area @@ -897,6 +900,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l HRAWINPUT ri = (HRAWINPUT) lParam; RAWINPUT* data = NULL; int dx, dy; + int width, height; + POINT pos; if (_glfw.win32.disabledCursorWindow != window) break; @@ -923,9 +928,30 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM l 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; + { + if (_glfw.win32.isRemoteSession) + { + //Remote Desktop Mode... + // 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. + width = GetSystemMetrics((data->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) ? SM_CXVIRTUALSCREEN : SM_CXSCREEN); + height = GetSystemMetrics((data->data.mouse.usFlags & MOUSE_VIRTUAL_DESKTOP) ? SM_CYVIRTUALSCREEN : SM_CYSCREEN); + + 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); + + dx = pos.x - window->win32.lastCursorPosX; + dy = pos.y - window->win32.lastCursorPosY; + } + else + { + //Normal mode... We should have the right absolute coords in data.mouse + dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; + dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; + } + } else { @@ -1432,11 +1458,13 @@ static int createNativeWindow(_GLFWwindow* window, window->win32.transparent = GLFW_TRUE; } + _glfwGetWindowSizeWin32(window, &window->win32.width, &window->win32.height); return GLFW_TRUE; } + GLFWbool _glfwCreateWindowWin32(_GLFWwindow* window, const _GLFWwndconfig* wndconfig, const _GLFWctxconfig* ctxconfig, @@ -1525,6 +1553,7 @@ void _glfwDestroyWindowWin32(_GLFWwindow* window) if (window->win32.smallIcon) DestroyIcon(window->win32.smallIcon); + } void _glfwSetWindowTitleWin32(_GLFWwindow* window, const char* title) @@ -2102,6 +2131,7 @@ void _glfwPollEventsWin32(void) // NOTE: Re-center the cursor only if it has moved since the last call, // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE + // The re-center is required in order to prevent the mouse cursor stopping at the edges of the screen. if (window->win32.lastCursorPosX != width / 2 || window->win32.lastCursorPosY != height / 2) {