Fix cursor mode application for unfocused windows

Fixes #1239.
Fixes #1247.
This commit is contained in:
Camilla Löwy 2018-04-17 19:54:36 +02:00
parent 819a2205e5
commit 50eccd298a
4 changed files with 165 additions and 129 deletions

View File

@ -128,6 +128,32 @@ static void updateCursorImage(_GLFWwindow* window)
hideCursor(window); hideCursor(window);
} }
// Apply chosen cursor mode to a focused window
//
static void updateCursorMode(_GLFWwindow* window)
{
if (window->cursorMode == GLFW_CURSOR_DISABLED)
{
_glfw.ns.disabledCursorWindow = window;
_glfwPlatformGetCursorPos(window,
&_glfw.ns.restoreCursorPosX,
&_glfw.ns.restoreCursorPosY);
centerCursor(window);
CGAssociateMouseAndMouseCursorPosition(false);
}
else if (_glfw.ns.disabledCursorWindow == window)
{
_glfw.ns.disabledCursorWindow = NULL;
CGAssociateMouseAndMouseCursorPosition(true);
_glfwPlatformSetCursorPos(window,
_glfw.ns.restoreCursorPosX,
_glfw.ns.restoreCursorPosY);
}
if (cursorInClientArea(window))
updateCursorImage(window);
}
// Transforms the specified y-coordinate between the CG display and NS screen // Transforms the specified y-coordinate between the CG display and NS screen
// coordinate systems // coordinate systems
// //
@ -321,7 +347,7 @@ static const NSRange kEmptyRange = { NSNotFound, 0 };
centerCursor(window); centerCursor(window);
_glfwInputWindowFocus(window, GLFW_TRUE); _glfwInputWindowFocus(window, GLFW_TRUE);
_glfwPlatformSetCursorMode(window, window->cursorMode); updateCursorMode(window);
} }
- (void)windowDidResignKey:(NSNotification *)notification - (void)windowDidResignKey:(NSNotification *)notification
@ -1638,26 +1664,8 @@ void _glfwPlatformSetCursorPos(_GLFWwindow* window, double x, double y)
void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode) void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
{ {
if (mode == GLFW_CURSOR_DISABLED) if (_glfwPlatformWindowFocused(window))
{ updateCursorMode(window);
_glfw.ns.disabledCursorWindow = window;
_glfwPlatformGetCursorPos(window,
&_glfw.ns.restoreCursorPosX,
&_glfw.ns.restoreCursorPosY);
centerCursor(window);
CGAssociateMouseAndMouseCursorPosition(false);
}
else if (_glfw.ns.disabledCursorWindow == window)
{
_glfw.ns.disabledCursorWindow = NULL;
CGAssociateMouseAndMouseCursorPosition(true);
_glfwPlatformSetCursorPos(window,
_glfw.ns.restoreCursorPosX,
_glfw.ns.restoreCursorPosY);
}
if (cursorInClientArea(window))
updateCursorImage(window);
} }
const char* _glfwPlatformGetScancodeName(int scancode) const char* _glfwPlatformGetScancodeName(int scancode)

View File

@ -509,9 +509,7 @@ GLFWAPI void glfwSetInputMode(GLFWwindow* handle, int mode, int value)
_glfwPlatformGetCursorPos(window, _glfwPlatformGetCursorPos(window,
&window->virtualCursorPosX, &window->virtualCursorPosX,
&window->virtualCursorPosY); &window->virtualCursorPosY);
_glfwPlatformSetCursorMode(window, value);
if (_glfwPlatformWindowFocused(window))
_glfwPlatformSetCursorMode(window, value);
} }
else if (mode == GLFW_STICKY_KEYS) else if (mode == GLFW_STICKY_KEYS)
{ {

View File

@ -235,26 +235,6 @@ static void centerCursor(_GLFWwindow* window)
_glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0); _glfwPlatformSetCursorPos(window, width / 2.0, height / 2.0);
} }
// Returns whether the cursor is in the client area of the specified window
//
static GLFWbool cursorInClientArea(_GLFWwindow* window)
{
RECT area;
POINT pos;
if (!GetCursorPos(&pos))
return GLFW_FALSE;
if (WindowFromPoint(pos) != window->win32.handle)
return GLFW_FALSE;
GetClientRect(window->win32.handle, &area);
ClientToScreen(window->win32.handle, (POINT*) &area.left);
ClientToScreen(window->win32.handle, (POINT*) &area.right);
return PtInRect(&area, pos);
}
// Updates the cursor image according to its cursor mode // Updates the cursor image according to its cursor mode
// //
static void updateCursorImage(_GLFWwindow* window) static void updateCursorImage(_GLFWwindow* window)
@ -286,6 +266,67 @@ static void updateClipRect(_GLFWwindow* window)
ClipCursor(NULL); ClipCursor(NULL);
} }
// Apply disabled cursor mode to a focused window
//
static void disableCursor(_GLFWwindow* window)
{
const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle };
_glfw.win32.disabledCursorWindow = window;
_glfwPlatformGetCursorPos(window,
&_glfw.win32.restoreCursorPosX,
&_glfw.win32.restoreCursorPosY);
updateCursorImage(window);
centerCursor(window);
updateClipRect(window);
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to register raw input device");
}
}
// Exit disabled cursor mode for the specified window
//
static void enableCursor(_GLFWwindow* window)
{
const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL };
_glfw.win32.disabledCursorWindow = NULL;
updateClipRect(NULL);
_glfwPlatformSetCursorPos(window,
_glfw.win32.restoreCursorPosX,
_glfw.win32.restoreCursorPosY);
updateCursorImage(window);
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to remove raw input device");
}
}
// Returns whether the cursor is in the client area of the specified window
//
static GLFWbool cursorInClientArea(_GLFWwindow* window)
{
RECT area;
POINT pos;
if (!GetCursorPos(&pos))
return GLFW_FALSE;
if (WindowFromPoint(pos) != window->win32.handle)
return GLFW_FALSE;
GetClientRect(window->win32.handle, &area);
ClientToScreen(window->win32.handle, (POINT*) &area.left);
ClientToScreen(window->win32.handle, (POINT*) &area.right);
return PtInRect(&area, pos);
}
// Update native window styles to match attributes // Update native window styles to match attributes
// //
static void updateWindowStyles(const _GLFWwindow* window) static void updateWindowStyles(const _GLFWwindow* window)
@ -575,7 +616,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
if (lParam == 0 && window->win32.frameAction) if (lParam == 0 && window->win32.frameAction)
{ {
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); disableCursor(window);
window->win32.frameAction = GLFW_FALSE; window->win32.frameAction = GLFW_FALSE;
} }
@ -593,7 +634,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
break; break;
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); disableCursor(window);
return 0; return 0;
} }
@ -601,7 +642,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
case WM_KILLFOCUS: case WM_KILLFOCUS:
{ {
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); enableCursor(window);
if (window->monitor && window->autoIconify) if (window->monitor && window->autoIconify)
_glfwPlatformIconifyWindow(window); _glfwPlatformIconifyWindow(window);
@ -857,10 +898,10 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
case WM_ENTERSIZEMOVE: case WM_ENTERSIZEMOVE:
case WM_ENTERMENULOOP: case WM_ENTERMENULOOP:
{ {
// HACK: Postpone cursor disabling while the user is moving or // HACK: Enable the cursor while the user is moving or
// resizing the window or using the menu // resizing the window or using the window menu
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); enableCursor(window);
break; break;
} }
@ -871,7 +912,7 @@ static LRESULT CALLBACK windowProc(HWND hWnd, UINT uMsg,
// HACK: Disable the cursor once the user is done moving or // HACK: Disable the cursor once the user is done moving or
// resizing the window or using the menu // resizing the window or using the menu
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); disableCursor(window);
break; break;
} }
@ -1772,39 +1813,12 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
{ {
if (mode == GLFW_CURSOR_DISABLED) if (mode == GLFW_CURSOR_DISABLED)
{ {
const RAWINPUTDEVICE rid = { 0x01, 0x02, 0, window->win32.handle }; if (_glfwPlatformWindowFocused(window))
disableCursor(window);
_glfw.win32.disabledCursorWindow = window;
_glfwPlatformGetCursorPos(window,
&_glfw.win32.restoreCursorPosX,
&_glfw.win32.restoreCursorPosY);
centerCursor(window);
updateClipRect(window);
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to register raw input device");
}
} }
else if (_glfw.win32.disabledCursorWindow == window) else if (_glfw.win32.disabledCursorWindow == window)
{ enableCursor(window);
const RAWINPUTDEVICE rid = { 0x01, 0x02, RIDEV_REMOVE, NULL }; else if (cursorInClientArea(window))
_glfw.win32.disabledCursorWindow = NULL;
updateClipRect(NULL);
_glfwPlatformSetCursorPos(window,
_glfw.win32.restoreCursorPosX,
_glfw.win32.restoreCursorPosY);
if (!RegisterRawInputDevices(&rid, 1, sizeof(rid)))
{
_glfwInputErrorWin32(GLFW_PLATFORM_ERROR,
"Win32: Failed to remove raw input device");
}
}
if (cursorInClientArea(window))
updateCursorImage(window); updateCursorImage(window);
} }

View File

@ -571,6 +571,61 @@ static void updateCursorImage(_GLFWwindow* window)
} }
} }
// Apply disabled cursor mode to a focused window
//
static void disableCursor(_GLFWwindow* window)
{
if (_glfw.x11.xi.available)
{
XIEventMask em;
unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
em.deviceid = XIAllMasterDevices;
em.mask_len = sizeof(mask);
em.mask = mask;
XISetMask(mask, XI_RawMotion);
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
}
_glfw.x11.disabledCursorWindow = window;
_glfwPlatformGetCursorPos(window,
&_glfw.x11.restoreCursorPosX,
&_glfw.x11.restoreCursorPosY);
updateCursorImage(window);
centerCursor(window);
XGrabPointer(_glfw.x11.display, window->x11.handle, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync,
window->x11.handle,
_glfw.x11.hiddenCursorHandle,
CurrentTime);
}
// Exit disabled cursor mode for the specified window
//
static void enableCursor(_GLFWwindow* window)
{
if (_glfw.x11.xi.available)
{
XIEventMask em;
unsigned char mask[] = { 0 };
em.deviceid = XIAllMasterDevices;
em.mask_len = sizeof(mask);
em.mask = mask;
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
}
_glfw.x11.disabledCursorWindow = NULL;
XUngrabPointer(_glfw.x11.display, CurrentTime);
_glfwPlatformSetCursorPos(window,
_glfw.x11.restoreCursorPosX,
_glfw.x11.restoreCursorPosY);
updateCursorImage(window);
}
// Create the X11 window (and its colormap) // Create the X11 window (and its colormap)
// //
static GLFWbool createNativeWindow(_GLFWwindow* window, static GLFWbool createNativeWindow(_GLFWwindow* window,
@ -1432,7 +1487,7 @@ static void processEvent(XEvent *event)
// HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise // HACK: This is a workaround for WMs (KWM, Fluxbox) that otherwise
// ignore the defined cursor for hidden cursor mode // ignore the defined cursor for hidden cursor mode
if (window->cursorMode == GLFW_CURSOR_HIDDEN) if (window->cursorMode == GLFW_CURSOR_HIDDEN)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_HIDDEN); updateCursorImage(window);
_glfwInputCursorEnter(window, GLFW_TRUE); _glfwInputCursorEnter(window, GLFW_TRUE);
return; return;
@ -1725,7 +1780,7 @@ static void processEvent(XEvent *event)
case FocusIn: case FocusIn:
{ {
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_DISABLED); disableCursor(window);
if (event->xfocus.mode == NotifyGrab || if (event->xfocus.mode == NotifyGrab ||
event->xfocus.mode == NotifyUngrab) event->xfocus.mode == NotifyUngrab)
@ -1745,7 +1800,7 @@ static void processEvent(XEvent *event)
case FocusOut: case FocusOut:
{ {
if (window->cursorMode == GLFW_CURSOR_DISABLED) if (window->cursorMode == GLFW_CURSOR_DISABLED)
_glfwPlatformSetCursorMode(window, GLFW_CURSOR_NORMAL); enableCursor(window);
if (event->xfocus.mode == NotifyGrab || if (event->xfocus.mode == NotifyGrab ||
event->xfocus.mode == NotifyUngrab) event->xfocus.mode == NotifyUngrab)
@ -2708,53 +2763,14 @@ void _glfwPlatformSetCursorMode(_GLFWwindow* window, int mode)
{ {
if (mode == GLFW_CURSOR_DISABLED) if (mode == GLFW_CURSOR_DISABLED)
{ {
if (_glfw.x11.xi.available) if (_glfwPlatformWindowFocused(window))
{ disableCursor(window);
XIEventMask em;
unsigned char mask[XIMaskLen(XI_RawMotion)] = { 0 };
em.deviceid = XIAllMasterDevices;
em.mask_len = sizeof(mask);
em.mask = mask;
XISetMask(mask, XI_RawMotion);
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
}
_glfw.x11.disabledCursorWindow = window;
_glfwPlatformGetCursorPos(window,
&_glfw.x11.restoreCursorPosX,
&_glfw.x11.restoreCursorPosY);
centerCursor(window);
XGrabPointer(_glfw.x11.display, window->x11.handle, True,
ButtonPressMask | ButtonReleaseMask | PointerMotionMask,
GrabModeAsync, GrabModeAsync,
window->x11.handle,
_glfw.x11.hiddenCursorHandle,
CurrentTime);
} }
else if (_glfw.x11.disabledCursorWindow == window) else if (_glfw.x11.disabledCursorWindow == window)
{ enableCursor(window);
if (_glfw.x11.xi.available) else
{ updateCursorImage(window);
XIEventMask em;
unsigned char mask[] = { 0 };
em.deviceid = XIAllMasterDevices;
em.mask_len = sizeof(mask);
em.mask = mask;
XISelectEvents(_glfw.x11.display, _glfw.x11.root, &em, 1);
}
_glfw.x11.disabledCursorWindow = NULL;
XUngrabPointer(_glfw.x11.display, CurrentTime);
_glfwPlatformSetCursorPos(window,
_glfw.x11.restoreCursorPosX,
_glfw.x11.restoreCursorPosY);
}
updateCursorImage(window);
XFlush(_glfw.x11.display); XFlush(_glfw.x11.display);
} }