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 <tclem@codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard@winehq.org>
This commit is contained in:
Tim Clem 2021-09-14 13:22:45 -07:00 committed by Alexandre Julliard
parent b5e17b669a
commit bb28d54d08
2 changed files with 66 additions and 28 deletions

View File

@ -131,6 +131,7 @@ @interface WineApplicationController : NSObject <NSApplicationDelegate>
BOOL beenActive;
NSMutableSet* windowsBeingDragged;
BOOL useDragNotifications;
}
@property (nonatomic) CGEventSourceKeyboardType keyboardType;

View File

@ -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