winemac.drv: Hide app's dock icon when it wouldn't have a taskbar item on Windows.

Hide the icon when an app has no visible windows, or when all of its
visible windows would have no taskbar entry (i.e., they are owned or
have WS_EX_TOOLWINDOW or WS_EX_NOACTIVATE, but not WS_EX_APPWINDOW).
The dock icon returns if those conditions are no longer satisfied.

This behavior is behind a Mac Driver registry key,
EagerDockIconHiding (defaulting to true), to allow toggling it on a
per-app or global basis.
This commit is contained in:
Tim Clem 2024-02-15 09:53:08 -08:00 committed by Alexandre Julliard
parent d6e39da995
commit f0efb2e46a
7 changed files with 112 additions and 2 deletions

View file

@ -157,6 +157,7 @@ - (void) noteKey:(uint16_t)keyCode pressed:(BOOL)pressed;
- (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged;
- (void) windowWillOrderOut:(WineWindow*)window;
- (void) maybeHideDockIconDueToWindowOrderingOut:(NSWindow *)window;
- (void) flipRect:(NSRect*)rect;
- (NSPoint) flippedMouseLocation:(NSPoint)point;

View file

@ -1300,6 +1300,78 @@ - (void) window:(WineWindow*)window isBeingDragged:(BOOL)dragged
[windowsBeingDragged removeObject:window];
}
/* Checks if, discounting the given window, there are any visible windows
that should make the app have a dock icon. window may be nil. anyVisible
will be set to reflect whether any window other than the given one is
visible. */
- (BOOL) shouldHaveDockIconAfterWindowOrdersOut:(NSWindow *)window
anyWindowIsVisible:(BOOL *)anyVisible
{
BOOL foundVisibleWindow = NO;
if (!eager_dock_icon_hiding)
return YES;
for (NSWindow *w in [NSApp windows])
{
if (w != window && (w.isVisible || w.isMiniaturized))
{
foundVisibleWindow = YES;
if ([w isKindOfClass:[WineWindow class]] && !((WineWindow *)w).needsDockIcon)
continue;
return YES;
}
}
if (anyVisible)
*anyVisible = foundVisibleWindow;
return NO;
}
/* If there are no visible windows that should make the app have a dock icon
(other than the provided one), hides the dock icon. window may be nil. */
- (void) maybeHideDockIconDueToWindowOrderingOut:(NSWindow *)window
{
BOOL anyVisibleWindows;
static int isMontereyOrLater = -1;
if (isMontereyOrLater == -1)
{
isMontereyOrLater = 0;
if ([NSProcessInfo instancesRespondToSelector:@selector(isOperatingSystemAtLeastVersion:)])
{
NSOperatingSystemVersion requiredVersion = { 12, 0, 0 };
isMontereyOrLater = [[NSProcessInfo processInfo] isOperatingSystemAtLeastVersion:requiredVersion];
}
}
if (!eager_dock_icon_hiding)
return;
if (window &&
((!window.isVisible && !window.isMiniaturized) ||
([window isKindOfClass:[WineWindow class]] && !((WineWindow *)window).needsDockIcon)))
{
/* Nothing to do; that window couldn't have changed anything. */
return;
}
if ([NSApp activationPolicy] == NSApplicationActivationPolicyRegular &&
![self shouldHaveDockIconAfterWindowOrdersOut:window
anyWindowIsVisible:&anyVisibleWindows])
{
/* Before macOS 12 Monterey, hiding the dock icon while there are
visible windows makes those windows disappear until they are
programmatically ordered back in. So we don't do that transition
(which should be rather uncommon) on older OSes. */
if (isMontereyOrLater || !anyVisibleWindows)
NSApp.activationPolicy = NSApplicationActivationPolicyAccessory;
}
}
- (void) windowWillOrderOut:(WineWindow*)window
{
if ([windowsBeingDragged containsObject:window])
@ -1310,6 +1382,8 @@ - (void) windowWillOrderOut:(WineWindow*)window
[window.queue postEvent:event];
macdrv_release_event(event);
}
[self maybeHideDockIconDueToWindowOrderingOut:window];
}
- (BOOL) isAnyWineWindowVisible

View file

@ -29,6 +29,7 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
BOOL disabled;
BOOL noForeground;
BOOL preventsAppActivation;
BOOL needsDockIcon;
BOOL floating;
BOOL resizable;
BOOL maximized;
@ -93,6 +94,7 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
@property (readonly, nonatomic) BOOL disabled;
@property (readonly, nonatomic) BOOL noForeground;
@property (readonly, nonatomic) BOOL preventsAppActivation;
@property (readonly, nonatomic) BOOL needsDockIcon;
@property (readonly, nonatomic) BOOL floating;
@property (readonly, getter=isFullscreen, nonatomic) BOOL fullscreen;
@property (readonly, getter=isFakingClose, nonatomic) BOOL fakingClose;

View file

@ -1009,6 +1009,7 @@ @implementation WineWindow
@synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
@synthesize usePerPixelAlpha;
@synthesize himc, commandDone;
@synthesize needsDockIcon;
+ (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
windowFrame:(NSRect)window_frame
@ -1048,6 +1049,7 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w
window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
window->resizable = wf->resizable;
window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
window->needsDockIcon = wf->dock_icon;
[window registerForDraggedTypes:@[(NSString*)kUTTypeData, (NSString*)kUTTypeContent]];
@ -1183,6 +1185,7 @@ - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
NSWindowStyleMaskNonactivatingPanel;
NSUInteger currentStyle = [self styleMask];
NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
int neededDockIcon;
self.preventsAppActivation = wf->prevents_app_activation;
@ -1227,6 +1230,17 @@ - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
resizable = wf->resizable;
[self adjustFeaturesForState];
[self setHasShadow:wf->shadow];
// We may need to hide or show our dock icon in response to changes.
neededDockIcon = needsDockIcon;
needsDockIcon = wf->dock_icon; // Need to update this before calling maybeHideDockIcon
if ([self isOrderedIn])
{
if (neededDockIcon && !needsDockIcon)
[[WineApplicationController sharedController] maybeHideDockIconDueToWindowOrderingOut:nil];
else if(!neededDockIcon && needsDockIcon)
[[WineApplicationController sharedController] transformProcessToForeground:!self.preventsAppActivation];
}
}
// Indicates if the window would be visible if the app were not hidden.
@ -1739,7 +1753,9 @@ - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)a
WineWindow* parent;
WineWindow* child;
[controller transformProcessToForeground:!self.preventsAppActivation];
if (!eager_dock_icon_hiding || self.needsDockIcon)
[controller transformProcessToForeground:!self.preventsAppActivation];
if ([NSApp isHidden])
[NSApp unhide:nil];
wasVisible = [self isVisible];
@ -2110,7 +2126,10 @@ - (void) makeFocused:(BOOL)activate
if (activate)
{
WineApplicationController *controller = [WineApplicationController sharedController];
[controller transformProcessToForeground:YES];
if (!eager_dock_icon_hiding || self.needsDockIcon)
[controller transformProcessToForeground:YES];
[controller tryToActivateIgnoringOtherApps:YES];
}

View file

@ -158,6 +158,7 @@
extern int retina_enabled; /* Whether Retina mode is enabled via registry setting. */
extern int retina_on; /* Whether Retina mode is currently active (enabled and display is in default mode). */
extern int enable_app_nap;
extern int eager_dock_icon_hiding;
static inline CGRect cgrect_mac_from_win(CGRect rect)
{
@ -521,6 +522,7 @@ extern int macdrv_register_hot_key(macdrv_event_queue q, unsigned int vkey, unsi
unsigned int utility:1;
unsigned int shadow:1;
unsigned int prevents_app_activation:1;
unsigned int dock_icon:1;
};
struct macdrv_window_state {

View file

@ -60,6 +60,7 @@ int use_precise_scrolling = TRUE;
int gl_surface_mode = GL_SURFACE_IN_FRONT_OPAQUE;
int retina_enabled = FALSE;
int enable_app_nap = FALSE;
int eager_dock_icon_hiding = TRUE;
CFDictionaryRef localized_strings;
@ -378,6 +379,9 @@ static void setup_options(void)
if (!get_config_key(hkey, appkey, "EnableAppNap", buffer, sizeof(buffer)))
enable_app_nap = IS_OPTION_TRUE(buffer[0]);
if (!get_config_key(hkey, appkey, "EagerDockIconHiding", buffer, sizeof(buffer)))
eager_dock_icon_hiding = IS_OPTION_TRUE(buffer[0]);
/* Don't use appkey. The DPI and monitor sizes should be consistent for all
processes in the prefix. */
if (!get_config_key(hkey, NULL, "RetinaMode", buffer, sizeof(buffer)))

View file

@ -59,6 +59,14 @@ static void get_cocoa_window_features(struct macdrv_win_data *data,
if (ex_style & WS_EX_NOACTIVATE) wf->prevents_app_activation = TRUE;
/* The dock_icon flag is only relevant when the window is visible, so we
don't need to worry about any other styles or the window rect. */
if (NtUserGetWindowRelative(data->hwnd, GW_OWNER))
wf->dock_icon = FALSE;
else
wf->dock_icon = (ex_style & WS_EX_APPWINDOW) ||
((ex_style & (WS_EX_TOOLWINDOW | WS_EX_NOACTIVATE)) == 0);
if (disable_window_decorations) return;
if (IsRectEmpty(window_rect)) return;
if (EqualRect(window_rect, client_rect)) return;