Fix disabled cursor mode over remote desktop

There were two major issues:

- RDP uses MOUSE_MOVE_ABSOLUTE for its input events, and the
implementation of this was incorrect.  Although poorly documented,
the values actually range from 0-65535 and represent monitor positions,
instead of being positions beforehand.  The equally poorly documented
MOUSE_VIRTUAL_DESKTOP flag specifies whether or not to use the
SCREEN or VIRTUALSCREEN system metrics.

- Using `SetCursor(NULL)` causes `SetCursorPos` to behave incorrectly
over RDP; it doesn't actually move it on the connected machine.
This has been fixed by creating an invisible cursor, which does
get moved correctly.

Aside from that, the center of the window is now cached and the cursor
test now supports pressing M to move the cursor to the center.

Fixes #1276.
This commit is contained in:
Pokechu22 2018-05-25 14:11:40 -07:00
parent bf6551a3ca
commit 49914ab15b
4 changed files with 68 additions and 13 deletions

View File

@ -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: The HID device notification was not unregistered (#1170)
- [Win32] Bugfix: `glfwCreateWindow` activated window even with `GLFW_FOCUSED` - [Win32] Bugfix: `glfwCreateWindow` activated window even with `GLFW_FOCUSED`
hint set to false (#1179,#1180) 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] Moved to XI2 `XI_RawMotion` for disable cursor mode motion input (#125)
- [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading - [X11] Replaced `_GLFW_HAS_XF86VM` compile-time option with dynamic loading
- [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X - [X11] Bugfix: `glfwGetVideoMode` would segfault on Cygwin/X
@ -458,6 +459,7 @@ skills.
- Santi Zupancic - Santi Zupancic
- Jonas Ådahl - Jonas Ådahl
- Lasse Öörni - Lasse Öörni
- Pokechu22
- All the unmentioned and anonymous contributors in the GLFW community, for bug - All the unmentioned and anonymous contributors in the GLFW community, for bug
reports, patches, feedback, testing and encouragement reports, patches, feedback, testing and encouragement

View File

@ -279,6 +279,10 @@ typedef struct _GLFWwindowWin32
// The last received cursor position, regardless of source // The last received cursor position, regardless of source
int lastCursorPosX, lastCursorPosY; int lastCursorPosX, lastCursorPosY;
// Cached center positions
int centerX, centerY;
// An invisible cursor, needed for special cases (see WM_INPUT handler)
HCURSOR blankCursor;
} _GLFWwindowWin32; } _GLFWwindowWin32;

View File

@ -230,9 +230,7 @@ static void applyAspectRatio(_GLFWwindow* window, int edge, RECT* area)
// //
static void centerCursor(_GLFWwindow* window) static void centerCursor(_GLFWwindow* window)
{ {
int width, height; _glfwPlatformSetCursorPos(window, window->win32.centerX, window->win32.centerY);
_glfwPlatformGetWindowSize(window, &width, &height);
_glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
} }
// Updates the cursor image according to its cursor mode // Updates the cursor image according to its cursor mode
@ -247,7 +245,7 @@ static void updateCursorImage(_GLFWwindow* window)
SetCursor(LoadCursorW(NULL, IDC_ARROW)); SetCursor(LoadCursorW(NULL, IDC_ARROW));
} }
else else
SetCursor(NULL); SetCursor(window->win32.blankCursor);
} }
// Updates the cursor clip rect // Updates the cursor clip rect
@ -856,11 +854,32 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
data = _glfw.win32.rawInput; data = _glfw.win32.rawInput;
if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE) if (data->data.mouse.usFlags & MOUSE_MOVE_ABSOLUTE)
{ {
dx = data->data.mouse.lLastX - window->win32.lastCursorPosX; // As per https://github.com/Microsoft/DirectXTK/commit/ef56b63f3739381e451f7a5a5bd2c9779d2a7555
dy = data->data.mouse.lLastY - window->win32.lastCursorPosY; // 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 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; dx = data->data.mouse.lLastX;
dy = data->data.mouse.lLastY; dy = data->data.mouse.lLastY;
} }
@ -923,6 +942,8 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
const GLFWbool maximized = wParam == SIZE_MAXIMIZED || const GLFWbool maximized = wParam == SIZE_MAXIMIZED ||
(window->win32.maximized && (window->win32.maximized &&
wParam != SIZE_RESTORED); wParam != SIZE_RESTORED);
int width, height;
_glfwPlatformGetWindowSize(window, &width, &height);
if (_glfw.win32.disabledCursorWindow == window) if (_glfw.win32.disabledCursorWindow == window)
updateClipRect(window); updateClipRect(window);
@ -949,6 +970,9 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
window->win32.iconified = iconified; window->win32.iconified = iconified;
window->win32.maximized = maximized; window->win32.maximized = maximized;
window->win32.centerX = width / 2;
window->win32.centerY = height / 2;
return 0; return 0;
} }
@ -1176,6 +1200,19 @@ static int createNativeWindow(_GLFWwindow* window,
window->win32.transparent = GLFW_TRUE; 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; return GLFW_TRUE;
} }
@ -1313,6 +1350,9 @@ void _glfwPlatformDestroyWindow(_GLFWwindow* window)
if (window->win32.smallIcon) if (window->win32.smallIcon)
DestroyIcon(window->win32.smallIcon); DestroyIcon(window->win32.smallIcon);
if (window->win32.blankCursor)
DestroyCursor(window->win32.blankCursor);
} }
void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title) void _glfwPlatformSetWindowTitle(_GLFWwindow* window, const char* title)
@ -1750,15 +1790,12 @@ void _glfwPlatformPollEvents(void)
window = _glfw.win32.disabledCursorWindow; window = _glfw.win32.disabledCursorWindow;
if (window) if (window)
{ {
int width, height;
_glfwPlatformGetWindowSize(window, &width, &height);
// NOTE: Re-center the cursor only if it has moved since the last call, // NOTE: Re-center the cursor only if it has moved since the last call,
// to avoid breaking glfwWaitEvents with WM_MOUSEMOVE // to avoid breaking glfwWaitEvents with WM_MOUSEMOVE
if (window->win32.lastCursorPosX != width / 2 || if (window->win32.lastCursorPosX != window->win32.centerX ||
window->win32.lastCursorPosY != height / 2) 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; window->win32.lastCursorPosY = pos.y;
ClientToScreen(window->win32.handle, &pos); 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) void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)

View File

@ -205,6 +205,14 @@ static void key_callback(GLFWwindow* window, int key, int scancode, int action,
case GLFW_KEY_6: case GLFW_KEY_6:
glfwSetCursor(window, standard_cursors[5]); glfwSetCursor(window, standard_cursors[5]);
break; break;
case GLFW_KEY_M:
{
int width, height;
glfwGetWindowSize(window, &width, &height);
glfwSetCursorPos(window, width / 2, height / 2);
break;
}
} }
} }