winemac: Centralize adjusting of window levels using the window server's z-order.

This commit is contained in:
Ken Thomases 2013-05-16 18:43:33 -05:00 committed by Alexandre Julliard
parent 8343e7ce4d
commit 27510fae8e
4 changed files with 185 additions and 223 deletions

View File

@ -65,8 +65,6 @@ @interface WineApplicationController : NSObject <NSApplicationDelegate>
double mouseMoveDeltaX, mouseMoveDeltaY;
NSUInteger unmatchedMouseDowns;
NSMutableArray* orderedWineWindows;
NSMutableDictionary* originalDisplayModes;
NSArray* cursorFrames;
@ -87,7 +85,6 @@ @interface WineApplicationController : NSObject <NSApplicationDelegate>
@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 @@ - (void) keyboardSelectionDidChange;
- (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;

View File

@ -95,7 +95,7 @@ - (void) applicationDidBecomeActive:(NSNotification *)notification;
@implementation WineApplicationController
@synthesize keyboardType, lastFlagsChanged;
@synthesize orderedWineWindows, applicationIcon;
@synthesize applicationIcon;
@synthesize cursorFrames, cursorTimer;
@synthesize mouseCaptureWindow;
@ -147,14 +147,13 @@ - (id) init
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 @@ - (void) dealloc
[cursorTimer release];
[cursorFrames release];
[originalDisplayModes release];
[orderedWineWindows release];
[keyWindows release];
[eventQueues release];
[eventQueuesLock release];
@ -441,64 +439,6 @@ - (void) flipRect:(NSRect*)rect
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 @@ - (WineWindow*) frontWineWindow
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 @@ - (BOOL) setMode:(CGDisplayModeRef)mode forDisplay:(CGDirectDisplayID)displayID
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 @@ - (void) didSendEvent:(NSEvent*)anEvent
- (void) setupObservations
{
NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
NSNotificationCenter* wsnc = [[NSWorkspace sharedWorkspace] notificationCenter];
[nc addObserverForName:NSWindowDidBecomeKeyNotification
object:nil
@ -1597,7 +1641,6 @@ - (void) setupObservations
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 @@ - (void) setupObservations
/* 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 @@ - (void)applicationDidBecomeActive:(NSNotification *)notification
{
[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 @@ - (void)applicationDidChangeScreenParameters:(NSNotification *)notification
{
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 @@ - (void)applicationWillResignActive:(NSNotification *)notification
{
[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];
}
/***********************************************************************

View File

@ -47,8 +47,6 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
NSUInteger lastModifierFlags;
NSInteger levelWhenActive;
NSTimer* liveResizeDisplayTimer;
void* imeData;
@ -61,8 +59,7 @@ @interface WineWindow : NSPanel <NSWindowDelegate>
@property (retain, readonly, nonatomic) WineEventQueue* queue;
@property (readonly, nonatomic) BOOL floating;
@property (readonly, nonatomic) NSInteger levelWhenActive;
- (void) adjustWindowLevel;
- (NSInteger) minimumLevelForActive:(BOOL)active;
@end

View File

@ -155,8 +155,6 @@ @interface WineWindow ()
@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 @@ @implementation WineWindow
@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 @@ - (BOOL) isOrderedIn
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];
screen = screen_covered_by_rect([self frame], [NSScreen screens]);
fullscreen = (screen != nil);
captured = (screen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
/* 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])
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 @@ - (void) setMacDrvState:(const struct macdrv_window_state*)state
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 @@ - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
{
[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 @@ - (void) doOrderOut
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 @@ - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
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 @@ - (void) setMacDrvParentWindow:(WineWindow*)parent
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 @@ - (void) makeFocused:(BOOL)activate
{
WineApplicationController* controller = [WineApplicationController sharedController];
NSArray* screens;
WineWindow* front;
[controller transformProcessToForeground];
@ -907,34 +873,30 @@ - (void) makeFocused:(BOOL)activate
[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 @@ - (void) postKeyEvent:(NSEvent *)theEvent
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 @@ - (void) sendEvent:(NSEvent*)event
{
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 @@ - (void) sendEvent:(NSEvent*)event
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 @@ - (void)windowDidDeminiaturize:(NSNotification *)notification
ignore_windowDeminiaturize = FALSE;
[[WineApplicationController sharedController] wineWindow:self ordered:NSWindowAbove relativeTo:nil];
[[WineApplicationController sharedController] adjustWindowLevels];
}
- (void) windowDidEndLiveResize:(NSNotification *)notification