diff --git a/dlls/winemac.drv/Makefile.in b/dlls/winemac.drv/Makefile.in index 2d9c488d3c3..b1ee0254aae 100644 --- a/dlls/winemac.drv/Makefile.in +++ b/dlls/winemac.drv/Makefile.in @@ -1,7 +1,7 @@ MODULE = winemac.drv IMPORTS = uuid user32 gdi32 advapi32 DELAYIMPORTS = ole32 shell32 imm32 -EXTRALIBS = -framework AppKit -framework Carbon -framework Security -framework OpenGL -framework IOKit +EXTRALIBS = -framework AppKit -framework Carbon -framework Security -framework OpenGL -framework IOKit -framework CoreVideo C_SRCS = \ clipboard.c \ diff --git a/dlls/winemac.drv/cocoa_window.h b/dlls/winemac.drv/cocoa_window.h index 9553b92fe03..f24570157bd 100644 --- a/dlls/winemac.drv/cocoa_window.h +++ b/dlls/winemac.drv/cocoa_window.h @@ -43,6 +43,8 @@ @interface WineWindow : NSPanel void* surface; pthread_mutex_t* surface_mutex; + CGDirectDisplayID _lastDisplayID; + NSBezierPath* shape; NSData* shapeData; BOOL shapeChangedSinceLastDraw; diff --git a/dlls/winemac.drv/cocoa_window.m b/dlls/winemac.drv/cocoa_window.m index 0dd6a6fd5a5..cd1a26bcafc 100644 --- a/dlls/winemac.drv/cocoa_window.m +++ b/dlls/winemac.drv/cocoa_window.m @@ -19,6 +19,7 @@ */ #import +#import #import "cocoa_window.h" @@ -155,6 +156,100 @@ - (id) _displayChanged; @end +@interface WineDisplayLink : NSObject +{ + CGDirectDisplayID _displayID; + CVDisplayLinkRef _link; + NSMutableSet* _windows; +} + + - (id) initWithDisplayID:(CGDirectDisplayID)displayID; + + - (void) addWindow:(WineWindow*)window; + - (void) removeWindow:(WineWindow*)window; + +@end + +@implementation WineDisplayLink + +static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext); + + - (id) initWithDisplayID:(CGDirectDisplayID)displayID + { + self = [super init]; + if (self) + { + CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link); + if (status == kCVReturnSuccess && !_link) + status = kCVReturnError; + if (status == kCVReturnSuccess) + status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self); + if (status != kCVReturnSuccess) + { + [self release]; + return nil; + } + + _displayID = displayID; + _windows = [[NSMutableSet alloc] init]; + } + return self; + } + + - (void) dealloc + { + if (_link) + { + CVDisplayLinkStop(_link); + CVDisplayLinkRelease(_link); + } + [_windows release]; + [super dealloc]; + } + + - (void) addWindow:(WineWindow*)window + { + @synchronized(self) { + BOOL needsStart = !_windows.count; + [_windows addObject:window]; + if (needsStart) + CVDisplayLinkStart(_link); + } + } + + - (void) removeWindow:(WineWindow*)window + { + @synchronized(self) { + BOOL wasRunning = _windows.count > 0; + [_windows removeObject:window]; + if (wasRunning && !_windows.count) + CVDisplayLinkStop(_link); + } + } + + - (void) fire + { + NSSet* windows; + @synchronized(self) { + windows = [_windows copy]; + } + dispatch_async(dispatch_get_main_queue(), ^{ + for (WineWindow* window in windows) + [window displayIfNeeded]; + }); + [windows release]; + } + +static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext) +{ + WineDisplayLink* link = displayLinkContext; + [link fire]; + return kCVReturnSuccess; +} + +@end + + @interface WineContentView : NSView { NSMutableArray* glContexts; @@ -592,6 +687,7 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w [window setAcceptsMouseMovedEvents:YES]; [window setColorSpace:[NSColorSpace genericRGBColorSpace]]; [window setDelegate:window]; + [window setAutodisplay:NO]; window.hwnd = hwnd; window.queue = queue; window->savedContentMinSize = NSZeroSize; @@ -637,6 +733,11 @@ + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)w name:NSApplicationDidUnhideNotification object:NSApp]; + [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window + selector:@selector(checkWineDisplayLink) + name:NSWorkspaceActiveSpaceDidChangeNotification + object:[NSWorkspace sharedWorkspace]]; + return window; } @@ -1207,6 +1308,7 @@ - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)a if ([self level] != [other level]) [self setLevel:[other level]]; [self orderWindow:orderingMode relativeTo:[other windowNumber]]; + [self checkWineDisplayLink]; // The above call to -[NSWindow orderWindow:relativeTo:] won't // reorder windows which are both children of the same parent @@ -1225,6 +1327,7 @@ - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)a if (next && [self level] < [next level]) [self setLevel:[next level]]; [self orderFront:nil]; + [self checkWineDisplayLink]; needAdjustWindowLevels = TRUE; } @@ -1278,6 +1381,7 @@ - (void) doOrderOut } else [self orderOut:nil]; + [self checkWineDisplayLink]; savedVisibleState = FALSE; if (wasVisible && wasOnActiveSpace && fullscreen) [controller updateFullscreenWindows]; @@ -1573,6 +1677,56 @@ - (void) endWindowDragging } } + - (void) checkWineDisplayLink + { + NSScreen* screen = self.screen; + if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped]) + screen = nil; +#if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9 + if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible)) + screen = nil; +#endif + + NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"]; + CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue]; + if (displayID == _lastDisplayID) + return; + + static NSMutableDictionary* displayIDToDisplayLinkMap; + if (!displayIDToDisplayLinkMap) + { + displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init]; + + [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification + object:NSApp + queue:nil + usingBlock:^(NSNotification *note){ + NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys]; + NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]]; + [badDisplayIDs minusSet:validDisplayIDs]; + [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]]; + }]; + } + + if (_lastDisplayID) + { + WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]]; + [link removeWindow:self]; + } + if (displayID) + { + WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber]; + if (!link) + { + link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease]; + [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber]; + } + [link addWindow:self]; + [self displayIfNeeded]; + } + _lastDisplayID = displayID; + } + - (BOOL) isEmptyShaped { return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero))); @@ -1674,6 +1828,7 @@ - (void) checkEmptyShaped self.dockTile.contentView = nil; lastDockIconSnapshot = 0; } + [self checkWineDisplayLink]; } @@ -2046,6 +2201,16 @@ - (void)windowDidBecomeKey:(NSNotification *)notification [controller windowGotFocus:self]; } + - (void) windowDidChangeOcclusionState:(NSNotification*)notification + { + [self checkWineDisplayLink]; + } + + - (void) windowDidChangeScreen:(NSNotification*)notification + { + [self checkWineDisplayLink]; + } + - (void)windowDidDeminiaturize:(NSNotification *)notification { WineApplicationController* controller = [WineApplicationController sharedController]; @@ -2072,6 +2237,7 @@ - (void)windowDidDeminiaturize:(NSNotification *)notification } [self windowDidResize:notification]; + [self checkWineDisplayLink]; } - (void) windowDidEndLiveResize:(NSNotification *)notification @@ -2115,6 +2281,7 @@ - (void)windowDidMiniaturize:(NSNotification *)notification { if (fullscreen && [self isOnActiveSpace]) [[WineApplicationController sharedController] updateFullscreenWindows]; + [self checkWineDisplayLink]; } - (void)windowDidMove:(NSNotification *)notification