mirror of
git://source.winehq.org/git/wine.git
synced 2024-10-04 19:28:40 +00:00
winemac.drv: Support cooperative app activation in macOS 14 Sonoma.
Starting in Sonoma, apps can no longer force themselves to the foreground with -activateIgnoringOtherApps:. winemac currently does that in a few places - when an app creates its first window, and in the implementation of APIs like SetFocus. There's nothing we can do to work around the new behavior in the general case. This patch makes Wine apps running in the same prefix yield to one another, so that windows from multiple EXEs can at least behave as intended.
This commit is contained in:
parent
a1d429dabd
commit
fd67d67850
|
@ -142,6 +142,7 @@ @interface WineApplicationController : NSObject <NSApplicationDelegate>
|
|||
+ (WineApplicationController*) sharedController;
|
||||
|
||||
- (void) transformProcessToForeground:(BOOL)activateIfTransformed;
|
||||
- (void) tryToActivateIgnoringOtherApps:(BOOL)ignore;
|
||||
|
||||
- (BOOL) registerEventQueue:(WineEventQueue*)queue;
|
||||
- (void) unregisterEventQueue:(WineEventQueue*)queue;
|
||||
|
|
|
@ -34,6 +34,12 @@
|
|||
static NSString* const NSWindowWillStartDraggingNotification = @"NSWindowWillStartDraggingNotification";
|
||||
static NSString* const NSWindowDidEndDraggingNotification = @"NSWindowDidEndDraggingNotification";
|
||||
|
||||
// Internal distributed notification to handle cooperative app activation in Sonoma.
|
||||
static NSString* const WineAppWillActivateNotification = @"WineAppWillActivateNotification";
|
||||
static NSString* const WineActivatingAppPIDKey = @"ActivatingAppPID";
|
||||
static NSString* const WineActivatingAppPrefixKey = @"ActivatingAppPrefix";
|
||||
static NSString* const WineActivatingAppConfigDirKey = @"ActivatingAppConfigDir";
|
||||
|
||||
|
||||
int macdrv_err_on;
|
||||
|
||||
|
@ -47,6 +53,24 @@ + (void) setAllowsAutomaticWindowTabbing:(BOOL)allows;
|
|||
#endif
|
||||
|
||||
|
||||
#if !defined(MAC_OS_VERSION_14_0) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_VERSION_14_0
|
||||
@interface NSApplication (CooperativeActivationSelectorsForOldSDKs)
|
||||
|
||||
- (void)activate;
|
||||
- (void)yieldActivationToApplication:(NSRunningApplication *)application;
|
||||
- (void)yieldActivationToApplicationWithBundleIdentifier:(NSString *)bundleIdentifier;
|
||||
|
||||
@end
|
||||
|
||||
@interface NSRunningApplication (CooperativeActivationSelectorsForOldSDKs)
|
||||
|
||||
- (BOOL)activateFromApplication:(NSRunningApplication *)application
|
||||
options:(NSApplicationActivationOptions)options;
|
||||
|
||||
@end
|
||||
#endif
|
||||
|
||||
|
||||
/***********************************************************************
|
||||
* WineLocalizedString
|
||||
*
|
||||
|
@ -227,7 +251,7 @@ - (void) transformProcessToForeground:(BOOL)activateIfTransformed
|
|||
[NSApp setActivationPolicy:NSApplicationActivationPolicyRegular];
|
||||
|
||||
if (activateIfTransformed)
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[self tryToActivateIgnoringOtherApps:YES];
|
||||
|
||||
#if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
|
||||
if (!enable_app_nap && [NSProcessInfo instancesRespondToSelector:@selector(beginActivityWithOptions:reason:)])
|
||||
|
@ -1939,8 +1963,103 @@ - (void) setupObservations
|
|||
selector:@selector(enabledKeyboardInputSourcesChanged)
|
||||
name:(NSString*)kTISNotifyEnabledKeyboardInputSourcesChanged
|
||||
object:nil];
|
||||
|
||||
if ([NSApplication instancesRespondToSelector:@selector(yieldActivationToApplication:)])
|
||||
{
|
||||
/* App activation cooperation, starting in macOS 14 Sonoma. */
|
||||
[dnc addObserver:self
|
||||
selector:@selector(otherWineAppWillActivate:)
|
||||
name:WineAppWillActivateNotification
|
||||
object:nil
|
||||
suspensionBehavior:NSNotificationSuspensionBehaviorDeliverImmediately];
|
||||
}
|
||||
}
|
||||
|
||||
- (void) otherWineAppWillActivate:(NSNotification *)note
|
||||
{
|
||||
NSProcessInfo *ourProcess;
|
||||
pid_t otherPID;
|
||||
NSString *ourConfigDir, *otherConfigDir, *ourPrefix, *otherPrefix;
|
||||
NSRunningApplication *otherApp;
|
||||
|
||||
/* No point in yielding if we're not the foreground app. */
|
||||
if (![NSApp isActive]) return;
|
||||
|
||||
/* Ignore requests from ourself, dead processes, and other prefixes. */
|
||||
ourProcess = [NSProcessInfo processInfo];
|
||||
otherPID = [note.userInfo[WineActivatingAppPIDKey] integerValue];
|
||||
if (otherPID == ourProcess.processIdentifier) return;
|
||||
|
||||
otherApp = [NSRunningApplication runningApplicationWithProcessIdentifier:otherPID];
|
||||
if (!otherApp) return;
|
||||
|
||||
ourConfigDir = ourProcess.environment[@"WINECONFIGDIR"];
|
||||
otherConfigDir = note.userInfo[WineActivatingAppConfigDirKey];
|
||||
if (ourConfigDir.length && otherConfigDir.length &&
|
||||
![ourConfigDir isEqualToString:otherConfigDir])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
ourPrefix = ourProcess.environment[@"WINEPREFIX"];
|
||||
otherPrefix = note.userInfo[WineActivatingAppPrefixKey];
|
||||
if (ourPrefix.length && otherPrefix.length &&
|
||||
![ourPrefix isEqualToString:otherPrefix])
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
/* There's a race condition here. The requesting app sends out
|
||||
WineAppWillActivateNotification and then activates itself, but since
|
||||
distributed notifications are asynchronous, we may not have yielded
|
||||
in time. So we call activateFromApplication: on the other app here,
|
||||
which will work around that race if it happened. If we didn't hit the
|
||||
race, the activateFromApplication: call will be a no-op. */
|
||||
|
||||
/* We only add this observer if NSApplication responds to the yield
|
||||
methods, so they're safe to call without checking here. */
|
||||
[NSApp yieldActivationToApplication:otherApp];
|
||||
[otherApp activateFromApplication:[NSRunningApplication currentApplication]
|
||||
options:0];
|
||||
}
|
||||
|
||||
- (void) tryToActivateIgnoringOtherApps:(BOOL)ignore
|
||||
{
|
||||
NSProcessInfo *processInfo;
|
||||
NSString *configDir, *prefix;
|
||||
NSDictionary *userInfo;
|
||||
|
||||
if ([NSApp isActive]) return; /* Nothing to do. */
|
||||
|
||||
if (!ignore ||
|
||||
![NSApplication instancesRespondToSelector:@selector(yieldActivationToApplication:)])
|
||||
{
|
||||
/* Either we don't need to force activation, or the OS is old enough
|
||||
that this is our only option. */
|
||||
[NSApp activateIgnoringOtherApps:ignore];
|
||||
return;
|
||||
}
|
||||
|
||||
/* Ask other Wine apps to yield activation to us. */
|
||||
processInfo = [NSProcessInfo processInfo];
|
||||
configDir = processInfo.environment[@"WINECONFIGDIR"];
|
||||
prefix = processInfo.environment[@"WINEPREFIX"];
|
||||
userInfo = @{
|
||||
WineActivatingAppPIDKey: @(processInfo.processIdentifier),
|
||||
WineActivatingAppPrefixKey: prefix ? prefix : @"",
|
||||
WineActivatingAppConfigDirKey: configDir ? configDir : @""
|
||||
};
|
||||
|
||||
[[NSDistributedNotificationCenter defaultCenter]
|
||||
postNotificationName:WineAppWillActivateNotification
|
||||
object:nil
|
||||
userInfo:userInfo
|
||||
deliverImmediately:YES];
|
||||
|
||||
/* This is racy. See the note in otherWineAppWillActivate:. */
|
||||
[NSApp activate];
|
||||
}
|
||||
|
||||
- (BOOL) inputSourceIsInputMethod
|
||||
{
|
||||
if (!inputSourceIsInputMethodValid)
|
||||
|
|
|
@ -1720,7 +1720,7 @@ - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)a
|
|||
wasVisible = [self isVisible];
|
||||
|
||||
if (activate)
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
[controller tryToActivateIgnoringOtherApps:YES];
|
||||
|
||||
NSDisableScreenUpdates();
|
||||
|
||||
|
@ -2084,8 +2084,9 @@ - (void) makeFocused:(BOOL)activate
|
|||
{
|
||||
if (activate)
|
||||
{
|
||||
[[WineApplicationController sharedController] transformProcessToForeground:YES];
|
||||
[NSApp activateIgnoringOtherApps:YES];
|
||||
WineApplicationController *controller = [WineApplicationController sharedController];
|
||||
[controller transformProcessToForeground:YES];
|
||||
[controller tryToActivateIgnoringOtherApps:YES];
|
||||
}
|
||||
|
||||
causing_becomeKeyWindow = self;
|
||||
|
|
Loading…
Reference in a new issue