Centralizes all clipping behavior into -startClippingCursor: and -stopClippingCursor.
Signed-off-by: Tim Clem tclem@codeweavers.com --- The current methods are confusingly named and, in some cases, broken.
-updateCursorClippingState has an incomplete picture of when clipping should be enabled or disabled. Worse, -deactivateCursorClipping just disables the event tap without calling CGAssociateMouseAndMouseCursorPosition(true), so it will lock up the cursor if called while the app is active. There are two main scenarios where -updateCursorClippingState is called: when a window is being dragged and when the app is (de)activated.
The drag-related calls can be removed entirely because the handlers in window.c call ClipCursor(NULL) when a window is being dragged. (This is in fact the only reason that the cursor doesn't get locked up when windows are dragged right now.)
As for app (de)activation, the tap already ignores events if the app isn't active, so the -updateCursorClippingState calls there can be removed as well. (Moreover, Windows will cancel cursor clipping if the app loses focus; the next patch calls -stopClippingCursor when the app resigns active.)
dlls/winemac.drv/cocoa_app.m | 52 +++++++++++------------------------- 1 file changed, 15 insertions(+), 37 deletions(-)
diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index 2d18da7f99a8..806252651613 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -1456,33 +1456,6 @@ - (BOOL) setCursorPosition:(CGPoint)pos return ret; }
- - (void) activateCursorClipping - { - if (cursorClippingEventTap && !CGEventTapIsEnabled(cursorClippingEventTap)) - { - CGEventTapEnable(cursorClippingEventTap, TRUE); - [self setCursorPosition:NSPointToCGPoint([self flippedMouseLocation:[NSEvent mouseLocation]])]; - } - } - - - (void) deactivateCursorClipping - { - if (cursorClippingEventTap && CGEventTapIsEnabled(cursorClippingEventTap)) - { - CGEventTapEnable(cursorClippingEventTap, FALSE); - [warpRecords removeAllObjects]; - lastSetCursorPositionTime = [[NSProcessInfo processInfo] systemUptime]; - } - } - - - (void) updateCursorClippingState - { - if (clippingCursor && [NSApp isActive] && ![windowsBeingDragged count]) - [self activateCursorClipping]; - else - [self deactivateCursorClipping]; - } - - (void) updateWindowsForCursorClipping { WineWindow* window; @@ -1510,7 +1483,10 @@ - (BOOL) startClippingCursor:(CGRect)rect
clippingCursor = TRUE; cursorClipRect = rect; - [self updateCursorClippingState]; + + CGEventTapEnable(cursorClippingEventTap, TRUE); + [self setCursorPosition:NSPointToCGPoint([self flippedMouseLocation:[NSEvent mouseLocation]])]; + [self updateWindowsForCursorClipping];
return TRUE; @@ -1518,12 +1494,21 @@ - (BOOL) startClippingCursor:(CGRect)rect
- (BOOL) stopClippingCursor { - CGError err = CGAssociateMouseAndMouseCursorPosition(true); + CGError err; + + if (!clippingCursor) + return TRUE; + + err = CGAssociateMouseAndMouseCursorPosition(true); if (err != kCGErrorSuccess) return FALSE;
clippingCursor = FALSE; - [self updateCursorClippingState]; + + CGEventTapEnable(cursorClippingEventTap, FALSE); + [warpRecords removeAllObjects]; + lastSetCursorPositionTime = [[NSProcessInfo processInfo] systemUptime]; + [self updateWindowsForCursorClipping];
return TRUE; @@ -1554,7 +1539,6 @@ - (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged [windowsBeingDragged addObject:window]; else [windowsBeingDragged removeObject:window]; - [self updateCursorClippingState]; }
- (void) windowWillOrderOut:(WineWindow*)window @@ -1584,7 +1568,6 @@ - (void) handleWindowDrag:(WineWindow*)window begin:(BOOL)begin [windowsBeingDragged removeObject:window]; eventType = WINDOW_DRAG_END; } - [self updateCursorClippingState];
event = macdrv_create_event(eventType, window); if (eventType == WINDOW_DRAG_BEGIN) @@ -2161,7 +2144,6 @@ - (void) setupObservations }); } [windowsBeingDragged removeObject:window]; - [self updateCursorClippingState]; }];
if (useDragNotifications) { @@ -2305,8 +2287,6 @@ - (void)applicationDidBecomeActive:(NSNotification *)notification [self setMode:mode forDisplay:[displayID unsignedIntValue]]; }
- [self updateCursorClippingState]; - [self updateFullscreenWindows]; [self adjustWindowLevels:YES];
@@ -2349,8 +2329,6 @@ - (void)applicationDidResignActive:(NSNotification *)notification macdrv_event* event; WineEventQueue* queue;
- [self updateCursorClippingState]; - [self invalidateGotFocusEvents];
event = macdrv_create_event(APP_DEACTIVATED, nil);
Testing on Windows confirms this behavior.
Signed-off-by: Tim Clem tclem@codeweavers.com --- dlls/winemac.drv/cocoa_app.m | 1 + 1 file changed, 1 insertion(+)
diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index 806252651613..1e3fd5a9d339 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -2340,6 +2340,7 @@ - (void)applicationDidResignActive:(NSNotification *)notification
macdrv_release_event(event);
+ [self stopClippingCursor]; [self releaseMouseCapture]; }
Testing on Windows confirms this behavior.
Signed-off-by: Tim Clem tclem@codeweavers.com --- For undecorated windows, this already happens via WINPOS_SysCommandSizeMove, which explicitly calls ClipCursor(NULL). For decorated windows, that WM_SYSCOMMAND isn't issued for live resizes, so this callback to the application contoller is necessary.
Note that ef46771 locked windows when the cursor is clipped by default. The Mac driver registry key CursorClippingLocksWindows must be set to N to enable this behavior.
dlls/winemac.drv/cocoa_app.h | 1 + dlls/winemac.drv/cocoa_app.m | 5 +++++ dlls/winemac.drv/cocoa_window.m | 2 ++ 3 files changed, 8 insertions(+)
diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 0b70a2fd55b5..5db083d8e7bd 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -157,6 +157,7 @@ - (BOOL) waitUntilQueryDone:(int*)done timeout:(NSDate*)timeout processEvents:(B - (void) noteKey:(uint16_t)keyCode pressed:(BOOL)pressed;
- (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged; + - (void) windowWillStartLiveResize:(WineWindow*)window; - (void) windowWillOrderOut:(WineWindow*)window;
- (void) flipRect:(NSRect*)rect; diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index 1e3fd5a9d339..bace8b3973b1 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -1541,6 +1541,11 @@ - (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged [windowsBeingDragged removeObject:window]; }
+ - (void) windowWillStartLiveResize:(WineWindow*)window + { + [self stopClippingCursor]; + } + - (void) windowWillOrderOut:(WineWindow*)window { if ([windowsBeingDragged containsObject:window]) diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 80cb26934096..2e63e9645189 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -3060,6 +3060,8 @@ - (void) windowWillStartLiveResize:(NSNotification *)notification { [self endWindowDragging];
+ [[WineApplicationController sharedController] windowWillStartLiveResize:self]; + if (maximized) { macdrv_event* event;
There's no analogous state on Windows, where an app is focused but has no visible windows, but this seems like the best behavior.
Signed-off-by: Tim Clem tclem@codeweavers.com --- dlls/winemac.drv/cocoa_app.h | 1 + dlls/winemac.drv/cocoa_app.m | 12 ++++++++++++ dlls/winemac.drv/cocoa_window.m | 3 +++ 3 files changed, 16 insertions(+)
diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 5db083d8e7bd..185ba2ceb707 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -158,6 +158,7 @@ - (void) noteKey:(uint16_t)keyCode pressed:(BOOL)pressed;
- (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged; - (void) windowWillStartLiveResize:(WineWindow*)window; + - (void) windowDidMiniaturize:(WineWindow*)window; - (void) windowWillOrderOut:(WineWindow*)window;
- (void) flipRect:(NSRect*)rect; diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index bace8b3973b1..bd2ef4c8b1e3 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -1546,6 +1546,18 @@ - (void) windowWillStartLiveResize:(WineWindow*)window [self stopClippingCursor]; }
+ - (void) windowDidMiniaturize:(WineWindow *)window + { + /* If all our windows are minimized, disable cursor clipping. */ + for (WineWindow* w in [NSApp windows]) + { + if ([w isKindOfClass:[WineWindow class]] && ![w isMiniaturized] && [w isVisible]) + return; + } + + [self stopClippingCursor]; + } + - (void) windowWillOrderOut:(WineWindow*)window { if ([windowsBeingDragged containsObject:window]) diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 2e63e9645189..883c70ded928 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -2898,6 +2898,9 @@ - (void)windowDidMiniaturize:(NSNotification *)notification { if (fullscreen && [self isOnActiveSpace]) [[WineApplicationController sharedController] updateFullscreenWindows]; + + [[WineApplicationController sharedController] windowDidMiniaturize:self]; + [self checkWineDisplayLink]; }
We no longer enable or disable the event tap manually, and it re-enables itself on kCGEventTapDisabledByTimeout, so this check is not needed.
Signed-off-by: Tim Clem tclem@codeweavers.com --- dlls/winemac.drv/cocoa_app.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index bd2ef4c8b1e3..0f8a9c0876ad 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -1473,8 +1473,7 @@ - (BOOL) startClippingCursor:(CGRect)rect if (!cursorClippingEventTap && ![self installEventTap]) return FALSE;
- if (clippingCursor && CGRectEqualToRect(rect, cursorClipRect) && - CGEventTapIsEnabled(cursorClippingEventTap)) + if (clippingCursor && CGRectEqualToRect(rect, cursorClipRect)) return TRUE;
err = CGAssociateMouseAndMouseCursorPosition(false);
Actually, hold off on merging these. I need to make sure that when the driver stops clipping, it also updates the Win32 side of things. Probably that will mean no direct calls to -stopClippingCursor, and instead effectively calling ClipCursor(NULL).
October 14, 2021 11:39 AM, "Tim Clem" tclem@codeweavers.com wrote:
We no longer enable or disable the event tap manually, and it re-enables itself on kCGEventTapDisabledByTimeout, so this check is not needed.
Signed-off-by: Tim Clem tclem@codeweavers.com
dlls/winemac.drv/cocoa_app.m | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index bd2ef4c8b1e3..0f8a9c0876ad 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -1473,8 +1473,7 @@ - (BOOL) startClippingCursor:(CGRect)rect if (!cursorClippingEventTap && ![self installEventTap]) return FALSE;
- if (clippingCursor && CGRectEqualToRect(rect, cursorClipRect) &&
- CGEventTapIsEnabled(cursorClippingEventTap))
- if (clippingCursor && CGRectEqualToRect(rect, cursorClipRect))
return TRUE;
err = CGAssociateMouseAndMouseCursorPosition(false);
2.33.0