From bb28d54d08023f6cea7be2c2c52545ab7f61e30b Mon Sep 17 00:00:00 2001 From: Tim Clem Date: Tue, 14 Sep 2021 13:22:45 -0700 Subject: [PATCH] winemac.drv: Use window drag notifications when available. When running on macOS 10.12+, there are private notification center messages we can use to reliably detect when a window is being dragged by its titlebar. These are less finicky than the current combination of an undocumented event subtype and a left mouse up. Signed-off-by: Tim Clem Signed-off-by: Alexandre Julliard --- dlls/winemac.drv/cocoa_app.h | 1 + dlls/winemac.drv/cocoa_app.m | 93 +++++++++++++++++++++++++----------- 2 files changed, 66 insertions(+), 28 deletions(-) diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 7abea0990e9..0b70a2fd55b 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -131,6 +131,7 @@ @interface WineApplicationController : NSObject BOOL beenActive; NSMutableSet* windowsBeingDragged; + BOOL useDragNotifications; } @property (nonatomic) CGEventSourceKeyboardType keyboardType; diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index e296d4b4af0..fe10ee8c41d 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -27,6 +27,12 @@ static NSString* const WineAppWaitQueryResponseMode = @"WineAppWaitQueryResponseMode"; +// Private notifications that are reliably dispatched when a window is moved by dragging its titlebar. +// The object of the notification is the window being dragged. +// Available in macOS 10.12+ +static NSString* const NSWindowWillStartDraggingNotification = @"NSWindowWillStartDraggingNotification"; +static NSString* const NSWindowDidEndDraggingNotification = @"NSWindowDidEndDraggingNotification"; + int macdrv_err_on; @@ -181,6 +187,15 @@ - (id) init windowsBeingDragged = [[NSMutableSet alloc] init]; + // On macOS 10.12+, use notifications to more reliably detect when windows are being dragged. + if ([NSProcessInfo instancesRespondToSelector:@selector(isOperatingSystemAtLeastVersion:)]) + { + NSOperatingSystemVersion requiredVersion = { 10, 12, 0 }; + useDragNotifications = [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:requiredVersion]; + } + else + useDragNotifications = NO; + if (!requests || !requestsManipQueue || !eventQueues || !eventQueuesLock || !keyWindows || !originalDisplayModes || !latentDisplayModes || !warpRecords) { @@ -1554,32 +1569,28 @@ - (void) windowWillOrderOut:(WineWindow*)window } } - - (void) handleWindowDrag:(NSEvent*)anEvent begin:(BOOL)begin + - (void) handleWindowDrag:(WineWindow*)window begin:(BOOL)begin { - WineWindow* window = (WineWindow*)[anEvent window]; - if ([window isKindOfClass:[WineWindow class]]) + macdrv_event* event; + int eventType; + + if (begin) { - macdrv_event* event; - int eventType; - - if (begin) - { - [windowsBeingDragged addObject:window]; - eventType = WINDOW_DRAG_BEGIN; - } - else - { - [windowsBeingDragged removeObject:window]; - eventType = WINDOW_DRAG_END; - } - [self updateCursorClippingState]; - - event = macdrv_create_event(eventType, window); - if (eventType == WINDOW_DRAG_BEGIN) - event->window_drag_begin.no_activate = [NSEvent wine_commandKeyDown]; - [window.queue postEvent:event]; - macdrv_release_event(event); + [windowsBeingDragged addObject:window]; + eventType = WINDOW_DRAG_BEGIN; } + else + { + [windowsBeingDragged removeObject:window]; + eventType = WINDOW_DRAG_END; + } + [self updateCursorClippingState]; + + event = macdrv_create_event(eventType, window); + if (eventType == WINDOW_DRAG_BEGIN) + event->window_drag_begin.no_activate = [NSEvent wine_commandKeyDown]; + [window.queue postEvent:event]; + macdrv_release_event(event); } - (void) handleMouseMove:(NSEvent*)anEvent @@ -1736,8 +1747,13 @@ - (void) handleMouseButton:(NSEvent*)theEvent WineWindow* windowBroughtForward = nil; BOOL process = FALSE; - if (type == NSEventTypeLeftMouseUp && [windowsBeingDragged count]) - [self handleWindowDrag:theEvent begin:NO]; + if (!useDragNotifications && + type == NSEventTypeLeftMouseUp && + [windowsBeingDragged count] && + [window isKindOfClass:[WineWindow class]]) + { + [self handleWindowDrag:window begin:NO]; + } if ([window isKindOfClass:[WineWindow class]] && type == NSEventTypeLeftMouseDown && @@ -2085,15 +2101,16 @@ - (BOOL) handleEvent:(NSEvent*)anEvent [window postKeyEvent:anEvent]; } } - else if (type == NSEventTypeAppKitDefined) + else if (!useDragNotifications && type == NSEventTypeAppKitDefined) { + WineWindow *window = (WineWindow *)[anEvent window]; short subtype = [anEvent subtype]; // These subtypes are not documented but they appear to mean // "a window is being dragged" and "a window is no longer being // dragged", respectively. - if (subtype == 20 || subtype == 21) - [self handleWindowDrag:anEvent begin:(subtype == 20)]; + if ((subtype == 20 || subtype == 21) && [window isKindOfClass:[WineWindow class]]) + [self handleWindowDrag:window begin:(subtype == 20)]; } return ret; @@ -2155,6 +2172,26 @@ - (void) setupObservations [self updateCursorClippingState]; }]; + if (useDragNotifications) { + [nc addObserverForName:NSWindowWillStartDraggingNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *note){ + NSWindow* window = [note object]; + if ([window isKindOfClass:[WineWindow class]]) + [self handleWindowDrag:(WineWindow *)window begin:YES]; + }]; + + [nc addObserverForName:NSWindowDidEndDraggingNotification + object:nil + queue:[NSOperationQueue mainQueue] + usingBlock:^(NSNotification *note){ + NSWindow* window = [note object]; + if ([window isKindOfClass:[WineWindow class]]) + [self handleWindowDrag:(WineWindow *)window begin:NO]; + }]; + } + [nc addObserver:self selector:@selector(keyboardSelectionDidChange) name:NSTextInputContextKeyboardSelectionDidChangeNotification