From: Tim Clem tclem@codeweavers.com
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 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. --- dlls/winemac.drv/cocoa_app.h | 1 + dlls/winemac.drv/cocoa_app.m | 43 +++++++++++++++++++++++++++++++++ dlls/winemac.drv/cocoa_window.h | 2 ++ dlls/winemac.drv/cocoa_window.m | 23 ++++++++++++++++-- dlls/winemac.drv/macdrv_cocoa.h | 2 ++ dlls/winemac.drv/macdrv_main.c | 4 +++ dlls/winemac.drv/window.c | 5 ++++ 7 files changed, 78 insertions(+), 2 deletions(-)
diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index b2b8187e0b4..4db5b511c60 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -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; diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index de3a23a5d2d..545885cb540 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -1300,6 +1300,45 @@ - (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. */ + - (BOOL) shouldHaveDockIconAfterWindowOrdersOut:(NSWindow *)window + { + if (!eager_dock_icon_hiding) + return YES; + + for (NSWindow *w in [NSApp windows]) + { + if (w != window && (w.isVisible || w.isMiniaturized)) + { + if ([w isKindOfClass:[WineWindow class]] && !((WineWindow *)w).needsDockIcon) + continue; + + return YES; + } + } + + 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 + { + if ((!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]) + { + NSApp.activationPolicy = NSApplicationActivationPolicyAccessory; + } + } + - (void) windowWillOrderOut:(WineWindow*)window { if ([windowsBeingDragged containsObject:window]) @@ -1310,6 +1349,8 @@ - (void) windowWillOrderOut:(WineWindow*)window [window.queue postEvent:event]; macdrv_release_event(event); } + + [self maybeHideDockIconDueToWindowOrderingOut:window]; }
- (BOOL) isAnyWineWindowVisible @@ -1914,6 +1955,8 @@ - (void) setupObservations }); } [windowsBeingDragged removeObject:window]; + + [self maybeHideDockIconDueToWindowOrderingOut:window]; }];
if (useDragNotifications) { diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index 9539e4ebdd7..f5eb75e0bef 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -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; diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 1655ea98ef7..15ee4720630 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -1008,6 +1008,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 @@ -1047,6 +1048,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]];
@@ -1182,6 +1184,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;
@@ -1226,6 +1229,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. @@ -1738,7 +1752,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]; @@ -2109,7 +2125,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]; }
diff --git a/dlls/winemac.drv/macdrv_cocoa.h b/dlls/winemac.drv/macdrv_cocoa.h index 3a977f68955..294b0d9389b 100644 --- a/dlls/winemac.drv/macdrv_cocoa.h +++ b/dlls/winemac.drv/macdrv_cocoa.h @@ -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) { @@ -526,6 +527,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 { diff --git a/dlls/winemac.drv/macdrv_main.c b/dlls/winemac.drv/macdrv_main.c index 21b148ff558..acfad65ea81 100644 --- a/dlls/winemac.drv/macdrv_main.c +++ b/dlls/winemac.drv/macdrv_main.c @@ -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))) diff --git a/dlls/winemac.drv/window.c b/dlls/winemac.drv/window.c index 67f45d301df..62d164b6e09 100644 --- a/dlls/winemac.drv/window.c +++ b/dlls/winemac.drv/window.c @@ -59,6 +59,11 @@ static void get_cocoa_window_features(struct macdrv_win_data *data,
if (ex_style & WS_EX_NOACTIVATE) wf->prevents_app_activation = TRUE;
+ /* This flag is only relevant when the window is visible, so we don't need + to worry about any other styles or the window rect. */ + 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;