From 27510fae8e9c77dcdd3fb922a5eed2b98950fc9f Mon Sep 17 00:00:00 2001 From: Ken Thomases Date: Thu, 16 May 2013 18:43:33 -0500 Subject: [PATCH] winemac: Centralize adjusting of window levels using the window server's z-order. --- dlls/winemac.drv/cocoa_app.h | 7 +- dlls/winemac.drv/cocoa_app.m | 198 +++++++++++++++++++++++---------------- dlls/winemac.drv/cocoa_window.h | 5 +- dlls/winemac.drv/cocoa_window.m | 200 +++++++++++++--------------------------- 4 files changed, 186 insertions(+), 224 deletions(-) diff --git a/dlls/winemac.drv/cocoa_app.h b/dlls/winemac.drv/cocoa_app.h index 551635d708b..681ef52ddf5 100644 --- a/dlls/winemac.drv/cocoa_app.h +++ b/dlls/winemac.drv/cocoa_app.h @@ -65,8 +65,6 @@ enum { double mouseMoveDeltaX, mouseMoveDeltaY; NSUInteger unmatchedMouseDowns; - NSMutableArray* orderedWineWindows; - NSMutableDictionary* originalDisplayModes; NSArray* cursorFrames; @@ -87,7 +85,6 @@ enum { @property (nonatomic) CGEventSourceKeyboardType keyboardType; @property (readonly, copy, nonatomic) NSEvent* lastFlagsChanged; -@property (readonly, nonatomic) NSArray* orderedWineWindows; @property (readonly, nonatomic) BOOL areDisplaysCaptured; + (WineApplicationController*) sharedController; @@ -108,10 +105,8 @@ enum { - (void) flipRect:(NSRect*)rect; - - (void) wineWindow:(WineWindow*)window - ordered:(NSWindowOrderingMode)order - relativeTo:(WineWindow*)otherWindow; - (WineWindow*) frontWineWindow; + - (void) adjustWindowLevels; - (BOOL) handleEvent:(NSEvent*)anEvent; - (void) didSendEvent:(NSEvent*)anEvent; diff --git a/dlls/winemac.drv/cocoa_app.m b/dlls/winemac.drv/cocoa_app.m index 74eff00740c..bdf4d92590d 100644 --- a/dlls/winemac.drv/cocoa_app.m +++ b/dlls/winemac.drv/cocoa_app.m @@ -95,7 +95,7 @@ int macdrv_err_on; @implementation WineApplicationController @synthesize keyboardType, lastFlagsChanged; - @synthesize orderedWineWindows, applicationIcon; + @synthesize applicationIcon; @synthesize cursorFrames, cursorTimer; @synthesize mouseCaptureWindow; @@ -147,14 +147,13 @@ int macdrv_err_on; eventQueuesLock = [[NSLock alloc] init]; keyWindows = [[NSMutableArray alloc] init]; - orderedWineWindows = [[NSMutableArray alloc] init]; originalDisplayModes = [[NSMutableDictionary alloc] init]; warpRecords = [[NSMutableArray alloc] init]; if (!requests || !requestsManipQueue || !eventQueues || !eventQueuesLock || - !keyWindows || !orderedWineWindows || !originalDisplayModes || !warpRecords) + !keyWindows || !originalDisplayModes || !warpRecords) { [self release]; return nil; @@ -178,7 +177,6 @@ int macdrv_err_on; [cursorTimer release]; [cursorFrames release]; [originalDisplayModes release]; - [orderedWineWindows release]; [keyWindows release]; [eventQueues release]; [eventQueuesLock release]; @@ -441,64 +439,6 @@ int macdrv_err_on; rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect); } - - (void) wineWindow:(WineWindow*)window - ordered:(NSWindowOrderingMode)order - relativeTo:(WineWindow*)otherWindow - { - NSUInteger index; - - switch (order) - { - case NSWindowAbove: - [window retain]; - [orderedWineWindows removeObjectIdenticalTo:window]; - if (otherWindow) - { - index = [orderedWineWindows indexOfObjectIdenticalTo:otherWindow]; - if (index == NSNotFound) - index = 0; - } - else - { - index = 0; - for (otherWindow in orderedWineWindows) - { - if ([otherWindow levelWhenActive] <= [window levelWhenActive]) - break; - index++; - } - } - [orderedWineWindows insertObject:window atIndex:index]; - [window release]; - break; - case NSWindowBelow: - [window retain]; - [orderedWineWindows removeObjectIdenticalTo:window]; - if (otherWindow) - { - index = [orderedWineWindows indexOfObjectIdenticalTo:otherWindow]; - if (index == NSNotFound) - index = [orderedWineWindows count]; - } - else - { - index = 0; - for (otherWindow in orderedWineWindows) - { - if ([otherWindow levelWhenActive] < [window levelWhenActive]) - break; - index++; - } - } - [orderedWineWindows insertObject:window atIndex:index]; - [window release]; - break; - case NSWindowOut: - default: - break; - } - } - - (WineWindow*) frontWineWindow { NSNumber* windowNumber; @@ -512,6 +452,113 @@ int macdrv_err_on; return nil; } + - (void) adjustWindowLevels:(BOOL)active + { + NSArray* windowNumbers = [NSWindow windowNumbersWithOptions:0]; + NSMutableArray* wineWindows = [[NSMutableArray alloc] initWithCapacity:[windowNumbers count]]; + NSNumber* windowNumber; + NSUInteger nextFloatingIndex = 0; + __block NSInteger maxLevel = NSIntegerMin; + __block NSInteger maxNonfloatingLevel = NSNormalWindowLevel; + __block WineWindow* prev = nil; + WineWindow* window; + + // For the most part, we rely on the window server's ordering of the windows + // to be authoritative. The one exception is if the "floating" property of + // one of the windows has been changed, it may be in the wrong level and thus + // in the order. This method is what's supposed to fix that up. So build + // a list of Wine windows sorted first by floating-ness and then by order + // as indicated by the window server. + for (windowNumber in windowNumbers) + { + window = (WineWindow*)[NSApp windowWithWindowNumber:[windowNumber integerValue]]; + if ([window isKindOfClass:[WineWindow class]]) + { + if (window.floating) + [wineWindows insertObject:window atIndex:nextFloatingIndex++]; + else + [wineWindows addObject:window]; + } + } + + NSDisableScreenUpdates(); + + // Go from back to front so that all windows in front of one which is + // elevated for full-screen are also elevated. + [wineWindows enumerateObjectsWithOptions:NSEnumerationReverse + usingBlock:^(id obj, NSUInteger idx, BOOL *stop){ + WineWindow* window = (WineWindow*)obj; + NSInteger origLevel = [window level]; + NSInteger newLevel = [window minimumLevelForActive:active]; + + if (newLevel < maxLevel) + newLevel = maxLevel; + else + maxLevel = newLevel; + + if (!window.floating && maxNonfloatingLevel < newLevel) + maxNonfloatingLevel = newLevel; + + if (newLevel != origLevel) + { + [window setLevel:newLevel]; + + // -setLevel: puts the window at the front of its new level. If + // we decreased the level, that's good (it was in front of that + // level before, so it should still be now). But if we increased + // the level, the window should be toward the back (but still + // ahead of the previous windows we did this to). + if (origLevel < newLevel) + { + if (prev) + [window orderWindow:NSWindowAbove relativeTo:[prev windowNumber]]; + else + [window orderBack:nil]; + } + } + + prev = window; + }]; + + NSEnableScreenUpdates(); + + [wineWindows release]; + + // The above took care of the visible windows on the current space. That + // leaves windows on other spaces, minimized windows, and windows which + // are not ordered in. We want to leave windows on other spaces alone + // so the space remains just as they left it (when viewed in Exposé or + // Mission Control, for example). We'll adjust the window levels again + // after we switch to another space, anyway. Windows which aren't + // ordered in will be handled when we order them in. Minimized windows + // on the current space should be set to the level they would have gotten + // if they were at the front of the windows with the same floating-ness, + // because that's where they'll go if/when they are unminimized. Again, + // for good measure we'll adjust window levels again when a window is + // unminimized, too. + for (window in [NSApp windows]) + { + if ([window isKindOfClass:[WineWindow class]] && [window isMiniaturized] && + [window isOnActiveSpace]) + { + NSInteger origLevel = [window level]; + NSInteger newLevel = [window minimumLevelForActive:YES]; + NSInteger maxLevelForType = window.floating ? maxLevel : maxNonfloatingLevel; + + if (newLevel < maxLevelForType) + newLevel = maxLevelForType; + + if (newLevel != origLevel) + [window setLevel:newLevel]; + } + } + } + + - (void) adjustWindowLevels + { + [self adjustWindowLevels:[NSApp isActive]]; + } + - (void) sendDisplaysChanged:(BOOL)activating { macdrv_event* event; @@ -650,11 +697,7 @@ int macdrv_err_on; CGDisplayModeRelease(currentMode); if (ret) - { - [orderedWineWindows enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop){ - [(WineWindow*)obj adjustWindowLevel]; - }]; - } + [self adjustWindowLevels]; return ret; } @@ -1581,6 +1624,7 @@ int macdrv_err_on; - (void) setupObservations { NSNotificationCenter* nc = [NSNotificationCenter defaultCenter]; + NSNotificationCenter* wsnc = [[NSWorkspace sharedWorkspace] notificationCenter]; [nc addObserverForName:NSWindowDidBecomeKeyNotification object:nil @@ -1597,7 +1641,6 @@ int macdrv_err_on; usingBlock:^(NSNotification *note){ NSWindow* window = [note object]; [keyWindows removeObjectIdenticalTo:window]; - [orderedWineWindows removeObjectIdenticalTo:window]; if (window == lastTargetWindow) lastTargetWindow = nil; if (window == self.mouseCaptureWindow) @@ -1612,6 +1655,11 @@ int macdrv_err_on; /* The above notification isn't sent unless the NSTextInputContext class has initialized itself. Poke it. */ [NSTextInputContext self]; + + [wsnc addObserver:self + selector:@selector(adjustWindowLevels) + name:NSWorkspaceActiveSpaceDidChangeNotification + object:nil]; } - (BOOL) inputSourceIsInputMethod @@ -1641,11 +1689,7 @@ int macdrv_err_on; { [self activateCursorClipping]; - [orderedWineWindows enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop){ - WineWindow* window = obj; - if ([window levelWhenActive] != [window level]) - [window setLevel:[window levelWhenActive]]; - }]; + [self adjustWindowLevels:YES]; if (![self frontWineWindow]) { @@ -1681,6 +1725,7 @@ int macdrv_err_on; { primaryScreenHeightValid = FALSE; [self sendDisplaysChanged:FALSE]; + [self adjustWindowLevels]; // When the display configuration changes, the cursor position may jump. // Accumulated mouse movement deltas are invalidated. Make sure the next @@ -1752,12 +1797,7 @@ int macdrv_err_on; { [self deactivateCursorClipping]; - [orderedWineWindows enumerateObjectsWithOptions:NSEnumerationReverse usingBlock:^(id obj, NSUInteger idx, BOOL *stop){ - WineWindow* window = obj; - NSInteger level = window.floating ? NSFloatingWindowLevel : NSNormalWindowLevel; - if ([window level] > level) - [window setLevel:level]; - }]; + [self adjustWindowLevels:NO]; } /*********************************************************************** diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index 21bcf446f56..e1af247bcde 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -47,8 +47,6 @@ NSUInteger lastModifierFlags; - NSInteger levelWhenActive; - NSTimer* liveResizeDisplayTimer; void* imeData; @@ -61,8 +59,7 @@ @property (retain, readonly, nonatomic) WineEventQueue* queue; @property (readonly, nonatomic) BOOL floating; -@property (readonly, nonatomic) NSInteger levelWhenActive; - - (void) adjustWindowLevel; + - (NSInteger) minimumLevelForActive:(BOOL)active; @end diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 717bcf64c9c..57d6e5dd60d 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -155,8 +155,6 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue; @property (nonatomic) BOOL usePerPixelAlpha; -@property (readwrite, nonatomic) NSInteger levelWhenActive; - @property (assign, nonatomic) void* imeData; @property (nonatomic) BOOL commandDone; @@ -464,7 +462,6 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) @synthesize shape, shapeChangedSinceLastDraw; @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue; @synthesize usePerPixelAlpha; - @synthesize levelWhenActive; @synthesize imeData, commandDone; + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf @@ -578,67 +575,37 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) return [self isVisible] || [self isMiniaturized]; } - - (void) adjustWindowLevel + - (NSInteger) minimumLevelForActive:(BOOL)active { - WineApplicationController* controller = [WineApplicationController sharedController]; NSInteger level; - BOOL fullscreen, captured; - NSScreen* screen; - NSUInteger index; - WineWindow* other = nil; - screen = screen_covered_by_rect([self frame], [NSScreen screens]); - fullscreen = (screen != nil); - captured = (screen || [self screen]) && [controller areDisplaysCaptured]; - - if (captured || fullscreen) - { - if (captured) - level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */ - else - level = NSMainMenuWindowLevel + 1; - - if (self.floating) - level++; - } - else if (self.floating) + if (self.floating) level = NSFloatingWindowLevel; else level = NSNormalWindowLevel; - index = [[controller orderedWineWindows] indexOfObjectIdenticalTo:self]; - if (index != NSNotFound && index + 1 < [[controller orderedWineWindows] count]) + if (active) { - other = [[controller orderedWineWindows] objectAtIndex:index + 1]; - if (level < [other level]) - level = [other level]; - } + BOOL fullscreen, captured; + NSScreen* screen; - if (level != [self level]) - { - [self setLevelWhenActive:level]; - - /* Setting the window level above has moved this window to the front - of all other windows at the same level. We need to move it - back into its proper place among other windows of that level. - Also, any windows which are supposed to be in front of it had - better have the same or higher window level. If not, bump them - up. */ - if (index != NSNotFound && [self isOrderedIn]) + screen = screen_covered_by_rect([self frame], [NSScreen screens]); + fullscreen = (screen != nil); + captured = (screen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured]; + + if (captured || fullscreen) { - for (; index > 0; index--) - { - other = [[controller orderedWineWindows] objectAtIndex:index - 1]; - if ([other level] < level) - [other setLevelWhenActive:level]; - else if ([self isVisible]) - { - [self orderWindow:NSWindowBelow relativeTo:[other windowNumber]]; - break; - } - } + if (captured) + level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */ + else + level = NSMainMenuWindowLevel + 1; + + if (self.floating) + level++; } } + + return level; } - (void) setMacDrvState:(const struct macdrv_window_state*)state @@ -648,8 +615,11 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) self.disabled = state->disabled; self.noActivate = state->no_activate; - self.floating = state->floating; - [self adjustWindowLevel]; + if (self.floating != state->floating) + { + self.floating = state->floating; + [[WineApplicationController sharedController] adjustWindowLevels]; + } behavior = NSWindowCollectionBehaviorDefault; if (state->excluded_by_expose) @@ -697,45 +667,40 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) { [controller transformProcessToForeground]; + NSDisableScreenUpdates(); + if (latentParentWindow) { if ([latentParentWindow level] > [self level]) - [self setLevelWhenActive:[latentParentWindow level]]; + [self setLevel:[latentParentWindow level]]; [latentParentWindow addChildWindow:self ordered:NSWindowAbove]; - [controller wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow]; self.latentParentWindow = nil; } - if (prev) + if (prev || next) { - /* Make sure that windows that should be above this one really are. - This is necessary since a full-screen window gets a boost to its - window level to be in front of the menu bar and Dock and that moves - it out of the z-order that Win32 would otherwise establish. */ - if ([prev level] < [self level]) - { - NSUInteger index = [[controller orderedWineWindows] indexOfObjectIdenticalTo:prev]; - if (index != NSNotFound) - { - [prev setLevelWhenActive:[self level]]; - for (; index > 0; index--) - { - WineWindow* other = [[controller orderedWineWindows] objectAtIndex:index - 1]; - if ([other level] < [self level]) - [other setLevelWhenActive:[self level]]; - } - } - } - [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]]; - [controller wineWindow:self ordered:NSWindowBelow relativeTo:prev]; + WineWindow* other = [prev isVisible] ? prev : next; + NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove; + + // This window level may not be right for this window based + // on floating-ness, fullscreen-ness, etc. But we set it + // temporarily to allow us to order the windows properly. + // Then the levels get fixed by -adjustWindowLevels. + if ([self level] != [other level]) + [self setLevel:[other level]]; + [self orderWindow:orderingMode relativeTo:[other windowNumber]]; } else { - /* Similarly, make sure this window is really above what it should be. */ - if (next && [next level] > [self level]) - [self setLevelWhenActive:[next level]]; - [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]]; - [controller wineWindow:self ordered:NSWindowAbove relativeTo:next]; + // Again, temporarily set level to make sure we can order to + // the right place. + next = [controller frontWineWindow]; + if (next && [self level] < [next level]) + [self setLevel:[next level]]; + [self orderFront:nil]; } + [controller adjustWindowLevels]; + + NSEnableScreenUpdates(); /* Cocoa may adjust the frame when the window is ordered onto the screen. Generate a frame-changed event just in case. The back end will ignore @@ -754,7 +719,7 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) self.latentParentWindow = [self parentWindow]; [latentParentWindow removeChildWindow:self]; [self orderOut:nil]; - [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowOut relativeTo:nil]; + [[WineApplicationController sharedController] adjustWindowLevels]; [NSApp removeWindowsItem:self]; } @@ -792,7 +757,7 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) if (on_screen) { - [self adjustWindowLevel]; + [[WineApplicationController sharedController] adjustWindowLevels]; /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed event. The back end will ignore it if nothing actually changed. */ @@ -819,9 +784,9 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) if ([self isVisible] && parent) { if ([parent level] > [self level]) - [self setLevelWhenActive:[parent level]]; + [self setLevel:[parent level]]; [parent addChildWindow:self ordered:NSWindowAbove]; - [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowAbove relativeTo:parent]; + [[WineApplicationController sharedController] adjustWindowLevels]; } else self.latentParentWindow = parent; @@ -891,6 +856,7 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) { WineApplicationController* controller = [WineApplicationController sharedController]; NSArray* screens; + WineWindow* front; [controller transformProcessToForeground]; @@ -907,34 +873,30 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) [self setFrame:frame display:YES]; } - if ([[controller orderedWineWindows] count]) - { - WineWindow* front; - if (self.floating) - front = [[controller orderedWineWindows] objectAtIndex:0]; - else - { - for (front in [controller orderedWineWindows]) - if (!front.floating) break; - } - if (front && [front levelWhenActive] > [self levelWhenActive]) - [self setLevelWhenActive:[front levelWhenActive]]; - } if (activate) [NSApp activateIgnoringOtherApps:YES]; + + NSDisableScreenUpdates(); + if (latentParentWindow) { if ([latentParentWindow level] > [self level]) - [self setLevelWhenActive:[latentParentWindow level]]; + [self setLevel:[latentParentWindow level]]; [latentParentWindow addChildWindow:self ordered:NSWindowAbove]; - [controller wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow]; self.latentParentWindow = nil; } + front = [controller frontWineWindow]; + if (front && [self level] < [front level]) + [self setLevel:[front level]]; [self orderFront:nil]; - [controller wineWindow:self ordered:NSWindowAbove relativeTo:nil]; + [controller adjustWindowLevels]; + + NSEnableScreenUpdates(); + causing_becomeKeyWindow = TRUE; [self makeKeyWindow]; causing_becomeKeyWindow = FALSE; + if (![self isExcludedFromWindowsMenu]) [NSApp addWindowsItem:self title:[self title] filename:NO]; @@ -983,14 +945,6 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) event:theEvent]; } - - (void) setLevelWhenActive:(NSInteger)level - { - levelWhenActive = level; - if (([NSApp isActive] || level <= NSFloatingWindowLevel) && - level != [self level]) - [self setLevel:level]; - } - /* * ---------- NSWindow method overrides ---------- @@ -1054,9 +1008,6 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) { if ([event type] == NSLeftMouseDown) { - NSWindowButton windowButton; - BOOL broughtWindowForward = TRUE; - /* Since our windows generally claim they can't be made key, clicks in their title bars are swallowed by the theme frame stuff. So, we hook directly into the event stream and assume that any click @@ -1064,27 +1015,6 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) accept. */ if (![self isKeyWindow] && !self.disabled && !self.noActivate) [controller windowGotFocus:self]; - - /* Any left-click on our window anyplace other than the close or - minimize buttons will bring it forward. */ - for (windowButton = NSWindowCloseButton; - windowButton <= NSWindowMiniaturizeButton; - windowButton++) - { - NSButton* button = [[event window] standardWindowButton:windowButton]; - if (button) - { - NSPoint point = [button convertPoint:[event locationInWindow] fromView:nil]; - if ([button mouse:point inRect:[button bounds]]) - { - broughtWindowForward = FALSE; - break; - } - } - } - - if (broughtWindowForward) - [controller wineWindow:self ordered:NSWindowAbove relativeTo:nil]; } [super sendEvent:event]; @@ -1194,7 +1124,7 @@ static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers) ignore_windowDeminiaturize = FALSE; - [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowAbove relativeTo:nil]; + [[WineApplicationController sharedController] adjustWindowLevels]; } - (void) windowDidEndLiveResize:(NSNotification *)notification -- 2.11.4.GIT