2 * MACDRV Cocoa window code
4 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 #import <Carbon/Carbon.h>
22 #import <CoreVideo/CoreVideo.h>
24 #import "cocoa_window.h"
26 #include "macdrv_cocoa.h"
28 #import "cocoa_event.h"
29 #import "cocoa_opengl.h"
32 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
34 NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
35 NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
36 NSWindowFullScreenButton = 7,
37 NSFullScreenWindowMask = 1 << 14,
40 @interface NSWindow (WineFullScreenExtensions)
41 - (void) toggleFullScreen:(id)sender;
46 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
47 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
49 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
54 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
56 NSUInteger style_mask;
60 style_mask = NSTitledWindowMask;
61 if (wf->close_button) style_mask |= NSClosableWindowMask;
62 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
63 if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
64 if (wf->utility) style_mask |= NSUtilityWindowMask;
66 else style_mask = NSBorderlessWindowMask;
72 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
75 for (screen in screens)
77 if (NSIntersectsRect(frame, [screen frame]))
84 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
86 for (NSScreen* screen in screens)
88 if (NSContainsRect(rect, [screen frame]))
95 /* We rely on the supposedly device-dependent modifier flags to distinguish the
96 keys on the left side of the keyboard from those on the right. Some event
97 sources don't set those device-depdendent flags. If we see a device-independent
98 flag for a modifier without either corresponding device-dependent flag, assume
100 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
102 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
103 *modifiers |= NX_DEVICELCMDKEYMASK;
104 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
105 *modifiers |= NX_DEVICELSHIFTKEYMASK;
106 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
107 *modifiers |= NX_DEVICELCTLKEYMASK;
108 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
109 *modifiers |= NX_DEVICELALTKEYMASK;
112 /* As we manipulate individual bits of a modifier mask, we can end up with
113 inconsistent sets of flags. In particular, we might set or clear one of the
114 left/right-specific bits, but not the corresponding non-side-specific bit.
115 Fix that. If either side-specific bit is set, set the non-side-specific bit,
116 otherwise clear it. */
117 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
119 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
120 *modifiers |= NX_COMMANDMASK;
122 *modifiers &= ~NX_COMMANDMASK;
123 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
124 *modifiers |= NX_SHIFTMASK;
126 *modifiers &= ~NX_SHIFTMASK;
127 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
128 *modifiers |= NX_CONTROLMASK;
130 *modifiers &= ~NX_CONTROLMASK;
131 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
132 *modifiers |= NX_ALTERNATEMASK;
134 *modifiers &= ~NX_ALTERNATEMASK;
137 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
139 fix_device_modifiers_by_generic(&modifiers);
140 if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
142 modifiers |= NX_DEVICELCMDKEYMASK;
143 modifiers &= ~NX_DEVICELALTKEYMASK;
145 if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
147 modifiers |= NX_DEVICERCMDKEYMASK;
148 modifiers &= ~NX_DEVICERALTKEYMASK;
150 fix_generic_modifiers_by_device(&modifiers);
156 @interface NSWindow (WineAccessPrivateMethods)
157 - (id) _displayChanged;
161 @interface WineDisplayLink : NSObject
163 CGDirectDisplayID _displayID;
164 CVDisplayLinkRef _link;
165 NSMutableSet* _windows;
167 NSTimeInterval _actualRefreshPeriod;
168 NSTimeInterval _nominalRefreshPeriod;
171 - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
173 - (void) addWindow:(WineWindow*)window;
174 - (void) removeWindow:(WineWindow*)window;
176 - (NSTimeInterval) refreshPeriod;
182 @implementation WineDisplayLink
184 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
186 - (id) initWithDisplayID:(CGDirectDisplayID)displayID
191 CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
192 if (status == kCVReturnSuccess && !_link)
193 status = kCVReturnError;
194 if (status == kCVReturnSuccess)
195 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
196 if (status != kCVReturnSuccess)
202 _displayID = displayID;
203 _windows = [[NSMutableSet alloc] init];
212 CVDisplayLinkStop(_link);
213 CVDisplayLinkRelease(_link);
219 - (void) addWindow:(WineWindow*)window
221 @synchronized(self) {
222 BOOL needsStart = !_windows.count;
223 [_windows addObject:window];
225 CVDisplayLinkStart(_link);
229 - (void) removeWindow:(WineWindow*)window
231 @synchronized(self) {
232 BOOL wasRunning = _windows.count > 0;
233 [_windows removeObject:window];
234 if (wasRunning && !_windows.count)
235 CVDisplayLinkStop(_link);
242 @synchronized(self) {
243 windows = [_windows copy];
245 dispatch_async(dispatch_get_main_queue(), ^{
246 BOOL anyDisplayed = FALSE;
247 for (WineWindow* window in windows)
249 if ([window viewsNeedDisplay])
251 [window displayIfNeeded];
256 CVDisplayLinkStop(_link);
261 - (NSTimeInterval) refreshPeriod
263 if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
264 return _actualRefreshPeriod;
266 if (_nominalRefreshPeriod)
267 return _nominalRefreshPeriod;
269 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
270 if (time.flags & kCVTimeIsIndefinite)
272 _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
273 return _nominalRefreshPeriod;
278 CVDisplayLinkStart(_link);
281 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
283 WineDisplayLink* link = displayLinkContext;
285 return kCVReturnSuccess;
291 @interface WineContentView : NSView <NSTextInputClient>
293 NSMutableArray* glContexts;
294 NSMutableArray* pendingGlContexts;
295 BOOL _cachedHasGLDescendant;
296 BOOL _cachedHasGLDescendantValid;
297 BOOL clearedGlSurface;
299 NSMutableAttributedString* markedText;
300 NSRange markedTextSelection;
305 - (void) addGLContext:(WineOpenGLContext*)context;
306 - (void) removeGLContext:(WineOpenGLContext*)context;
307 - (void) updateGLContexts;
309 - (void) wine_getBackingSize:(int*)outBackingSize;
310 - (void) wine_setBackingSize:(const int*)newBackingSize;
315 @interface WineWindow ()
317 @property (readwrite, nonatomic) BOOL disabled;
318 @property (readwrite, nonatomic) BOOL noActivate;
319 @property (readwrite, nonatomic) BOOL floating;
320 @property (readwrite, nonatomic) BOOL drawnSinceShown;
321 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
322 @property (retain, nonatomic) NSWindow* latentParentWindow;
324 @property (nonatomic) void* hwnd;
325 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
327 @property (nonatomic) void* surface;
328 @property (nonatomic) pthread_mutex_t* surface_mutex;
330 @property (copy, nonatomic) NSBezierPath* shape;
331 @property (copy, nonatomic) NSData* shapeData;
332 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
333 @property (readonly, nonatomic) BOOL needsTransparency;
335 @property (nonatomic) BOOL colorKeyed;
336 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
337 @property (nonatomic) BOOL usePerPixelAlpha;
339 @property (assign, nonatomic) void* imeData;
340 @property (nonatomic) BOOL commandDone;
342 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
344 - (void) updateColorSpace;
345 - (void) updateForGLSubviews;
347 - (BOOL) becameEligibleParentOrChild;
348 - (void) becameIneligibleChild;
350 - (void) windowDidDrawContent;
355 @implementation WineContentView
359 [markedText release];
360 [glContexts release];
361 [pendingGlContexts release];
370 - (void) drawRect:(NSRect)rect
372 WineWindow* window = (WineWindow*)[self window];
374 for (WineOpenGLContext* context in pendingGlContexts)
376 if (!clearedGlSurface)
378 context.shouldClearToBlack = TRUE;
379 clearedGlSurface = TRUE;
381 context.needsUpdate = TRUE;
383 [glContexts addObjectsFromArray:pendingGlContexts];
384 [pendingGlContexts removeAllObjects];
386 if ([window contentView] != self)
389 if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
391 [[NSColor clearColor] setFill];
394 [window.shape addClip];
396 [[NSColor windowBackgroundColor] setFill];
400 if (window.surface && window.surface_mutex &&
401 !pthread_mutex_lock(window.surface_mutex))
406 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
408 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
409 CGContextRef context;
412 [window.shape addClip];
414 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
415 CGContextSetBlendMode(context, kCGBlendModeCopy);
416 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
418 for (i = 0; i < count; i++)
423 imageRect = CGRectIntersection(rects[i], dirtyRect);
424 image = create_surface_image(window.surface, &imageRect, FALSE);
428 if (window.colorKeyed)
430 CGImageRef maskedImage;
431 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
432 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
433 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
434 maskedImage = CGImageCreateWithMaskingColors(image, components);
437 CGImageRelease(image);
442 CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
444 CGImageRelease(image);
448 [window windowDidDrawContent];
451 pthread_mutex_unlock(window.surface_mutex);
454 // If the window may be transparent, then we have to invalidate the
455 // shadow every time we draw. Also, if this is the first time we've
456 // drawn since changing from transparent to opaque.
457 if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
459 window.shapeChangedSinceLastDraw = FALSE;
460 [window invalidateShadow];
464 - (void) addGLContext:(WineOpenGLContext*)context
466 BOOL hadContext = [self hasGLContext];
468 glContexts = [[NSMutableArray alloc] init];
469 if (!pendingGlContexts)
470 pendingGlContexts = [[NSMutableArray alloc] init];
472 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
474 [glContexts addObject:context];
475 if (!clearedGlSurface)
477 context.shouldClearToBlack = TRUE;
478 clearedGlSurface = TRUE;
480 context.needsUpdate = TRUE;
484 [pendingGlContexts addObject:context];
485 [self setNeedsDisplay:YES];
489 [self invalidateHasGLDescendant];
490 [(WineWindow*)[self window] updateForGLSubviews];
493 - (void) removeGLContext:(WineOpenGLContext*)context
495 BOOL hadContext = [self hasGLContext];
496 [glContexts removeObjectIdenticalTo:context];
497 [pendingGlContexts removeObjectIdenticalTo:context];
498 if (hadContext && ![self hasGLContext])
499 [self invalidateHasGLDescendant];
500 [(WineWindow*)[self window] updateForGLSubviews];
503 - (void) updateGLContexts:(BOOL)reattach
505 for (WineOpenGLContext* context in glContexts)
507 context.needsUpdate = TRUE;
509 context.needsReattach = TRUE;
513 - (void) updateGLContexts
515 [self updateGLContexts:NO];
518 - (BOOL) hasGLContext
520 return [glContexts count] || [pendingGlContexts count];
523 - (BOOL) _hasGLDescendant
527 if ([self hasGLContext])
529 for (WineContentView* view in [self subviews])
531 if ([view hasGLDescendant])
537 - (BOOL) hasGLDescendant
539 if (!_cachedHasGLDescendantValid)
541 _cachedHasGLDescendant = [self _hasGLDescendant];
542 _cachedHasGLDescendantValid = YES;
544 return _cachedHasGLDescendant;
547 - (void) invalidateHasGLDescendant
549 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
550 _cachedHasGLDescendantValid = NO;
551 if (invalidateAncestors && self != [[self window] contentView])
553 WineContentView* superview = (WineContentView*)[self superview];
554 if ([superview isKindOfClass:[WineContentView class]])
555 [superview invalidateHasGLDescendant];
559 - (void) wine_getBackingSize:(int*)outBackingSize
561 @synchronized(self) {
562 memcpy(outBackingSize, backingSize, sizeof(backingSize));
565 - (void) wine_setBackingSize:(const int*)newBackingSize
567 @synchronized(self) {
568 memcpy(backingSize, newBackingSize, sizeof(backingSize));
572 - (void) setRetinaMode:(int)mode
574 double scale = mode ? 0.5 : 2.0;
575 NSRect frame = self.frame;
576 frame.origin.x *= scale;
577 frame.origin.y *= scale;
578 frame.size.width *= scale;
579 frame.size.height *= scale;
580 [self setFrame:frame];
581 [self updateGLContexts];
583 for (WineContentView* subview in [self subviews])
585 if ([subview isKindOfClass:[WineContentView class]])
586 [subview setRetinaMode:mode];
590 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
595 - (BOOL) preservesContentDuringLiveResize
597 // Returning YES from this tells Cocoa to keep our view's content during
598 // a Cocoa-driven resize. In theory, we're also supposed to override
599 // -setFrameSize: to mark exposed sections as needing redisplay, but
600 // user32 will take care of that in a roundabout way. This way, we don't
601 // redraw until the window surface is flushed.
603 // This doesn't do anything when we resize the window ourselves.
607 - (BOOL)acceptsFirstResponder
609 return [[self window] contentView] == self;
612 - (BOOL) mouseDownCanMoveWindow
620 if ([self hasGLContext])
621 [self invalidateHasGLDescendant];
624 - (void) viewDidUnhide
626 [super viewDidUnhide];
627 if ([self hasGLContext])
629 [self updateGLContexts:YES];
630 [self invalidateHasGLDescendant];
634 - (void) completeText:(NSString*)text
637 WineWindow* window = (WineWindow*)[self window];
639 event = macdrv_create_event(IM_SET_TEXT, window);
640 event->im_set_text.data = [window imeData];
641 event->im_set_text.text = (CFStringRef)[text copy];
642 event->im_set_text.complete = TRUE;
644 [[window queue] postEvent:event];
646 macdrv_release_event(event);
648 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
649 markedTextSelection = NSMakeRange(0, 0);
650 [[self inputContext] discardMarkedText];
653 - (NSFocusRingType) focusRingType
655 return NSFocusRingTypeNone;
658 - (void) didAddSubview:(NSView*)subview
660 if ([subview isKindOfClass:[WineContentView class]])
662 WineContentView* view = (WineContentView*)subview;
663 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
664 [self invalidateHasGLDescendant];
666 [super didAddSubview:subview];
669 - (void) willRemoveSubview:(NSView*)subview
671 if ([subview isKindOfClass:[WineContentView class]])
673 WineContentView* view = (WineContentView*)subview;
674 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
675 [self invalidateHasGLDescendant];
677 [super willRemoveSubview:subview];
681 * ---------- NSTextInputClient methods ----------
683 - (NSTextInputContext*) inputContext
686 markedText = [[NSMutableAttributedString alloc] init];
687 return [super inputContext];
690 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
692 if ([string isKindOfClass:[NSAttributedString class]])
693 string = [string string];
695 if ([string isKindOfClass:[NSString class]])
696 [self completeText:string];
699 - (void) doCommandBySelector:(SEL)aSelector
701 [(WineWindow*)[self window] setCommandDone:TRUE];
704 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
706 if ([string isKindOfClass:[NSAttributedString class]])
707 string = [string string];
709 if ([string isKindOfClass:[NSString class]])
712 WineWindow* window = (WineWindow*)[self window];
714 if (replacementRange.location == NSNotFound)
715 replacementRange = NSMakeRange(0, [markedText length]);
717 [markedText replaceCharactersInRange:replacementRange withString:string];
718 markedTextSelection = selectedRange;
719 markedTextSelection.location += replacementRange.location;
721 event = macdrv_create_event(IM_SET_TEXT, window);
722 event->im_set_text.data = [window imeData];
723 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
724 event->im_set_text.complete = FALSE;
725 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
727 [[window queue] postEvent:event];
729 macdrv_release_event(event);
731 [[self inputContext] invalidateCharacterCoordinates];
737 [self completeText:nil];
740 - (NSRange) selectedRange
742 return markedTextSelection;
745 - (NSRange) markedRange
747 NSRange range = NSMakeRange(0, [markedText length]);
749 range.location = NSNotFound;
753 - (BOOL) hasMarkedText
755 return [markedText length] > 0;
758 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
760 if (aRange.location >= [markedText length])
763 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
765 *actualRange = aRange;
766 return [markedText attributedSubstringFromRange:aRange];
769 - (NSArray*) validAttributesForMarkedText
771 return [NSArray array];
774 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
777 WineWindow* window = (WineWindow*)[self window];
780 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
782 query = macdrv_create_query();
783 query->type = QUERY_IME_CHAR_RECT;
784 query->window = (macdrv_window)[window retain];
785 query->ime_char_rect.data = [window imeData];
786 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
788 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
790 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
791 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
792 [[WineApplicationController sharedController] flipRect:&ret];
795 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
797 macdrv_release_query(query);
800 *actualRange = aRange;
804 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
809 - (NSInteger) windowLevel
811 return [[self window] level];
817 @implementation WineWindow
819 static WineWindow* causing_becomeKeyWindow;
821 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
822 @synthesize drawnSinceShown;
823 @synthesize surface, surface_mutex;
824 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
825 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
826 @synthesize usePerPixelAlpha;
827 @synthesize imeData, commandDone;
829 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
830 windowFrame:(NSRect)window_frame
832 queue:(WineEventQueue*)queue
835 WineContentView* contentView;
836 NSTrackingArea* trackingArea;
837 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
839 [[WineApplicationController sharedController] flipRect:&window_frame];
841 window = [[[self alloc] initWithContentRect:window_frame
842 styleMask:style_mask_for_features(wf)
843 backing:NSBackingStoreBuffered
844 defer:YES] autorelease];
846 if (!window) return nil;
848 /* Standardize windows to eliminate differences between titled and
849 borderless windows and between NSWindow and NSPanel. */
850 [window setHidesOnDeactivate:NO];
851 [window setReleasedWhenClosed:NO];
853 [window setOneShot:YES];
854 [window disableCursorRects];
855 [window setShowsResizeIndicator:NO];
856 [window setHasShadow:wf->shadow];
857 [window setAcceptsMouseMovedEvents:YES];
858 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
859 [window setDelegate:window];
860 [window setBackgroundColor:[NSColor clearColor]];
861 [window setOpaque:NO];
863 window.queue = queue;
864 window->savedContentMinSize = NSZeroSize;
865 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
866 window->resizable = wf->resizable;
867 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
869 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
870 (NSString*)kUTTypeContent,
873 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
876 [contentView setAutoresizesSubviews:NO];
878 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
879 because they give us mouse moves in the background. */
880 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
881 options:(NSTrackingMouseMoved |
882 NSTrackingActiveAlways |
883 NSTrackingInVisibleRect)
885 userInfo:nil] autorelease];
888 [contentView addTrackingArea:trackingArea];
890 [window setContentView:contentView];
891 [window setInitialFirstResponder:contentView];
893 [nc addObserver:window
894 selector:@selector(updateFullscreen)
895 name:NSApplicationDidChangeScreenParametersNotification
897 [window updateFullscreen];
899 [nc addObserver:window
900 selector:@selector(applicationWillHide)
901 name:NSApplicationWillHideNotification
903 [nc addObserver:window
904 selector:@selector(applicationDidUnhide)
905 name:NSApplicationDidUnhideNotification
908 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
909 selector:@selector(checkWineDisplayLink)
910 name:NSWorkspaceActiveSpaceDidChangeNotification
911 object:[NSWorkspace sharedWorkspace]];
913 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
920 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
921 [[NSNotificationCenter defaultCenter] removeObserver:self];
923 [latentChildWindows release];
924 [latentParentWindow release];
930 - (BOOL) preventResizing
932 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
933 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
936 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
938 if (allow_immovable_windows && (disabled || inMaximized))
940 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
946 - (void) adjustFeaturesForState
948 NSUInteger style = [self styleMask];
950 if (style & NSClosableWindowMask)
951 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
952 if (style & NSMiniaturizableWindowMask)
953 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
954 if (style & NSResizableWindowMask)
955 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
956 if ([self respondsToSelector:@selector(toggleFullScreen:)])
958 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
959 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
962 if ([self preventResizing])
964 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
965 [self setContentMinSize:size];
966 [self setContentMaxSize:size];
970 [self setContentMaxSize:savedContentMaxSize];
971 [self setContentMinSize:savedContentMinSize];
974 if (allow_immovable_windows || cursor_clipping_locks_windows)
975 [self setMovable:[self allowsMovingWithMaximized:maximized]];
978 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
980 if ([self respondsToSelector:@selector(toggleFullScreen:)])
982 NSUInteger style = [self styleMask];
984 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
985 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized)
987 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
988 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
992 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
993 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
994 if (style & NSFullScreenWindowMask)
995 [super toggleFullScreen:nil];
999 if (behavior != [self collectionBehavior])
1001 [self setCollectionBehavior:behavior];
1002 [self adjustFeaturesForState];
1006 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1008 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1009 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1010 NSUInteger currentStyle = [self styleMask];
1011 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1013 if (newStyle != currentStyle)
1015 NSString* title = [[[self title] copy] autorelease];
1016 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1017 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1018 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1020 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1021 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1022 // just NSTitledWindowMask, the window buttons should disappear rather
1023 // than just being disabled. But they don't. Similarly in reverse.
1024 // The workaround is to also toggle NSClosableWindowMask at the same time.
1025 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1027 [self setStyleMask:newStyle];
1029 // -setStyleMask: resets the firstResponder to the window. Set it
1030 // back to the content view.
1031 if ([[self contentView] acceptsFirstResponder])
1032 [self makeFirstResponder:[self contentView]];
1034 [self adjustFullScreenBehavior:[self collectionBehavior]];
1036 if ([[self title] length] == 0 && [title length] > 0)
1037 [self setTitle:title];
1040 resizable = wf->resizable;
1041 [self adjustFeaturesForState];
1042 [self setHasShadow:wf->shadow];
1045 // Indicates if the window would be visible if the app were not hidden.
1046 - (BOOL) wouldBeVisible
1048 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1051 - (BOOL) isOrderedIn
1053 return [self wouldBeVisible] || [self isMiniaturized];
1056 - (NSInteger) minimumLevelForActive:(BOOL)active
1060 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1061 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1062 level = NSFloatingWindowLevel;
1064 level = NSNormalWindowLevel;
1070 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1072 if (captured || fullscreen)
1075 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1077 level = NSStatusWindowLevel + 1;
1087 - (void) postDidUnminimizeEvent
1089 macdrv_event* event;
1091 /* Coalesce events by discarding any previous ones still in the queue. */
1092 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1095 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1096 [queue postEvent:event];
1097 macdrv_release_event(event);
1100 - (void) sendResizeStartQuery
1102 macdrv_query* query = macdrv_create_query();
1103 query->type = QUERY_RESIZE_START;
1104 query->window = (macdrv_window)[self retain];
1106 [self.queue query:query timeout:0.3];
1107 macdrv_release_query(query);
1110 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1112 NSWindowCollectionBehavior behavior;
1114 self.disabled = state->disabled;
1115 self.noActivate = state->no_activate;
1117 if (self.floating != state->floating)
1119 self.floating = state->floating;
1120 if (state->floating)
1122 // Became floating. If child of non-floating window, make that
1123 // relationship latent.
1124 WineWindow* parent = (WineWindow*)[self parentWindow];
1125 if (parent && !parent.floating)
1126 [self becameIneligibleChild];
1130 // Became non-floating. If parent of floating children, make that
1131 // relationship latent.
1133 for (child in [self childWineWindows])
1136 [child becameIneligibleChild];
1140 // Check our latent relationships. If floating status was the only
1141 // reason they were latent, then make them active.
1142 if ([self isVisible])
1143 [self becameEligibleParentOrChild];
1145 [[WineApplicationController sharedController] adjustWindowLevels];
1148 if (state->minimized_valid)
1150 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1152 pendingMinimize = FALSE;
1153 if (state->minimized && ![self isMiniaturized])
1155 if ([self wouldBeVisible])
1157 if ([self styleMask] & NSFullScreenWindowMask)
1159 [self postDidUnminimizeEvent];
1160 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1164 [super miniaturize:nil];
1165 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1166 event_mask_for_type(WINDOW_GOT_FOCUS) |
1167 event_mask_for_type(WINDOW_LOST_FOCUS);
1171 pendingMinimize = TRUE;
1173 else if (!state->minimized && [self isMiniaturized])
1175 ignore_windowDeminiaturize = TRUE;
1176 [self deminiaturize:nil];
1177 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1181 [queue discardEventsMatchingMask:discard forWindow:self];
1184 if (state->maximized != maximized)
1186 maximized = state->maximized;
1187 [self adjustFeaturesForState];
1189 if (!maximized && [self inLiveResize])
1190 [self sendResizeStartQuery];
1193 behavior = NSWindowCollectionBehaviorDefault;
1194 if (state->excluded_by_expose)
1195 behavior |= NSWindowCollectionBehaviorTransient;
1197 behavior |= NSWindowCollectionBehaviorManaged;
1198 if (state->excluded_by_cycle)
1200 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1201 if ([self isOrderedIn])
1202 [NSApp removeWindowsItem:self];
1206 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1207 if ([self isOrderedIn])
1208 [NSApp addWindowsItem:self title:[self title] filename:NO];
1210 [self adjustFullScreenBehavior:behavior];
1213 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1215 BOOL reordered = FALSE;
1217 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1219 if ([self level] > [child level])
1220 [child setLevel:[self level]];
1221 if (![child isVisible])
1222 [child setAutodisplay:YES];
1223 [self addChildWindow:child ordered:NSWindowAbove];
1224 [child checkWineDisplayLink];
1225 [latentChildWindows removeObjectIdenticalTo:child];
1226 child.latentParentWindow = nil;
1231 if (!latentChildWindows)
1232 latentChildWindows = [[NSMutableArray alloc] init];
1233 if (![latentChildWindows containsObject:child])
1234 [latentChildWindows addObject:child];
1235 child.latentParentWindow = self;
1241 - (BOOL) addChildWineWindow:(WineWindow*)child
1243 return [self addChildWineWindow:child assumeVisible:FALSE];
1246 - (void) removeChildWineWindow:(WineWindow*)child
1248 [self removeChildWindow:child];
1249 if (child.latentParentWindow == self)
1250 child.latentParentWindow = nil;
1251 [latentChildWindows removeObjectIdenticalTo:child];
1254 - (BOOL) becameEligibleParentOrChild
1256 BOOL reordered = FALSE;
1259 if (latentParentWindow.floating || !self.floating)
1261 // If we aren't visible currently, we assume that we should be and soon
1262 // will be. So, if the latent parent is visible that's enough to assume
1263 // we can establish the parent-child relationship in Cocoa. That will
1264 // actually make us visible, which is fine.
1265 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1269 // Here, though, we may not actually be visible yet and adding a child
1270 // won't make us visible. The caller will have to call this method
1271 // again after actually making us visible.
1272 if ([self isVisible] && (count = [latentChildWindows count]))
1274 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1277 for (i = 0; i < count; i++)
1279 WineWindow* child = [latentChildWindows objectAtIndex:i];
1280 if ([child isVisible] && (self.floating || !child.floating))
1282 if (child.latentParentWindow == self)
1284 if ([self level] > [child level])
1285 [child setLevel:[self level]];
1286 [self addChildWindow:child ordered:NSWindowAbove];
1287 child.latentParentWindow = nil;
1291 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1292 [indexesToRemove addIndex:i];
1296 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1302 - (void) becameIneligibleChild
1304 WineWindow* parent = (WineWindow*)[self parentWindow];
1307 if (!parent->latentChildWindows)
1308 parent->latentChildWindows = [[NSMutableArray alloc] init];
1309 [parent->latentChildWindows insertObject:self atIndex:0];
1310 self.latentParentWindow = parent;
1311 [parent removeChildWindow:self];
1315 - (void) becameIneligibleParentOrChild
1317 NSArray* childWindows = [self childWineWindows];
1319 [self becameIneligibleChild];
1321 if ([childWindows count])
1325 for (child in childWindows)
1327 child.latentParentWindow = self;
1328 [self removeChildWindow:child];
1331 if (latentChildWindows)
1332 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1334 latentChildWindows = [childWindows mutableCopy];
1338 // Determine if, among Wine windows, this window is directly above or below
1339 // a given other Wine window with no other Wine window intervening.
1340 // Intervening non-Wine windows are ignored.
1341 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1343 NSNumber* windowNumber;
1344 NSNumber* otherWindowNumber;
1345 NSArray* windowNumbers;
1346 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1348 if (![self isVisible] || ![otherWindow isVisible])
1351 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1352 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1353 windowNumbers = [[self class] windowNumbersWithOptions:0];
1354 windowIndex = [windowNumbers indexOfObject:windowNumber];
1355 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1357 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1360 if (orderingMode == NSWindowAbove)
1362 lowIndex = windowIndex;
1363 highIndex = otherWindowIndex;
1365 else if (orderingMode == NSWindowBelow)
1367 lowIndex = otherWindowIndex;
1368 highIndex = windowIndex;
1373 if (highIndex <= lowIndex)
1376 for (i = lowIndex + 1; i < highIndex; i++)
1378 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1379 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1380 if ([interveningWindow isKindOfClass:[WineWindow class]])
1387 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1389 NSMutableArray* windowNumbers;
1390 NSNumber* childWindowNumber;
1391 NSUInteger otherIndex, limit;
1392 NSArray* origChildren;
1393 NSMutableArray* children;
1395 // Get the z-order from the window server and modify it to reflect the
1396 // requested window ordering.
1397 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1398 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1399 [windowNumbers removeObject:childWindowNumber];
1400 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1401 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1403 // Get our child windows and sort them in the reverse of the desired
1404 // z-order (back-to-front).
1405 origChildren = [self childWineWindows];
1406 children = [[origChildren mutableCopy] autorelease];
1407 [children sortWithOptions:NSSortStable
1408 usingComparator:^NSComparisonResult(id obj1, id obj2){
1409 NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1410 NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1411 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1412 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1413 if (index1 == NSNotFound)
1415 if (index2 == NSNotFound)
1416 return NSOrderedSame;
1418 return NSOrderedAscending;
1420 else if (index2 == NSNotFound)
1421 return NSOrderedDescending;
1422 else if (index1 < index2)
1423 return NSOrderedDescending;
1424 else if (index2 < index1)
1425 return NSOrderedAscending;
1427 return NSOrderedSame;
1430 // If the current and desired children arrays match up to a point, leave
1431 // those matching children alone.
1432 limit = MIN([origChildren count], [children count]);
1433 for (otherIndex = 0; otherIndex < limit; otherIndex++)
1435 if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1438 [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1440 // Remove all of the child windows and re-add them back-to-front so they
1441 // are in the desired order.
1442 for (other in children)
1443 [self removeChildWindow:other];
1444 for (other in children)
1445 [self addChildWindow:other ordered:NSWindowAbove];
1448 /* Returns whether or not the window was ordered in, which depends on if
1449 its frame intersects any screen. */
1450 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1452 WineApplicationController* controller = [WineApplicationController sharedController];
1453 if (![self isMiniaturized])
1455 BOOL needAdjustWindowLevels = FALSE;
1458 [controller transformProcessToForeground];
1459 if ([NSApp isHidden])
1461 wasVisible = [self isVisible];
1464 [NSApp activateIgnoringOtherApps:YES];
1466 NSDisableScreenUpdates();
1468 if ([self becameEligibleParentOrChild])
1469 needAdjustWindowLevels = TRUE;
1473 WineWindow* other = [prev isVisible] ? prev : next;
1474 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1476 if (![self isOrdered:orderingMode relativeTo:other])
1478 WineWindow* parent = (WineWindow*)[self parentWindow];
1479 WineWindow* otherParent = (WineWindow*)[other parentWindow];
1481 // This window level may not be right for this window based
1482 // on floating-ness, fullscreen-ness, etc. But we set it
1483 // temporarily to allow us to order the windows properly.
1484 // Then the levels get fixed by -adjustWindowLevels.
1485 if ([self level] != [other level])
1486 [self setLevel:[other level]];
1487 [self setAutodisplay:YES];
1488 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1489 [self checkWineDisplayLink];
1491 // The above call to -[NSWindow orderWindow:relativeTo:] won't
1492 // reorder windows which are both children of the same parent
1493 // relative to each other, so do that separately.
1494 if (parent && parent == otherParent)
1495 [parent order:orderingMode childWindow:self relativeTo:other];
1497 needAdjustWindowLevels = TRUE;
1502 // Again, temporarily set level to make sure we can order to
1504 next = [controller frontWineWindow];
1505 if (next && [self level] < [next level])
1506 [self setLevel:[next level]];
1507 [self setAutodisplay:YES];
1508 [self orderFront:nil];
1509 [self checkWineDisplayLink];
1510 needAdjustWindowLevels = TRUE;
1513 if ([self becameEligibleParentOrChild])
1514 needAdjustWindowLevels = TRUE;
1516 if (needAdjustWindowLevels)
1518 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1519 [controller updateFullscreenWindows];
1520 [controller adjustWindowLevels];
1523 if (pendingMinimize)
1525 [super miniaturize:nil];
1526 pendingMinimize = FALSE;
1529 NSEnableScreenUpdates();
1531 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1532 Generate a frame-changed event just in case. The back end will ignore
1533 it if nothing actually changed. */
1534 [self windowDidResize:nil];
1536 if (![self isExcludedFromWindowsMenu])
1537 [NSApp addWindowsItem:self title:[self title] filename:NO];
1543 WineApplicationController* controller = [WineApplicationController sharedController];
1544 BOOL wasVisible = [self isVisible];
1545 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1547 if ([self isMiniaturized])
1548 pendingMinimize = TRUE;
1550 WineWindow* parent = (WineWindow*)self.parentWindow;
1551 if ([parent isKindOfClass:[WineWindow class]])
1552 [parent grabDockIconSnapshotFromWindow:self force:NO];
1554 [self becameIneligibleParentOrChild];
1555 if ([self isMiniaturized])
1559 fakingClose = FALSE;
1562 [self orderOut:nil];
1563 [self checkWineDisplayLink];
1564 [self setBackgroundColor:[NSColor clearColor]];
1565 [self setOpaque:NO];
1566 drawnSinceShown = NO;
1567 savedVisibleState = FALSE;
1568 if (wasVisible && wasOnActiveSpace && fullscreen)
1569 [controller updateFullscreenWindows];
1570 [controller adjustWindowLevels];
1571 [NSApp removeWindowsItem:self];
1573 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1574 event_mask_for_type(WINDOW_GOT_FOCUS) |
1575 event_mask_for_type(WINDOW_LOST_FOCUS) |
1576 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1577 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1578 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1582 - (void) updateFullscreen
1584 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1585 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1587 if (nowFullscreen != fullscreen)
1589 WineApplicationController* controller = [WineApplicationController sharedController];
1591 fullscreen = nowFullscreen;
1592 if ([self isVisible] && [self isOnActiveSpace])
1593 [controller updateFullscreenWindows];
1595 [controller adjustWindowLevels];
1599 - (void) setFrameAndWineFrame:(NSRect)frame
1601 [self setFrame:frame display:YES];
1604 roundedWineFrame = self.frame;
1606 #if CGFLOAT_IS_DOUBLE
1607 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1608 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1609 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1610 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1611 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1612 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1613 roundedWineFrame = wineFrame;
1615 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1616 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1617 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1618 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1619 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1620 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1621 roundedWineFrame = wineFrame;
1625 - (void) setFrameFromWine:(NSRect)contentRect
1627 /* Origin is (left, top) in a top-down space. Need to convert it to
1628 (left, bottom) in a bottom-up space. */
1629 [[WineApplicationController sharedController] flipRect:&contentRect];
1631 /* The back end is establishing a new window size and position. It's
1632 not interested in any stale events regarding those that may be sitting
1634 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1637 if (!NSIsEmptyRect(contentRect))
1639 NSRect frame, oldFrame;
1641 oldFrame = self.wine_fractionalFrame;
1642 frame = [self frameRectForContentRect:contentRect];
1643 if (!NSEqualRects(frame, oldFrame))
1645 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1646 BOOL needEnableScreenUpdates = FALSE;
1648 if ([self preventResizing])
1650 // Allow the following calls to -setFrame:display: to work even
1651 // if they would violate the content size constraints. This
1652 // shouldn't be necessary since the content size constraints are
1653 // documented to not constrain that method, but it seems to be.
1654 [self setContentMinSize:NSZeroSize];
1655 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1658 if (equalSizes && [[self childWineWindows] count])
1660 // If we change the window frame such that the origin moves
1661 // but the size doesn't change, then Cocoa moves child
1662 // windows with the parent. We don't want that so we fake
1663 // a change of the size and then change it back.
1664 NSRect bogusFrame = frame;
1665 bogusFrame.size.width++;
1667 NSDisableScreenUpdates();
1668 needEnableScreenUpdates = TRUE;
1670 ignore_windowResize = TRUE;
1671 [self setFrame:bogusFrame display:NO];
1672 ignore_windowResize = FALSE;
1675 [self setFrameAndWineFrame:frame];
1676 if ([self preventResizing])
1678 [self setContentMinSize:contentRect.size];
1679 [self setContentMaxSize:contentRect.size];
1682 if (needEnableScreenUpdates)
1683 NSEnableScreenUpdates();
1686 [self updateColorSpace];
1688 if (!enteringFullScreen &&
1689 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1690 nonFullscreenFrame = frame;
1692 [self updateFullscreen];
1694 if ([self isOrderedIn])
1696 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1697 event. The back end will ignore it if nothing actually changed. */
1698 [self windowDidResize:nil];
1704 - (NSRect) wine_fractionalFrame
1706 NSRect frame = self.frame;
1707 if (NSEqualRects(frame, roundedWineFrame))
1712 - (void) setMacDrvParentWindow:(WineWindow*)parent
1714 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1715 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1717 [oldParent removeChildWineWindow:self];
1718 [latentParentWindow removeChildWineWindow:self];
1719 if ([parent addChildWineWindow:self])
1720 [[WineApplicationController sharedController] adjustWindowLevels];
1724 - (void) setDisabled:(BOOL)newValue
1726 if (disabled != newValue)
1728 disabled = newValue;
1729 [self adjustFeaturesForState];
1733 - (BOOL) needsTransparency
1735 return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1736 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1739 - (void) checkTransparency
1741 if (![self isOpaque] && !self.needsTransparency)
1743 self.shapeChangedSinceLastDraw = TRUE;
1744 [[self contentView] setNeedsDisplay:YES];
1745 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1746 [self setOpaque:YES];
1748 else if ([self isOpaque] && self.needsTransparency)
1750 self.shapeChangedSinceLastDraw = TRUE;
1751 [[self contentView] setNeedsDisplay:YES];
1752 [self setBackgroundColor:[NSColor clearColor]];
1753 [self setOpaque:NO];
1757 - (void) setShape:(NSBezierPath*)newShape
1759 if (shape == newShape) return;
1763 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1767 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1769 shape = [newShape copy];
1770 self.shapeChangedSinceLastDraw = TRUE;
1772 [self checkTransparency];
1775 - (void) makeFocused:(BOOL)activate
1779 [[WineApplicationController sharedController] transformProcessToForeground];
1780 [NSApp activateIgnoringOtherApps:YES];
1783 causing_becomeKeyWindow = self;
1784 [self makeKeyWindow];
1785 causing_becomeKeyWindow = nil;
1787 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1788 event_mask_for_type(WINDOW_LOST_FOCUS)
1792 - (void) postKey:(uint16_t)keyCode
1793 pressed:(BOOL)pressed
1794 modifiers:(NSUInteger)modifiers
1795 event:(NSEvent*)theEvent
1797 macdrv_event* event;
1799 WineApplicationController* controller = [WineApplicationController sharedController];
1801 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1802 event->key.keycode = keyCode;
1803 event->key.modifiers = modifiers;
1804 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1806 if ((cgevent = [theEvent CGEvent]))
1808 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1809 kCGKeyboardEventKeyboardType);
1810 if (keyboardType != controller.keyboardType)
1812 controller.keyboardType = keyboardType;
1813 [controller keyboardSelectionDidChange];
1817 [queue postEvent:event];
1819 macdrv_release_event(event);
1821 [controller noteKey:keyCode pressed:pressed];
1824 - (void) postKeyEvent:(NSEvent *)theEvent
1826 [self flagsChanged:theEvent];
1827 [self postKey:[theEvent keyCode]
1828 pressed:[theEvent type] == NSKeyDown
1829 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1833 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1835 savedContentMinSize = minSize;
1836 savedContentMaxSize = maxSize;
1837 if (![self preventResizing])
1839 [self setContentMinSize:minSize];
1840 [self setContentMaxSize:maxSize];
1844 - (WineWindow*) ancestorWineWindow
1846 WineWindow* ancestor = self;
1849 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1850 if ([parent isKindOfClass:[WineWindow class]])
1858 - (void) postBroughtForwardEvent
1860 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1861 [queue postEvent:event];
1862 macdrv_release_event(event);
1865 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
1867 macdrv_event* event;
1868 NSUInteger style = self.styleMask;
1871 style |= NSFullScreenWindowMask;
1873 style &= ~NSFullScreenWindowMask;
1874 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
1875 [[WineApplicationController sharedController] flipRect:&frame];
1877 /* Coalesce events by discarding any previous ones still in the queue. */
1878 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1881 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1882 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
1883 event->window_frame_changed.fullscreen = isFullscreen;
1884 event->window_frame_changed.in_resize = resizing;
1885 [queue postEvent:event];
1886 macdrv_release_event(event);
1889 - (void) updateForCursorClipping
1891 [self adjustFeaturesForState];
1894 - (void) endWindowDragging
1898 if (draggingPhase == 3)
1900 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
1901 [queue postEvent:event];
1902 macdrv_release_event(event);
1906 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
1910 - (NSMutableDictionary*) displayIDToDisplayLinkMap
1912 static NSMutableDictionary* displayIDToDisplayLinkMap;
1913 if (!displayIDToDisplayLinkMap)
1915 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
1917 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
1920 usingBlock:^(NSNotification *note){
1921 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
1922 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
1923 [badDisplayIDs minusSet:validDisplayIDs];
1924 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
1927 return displayIDToDisplayLinkMap;
1930 - (WineDisplayLink*) wineDisplayLink
1932 if (!_lastDisplayID)
1935 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
1936 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
1939 - (void) checkWineDisplayLink
1941 NSScreen* screen = self.screen;
1942 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
1944 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
1945 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
1949 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
1950 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
1951 if (displayID == _lastDisplayID)
1954 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
1958 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
1959 [link removeWindow:self];
1963 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
1966 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
1967 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
1969 [link addWindow:self];
1970 [self displayIfNeeded];
1972 _lastDisplayID = displayID;
1975 - (BOOL) isEmptyShaped
1977 return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
1980 - (BOOL) canProvideSnapshot
1982 return (self.windowNumber > 0 && ![self isEmptyShaped]);
1985 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
1987 if (![self isEmptyShaped])
1990 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
1991 if (!force && now < lastDockIconSnapshot + 1)
1996 if (![window canProvideSnapshot])
2002 for (WineWindow* childWindow in self.childWindows)
2004 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2007 NSSize size = childWindow.frame.size;
2008 CGFloat area = size.width * size.height;
2009 if (!window || area > bestArea)
2011 window = childWindow;
2020 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2021 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2022 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2023 CFRelease(windowIDs);
2027 NSImage* appImage = [NSApp applicationIconImage];
2029 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2031 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2032 [dockIcon lockFocus];
2034 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2036 CGRect rect = CGRectMake(8, 8, 240, 240);
2037 size_t width = CGImageGetWidth(windowImage);
2038 size_t height = CGImageGetHeight(windowImage);
2041 rect.size.height *= height / (double)width;
2042 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2044 else if (width != height)
2046 rect.size.width *= width / (double)height;
2047 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2050 CGContextDrawImage(cgcontext, rect, windowImage);
2051 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2053 operation:NSCompositeSourceOver
2058 [dockIcon unlockFocus];
2060 CGImageRelease(windowImage);
2062 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2063 if (![imageView isKindOfClass:[NSImageView class]])
2065 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2066 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2067 self.dockTile.contentView = imageView;
2069 imageView.image = dockIcon;
2070 [self.dockTile display];
2071 lastDockIconSnapshot = now;
2074 - (void) checkEmptyShaped
2076 if (self.dockTile.contentView && ![self isEmptyShaped])
2078 self.dockTile.contentView = nil;
2079 lastDockIconSnapshot = 0;
2081 [self checkWineDisplayLink];
2086 * ---------- NSWindow method overrides ----------
2088 - (BOOL) canBecomeKeyWindow
2090 if (causing_becomeKeyWindow == self) return YES;
2091 if (self.disabled || self.noActivate) return NO;
2092 return [self isKeyWindow];
2095 - (BOOL) canBecomeMainWindow
2097 return [self canBecomeKeyWindow];
2100 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2102 // If a window is sized to completely cover a screen, then it's in
2103 // full-screen mode. In that case, we don't allow NSWindow to constrain
2105 NSArray* screens = [NSScreen screens];
2106 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2107 if (!screen_covered_by_rect(contentRect, screens) &&
2108 frame_intersects_screens(frameRect, screens))
2109 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2113 // This private method of NSWindow is called as Cocoa reacts to the display
2114 // configuration changing. Among other things, it adjusts the window's
2115 // frame based on how the screen(s) changed size. That tells Wine that the
2116 // window has been moved. We don't want that. Rather, we want to make
2117 // sure that the WinAPI notion of the window position is maintained/
2118 // restored, possibly undoing or overriding Cocoa's adjustment.
2120 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2121 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2122 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2123 // reassert its notion of the position. That call won't get processed
2124 // until after this method returns, so it will override whatever this
2125 // method does to the window position. It will also discard any pending
2126 // WINDOW_FRAME_CHANGED events.
2128 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2129 // adjust the window's position due to a display change is to hook into
2130 // this private method. This private method has remained stable from 10.6
2131 // through 10.11. If it does change, the most likely thing is that it
2132 // will be removed and no longer called and this fix will simply stop
2133 // working. The only real danger would be if Apple changed the return type
2134 // to a struct or floating-point type, which would change the calling
2136 - (id) _displayChanged
2138 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2139 [queue postEvent:event];
2140 macdrv_release_event(event);
2142 return [super _displayChanged];
2145 - (BOOL) isExcludedFromWindowsMenu
2147 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2150 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2152 BOOL ret = [super validateMenuItem:menuItem];
2154 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2155 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2156 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2162 /* We don't call this. It's the action method of the items in the Window menu. */
2163 - (void) makeKeyAndOrderFront:(id)sender
2165 if ([self isMiniaturized])
2166 [self deminiaturize:nil];
2167 [self orderBelow:nil orAbove:nil activate:NO];
2168 [[self ancestorWineWindow] postBroughtForwardEvent];
2170 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2171 [[WineApplicationController sharedController] windowGotFocus:self];
2174 - (void) sendEvent:(NSEvent*)event
2176 NSEventType type = event.type;
2178 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2179 interface control. For example, Control-Tab switches focus among
2180 views. We want to bypass that feature, so directly route key-down
2181 events to -keyDown:. */
2182 if (type == NSKeyDown)
2183 [[self firstResponder] keyDown:event];
2186 if (!draggingPhase && maximized && ![self isMovable] &&
2187 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2188 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2190 NSRect titleBar = self.frame;
2191 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2192 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2193 titleBar.origin.y = NSMaxY(contentRect);
2195 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2197 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2199 static const NSWindowButton buttons[] = {
2200 NSWindowCloseButton,
2201 NSWindowMiniaturizeButton,
2203 NSWindowFullScreenButton,
2205 BOOL hitButton = NO;
2208 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2212 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2215 button = [self standardWindowButton:buttons[i]];
2216 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2226 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2227 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2231 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2233 if ([self isMovable])
2235 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2236 NSPoint newTopLeft = dragWindowStartPosition;
2238 newTopLeft.x += point.x - dragStartPosition.x;
2239 newTopLeft.y += point.y - dragStartPosition.y;
2241 if (draggingPhase == 2)
2243 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2244 [queue postEvent:event];
2245 macdrv_release_event(event);
2250 [self setFrameTopLeftPoint:newTopLeft];
2252 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2254 macdrv_event* event;
2255 NSRect frame = [self contentRectForFrameRect:self.frame];
2257 [[WineApplicationController sharedController] flipRect:&frame];
2259 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2260 event->window_restore_requested.keep_frame = TRUE;
2261 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2262 [queue postEvent:event];
2263 macdrv_release_event(event);
2268 if (type == NSLeftMouseUp)
2269 [self endWindowDragging];
2272 [super sendEvent:event];
2276 - (void) miniaturize:(id)sender
2278 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2279 [queue postEvent:event];
2280 macdrv_release_event(event);
2282 WineWindow* parent = (WineWindow*)self.parentWindow;
2283 if ([parent isKindOfClass:[WineWindow class]])
2284 [parent grabDockIconSnapshotFromWindow:self force:YES];
2287 - (void) toggleFullScreen:(id)sender
2289 if (!self.disabled && !maximized)
2290 [super toggleFullScreen:sender];
2293 - (void) setViewsNeedDisplay:(BOOL)value
2295 if (value && ![self viewsNeedDisplay])
2297 WineDisplayLink* link = [self wineDisplayLink];
2300 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2301 if (_lastDisplayTime + [link refreshPeriod] < now)
2302 [self setAutodisplay:YES];
2306 _lastDisplayTime = now;
2310 [super setViewsNeedDisplay:value];
2315 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2317 [self setAutodisplay:NO];
2320 - (void) displayIfNeeded
2322 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2323 [super displayIfNeeded];
2324 [self setAutodisplay:NO];
2327 - (void) windowDidDrawContent
2329 if (!drawnSinceShown)
2331 drawnSinceShown = YES;
2332 dispatch_async(dispatch_get_main_queue(), ^{
2333 [self checkTransparency];
2338 - (NSArray*) childWineWindows
2340 NSArray* childWindows = self.childWindows;
2341 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2342 return [child isKindOfClass:[WineWindow class]];
2344 return [childWindows objectsAtIndexes:indexes];
2347 // We normally use the generic/calibrated RGB color space for the window,
2348 // rather than the device color space, to avoid expensive color conversion
2349 // which slows down drawing. However, for windows displaying OpenGL, having
2350 // a different color space than the screen greatly reduces frame rates, often
2351 // limiting it to the display refresh rate.
2353 // To avoid this, we switch back to the screen color space whenever the
2354 // window is covered by a view with an attached OpenGL context.
2355 - (void) updateColorSpace
2357 NSRect contentRect = [[self contentView] frame];
2358 BOOL coveredByGLView = FALSE;
2359 WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2360 if ([view isKindOfClass:[WineContentView class]] && [view hasGLContext])
2362 NSRect frame = [view convertRect:[view bounds] toView:nil];
2363 if (NSContainsRect(frame, contentRect))
2364 coveredByGLView = TRUE;
2367 if (coveredByGLView)
2368 [self setColorSpace:nil];
2370 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2373 - (void) updateForGLSubviews
2375 [self updateColorSpace];
2376 if (gl_surface_mode == GL_SURFACE_BEHIND)
2377 [self checkTransparency];
2380 - (void) setRetinaMode:(int)mode
2383 double scale = mode ? 0.5 : 2.0;
2384 NSAffineTransform* transform = [NSAffineTransform transform];
2386 [transform scaleBy:scale];
2389 [shape transformUsingAffineTransform:transform];
2391 for (WineContentView* subview in [self.contentView subviews])
2393 if ([subview isKindOfClass:[WineContentView class]])
2394 [subview setRetinaMode:mode];
2397 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2398 frame.origin.x *= scale;
2399 frame.origin.y *= scale;
2400 frame.size.width *= scale;
2401 frame.size.height *= scale;
2402 frame = [self frameRectForContentRect:frame];
2404 savedContentMinSize = [transform transformSize:savedContentMinSize];
2405 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2406 savedContentMaxSize.width *= scale;
2407 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2408 savedContentMaxSize.height *= scale;
2410 self.contentMinSize = [transform transformSize:self.contentMinSize];
2411 NSSize temp = self.contentMaxSize;
2412 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2413 temp.width *= scale;
2414 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2415 temp.height *= scale;
2416 self.contentMaxSize = temp;
2418 ignore_windowResize = TRUE;
2419 [self setFrameAndWineFrame:frame];
2420 ignore_windowResize = FALSE;
2425 * ---------- NSResponder method overrides ----------
2427 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
2429 - (void) flagsChanged:(NSEvent *)theEvent
2431 static const struct {
2435 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2436 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2437 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2438 { NX_DEVICELCTLKEYMASK, kVK_Control },
2439 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2440 { NX_DEVICELALTKEYMASK, kVK_Option },
2441 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2442 { NX_DEVICELCMDKEYMASK, kVK_Command },
2443 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2446 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2448 int i, last_changed;
2450 fix_device_modifiers_by_generic(&modifierFlags);
2451 changed = modifierFlags ^ lastModifierFlags;
2454 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2455 if (changed & modifiers[i].mask)
2458 for (i = 0; i <= last_changed; i++)
2460 if (changed & modifiers[i].mask)
2462 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2464 if (i == last_changed)
2465 lastModifierFlags = modifierFlags;
2468 lastModifierFlags ^= modifiers[i].mask;
2469 fix_generic_modifiers_by_device(&lastModifierFlags);
2472 // Caps lock generates one event for each press-release action.
2473 // We need to simulate a pair of events for each actual event.
2474 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2476 [self postKey:modifiers[i].keycode
2478 modifiers:lastModifierFlags
2479 event:(NSEvent*)theEvent];
2483 [self postKey:modifiers[i].keycode
2485 modifiers:lastModifierFlags
2486 event:(NSEvent*)theEvent];
2491 - (void) applicationWillHide
2493 savedVisibleState = [self isVisible];
2496 - (void) applicationDidUnhide
2498 if ([self isVisible])
2499 [self becameEligibleParentOrChild];
2504 * ---------- NSWindowDelegate methods ----------
2506 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2508 macdrv_query* query;
2511 query = macdrv_create_query();
2512 query->type = QUERY_MIN_MAX_INFO;
2513 query->window = (macdrv_window)[self retain];
2514 [self.queue query:query timeout:0.5];
2515 macdrv_release_query(query);
2517 size = [self contentMaxSize];
2518 if (proposedSize.width < size.width)
2519 size.width = proposedSize.width;
2520 if (proposedSize.height < size.height)
2521 size.height = proposedSize.height;
2525 - (void)windowDidBecomeKey:(NSNotification *)notification
2527 WineApplicationController* controller = [WineApplicationController sharedController];
2528 NSEvent* event = [controller lastFlagsChanged];
2530 [self flagsChanged:event];
2532 if (causing_becomeKeyWindow == self) return;
2534 [controller windowGotFocus:self];
2537 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2539 [self checkWineDisplayLink];
2542 - (void) windowDidChangeScreen:(NSNotification*)notification
2544 [self checkWineDisplayLink];
2547 - (void)windowDidDeminiaturize:(NSNotification *)notification
2549 WineApplicationController* controller = [WineApplicationController sharedController];
2551 if (!ignore_windowDeminiaturize)
2552 [self postDidUnminimizeEvent];
2553 ignore_windowDeminiaturize = FALSE;
2555 [self becameEligibleParentOrChild];
2557 if (fullscreen && [self isOnActiveSpace])
2558 [controller updateFullscreenWindows];
2559 [controller adjustWindowLevels];
2561 if (![self parentWindow])
2562 [self postBroughtForwardEvent];
2564 if (!self.disabled && !self.noActivate)
2566 causing_becomeKeyWindow = self;
2567 [self makeKeyWindow];
2568 causing_becomeKeyWindow = nil;
2569 [controller windowGotFocus:self];
2572 [self windowDidResize:notification];
2573 [self checkWineDisplayLink];
2576 - (void) windowDidEndLiveResize:(NSNotification *)notification
2580 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2581 [queue postEvent:event];
2582 macdrv_release_event(event);
2586 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2588 enteringFullScreen = FALSE;
2589 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2592 - (void) windowDidExitFullScreen:(NSNotification*)notification
2594 exitingFullScreen = FALSE;
2595 [self setFrameAndWineFrame:nonFullscreenFrame];
2596 [self windowDidResize:nil];
2599 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2601 enteringFullScreen = FALSE;
2602 enteredFullScreenTime = 0;
2605 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2607 exitingFullScreen = FALSE;
2608 [self windowDidResize:nil];
2611 - (void)windowDidMiniaturize:(NSNotification *)notification
2613 if (fullscreen && [self isOnActiveSpace])
2614 [[WineApplicationController sharedController] updateFullscreenWindows];
2615 [self checkWineDisplayLink];
2618 - (void)windowDidMove:(NSNotification *)notification
2620 [self windowDidResize:notification];
2623 - (void)windowDidResignKey:(NSNotification *)notification
2625 macdrv_event* event;
2627 if (causing_becomeKeyWindow) return;
2629 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2630 [queue postEvent:event];
2631 macdrv_release_event(event);
2634 - (void)windowDidResize:(NSNotification *)notification
2636 NSRect frame = self.wine_fractionalFrame;
2638 if ([self inLiveResize])
2640 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2641 resizingFromLeft = TRUE;
2642 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2643 resizingFromTop = TRUE;
2646 if (ignore_windowResize || exitingFullScreen) return;
2648 if ([self preventResizing])
2650 NSRect contentRect = [self contentRectForFrameRect:frame];
2651 [self setContentMinSize:contentRect.size];
2652 [self setContentMaxSize:contentRect.size];
2655 [self postWindowFrameChanged:frame
2656 fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2657 resizing:[self inLiveResize]];
2659 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2660 [self updateFullscreen];
2663 - (BOOL)windowShouldClose:(id)sender
2665 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2666 [queue postEvent:event];
2667 macdrv_release_event(event);
2671 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2675 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2676 [queue postEvent:event];
2677 macdrv_release_event(event);
2680 else if (!resizable)
2682 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2683 [queue postEvent:event];
2684 macdrv_release_event(event);
2691 - (void) windowWillClose:(NSNotification*)notification
2695 if (fakingClose) return;
2696 if (latentParentWindow)
2698 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2699 self.latentParentWindow = nil;
2702 for (child in latentChildWindows)
2704 if (child.latentParentWindow == self)
2705 child.latentParentWindow = nil;
2707 [latentChildWindows removeAllObjects];
2710 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2712 enteringFullScreen = TRUE;
2713 nonFullscreenFrame = self.wine_fractionalFrame;
2716 - (void) windowWillExitFullScreen:(NSNotification*)notification
2718 exitingFullScreen = TRUE;
2719 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2722 - (void)windowWillMiniaturize:(NSNotification *)notification
2724 [self becameIneligibleParentOrChild];
2725 [self grabDockIconSnapshotFromWindow:nil force:NO];
2728 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2730 if ([self inLiveResize])
2733 return self.wine_fractionalFrame.size;
2736 macdrv_query* query;
2738 rect = [self frame];
2739 if (resizingFromLeft)
2740 rect.origin.x = NSMaxX(rect) - frameSize.width;
2741 if (!resizingFromTop)
2742 rect.origin.y = NSMaxY(rect) - frameSize.height;
2743 rect.size = frameSize;
2744 rect = [self contentRectForFrameRect:rect];
2745 [[WineApplicationController sharedController] flipRect:&rect];
2747 query = macdrv_create_query();
2748 query->type = QUERY_RESIZE_SIZE;
2749 query->window = (macdrv_window)[self retain];
2750 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2751 query->resize_size.from_left = resizingFromLeft;
2752 query->resize_size.from_top = resizingFromTop;
2754 if ([self.queue query:query timeout:0.1])
2756 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2757 rect = [self frameRectForContentRect:rect];
2758 frameSize = rect.size;
2761 macdrv_release_query(query);
2767 - (void) windowWillStartLiveResize:(NSNotification *)notification
2769 [self endWindowDragging];
2773 macdrv_event* event;
2774 NSRect frame = [self contentRectForFrameRect:self.frame];
2776 [[WineApplicationController sharedController] flipRect:&frame];
2778 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2779 event->window_restore_requested.keep_frame = TRUE;
2780 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2781 [queue postEvent:event];
2782 macdrv_release_event(event);
2785 [self sendResizeStartQuery];
2787 frameAtResizeStart = [self frame];
2788 resizingFromLeft = resizingFromTop = FALSE;
2791 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2793 macdrv_query* query;
2794 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2797 query = macdrv_create_query();
2798 query->type = QUERY_MIN_MAX_INFO;
2799 query->window = (macdrv_window)[self retain];
2800 [self.queue query:query timeout:0.5];
2801 macdrv_release_query(query);
2803 currentContentRect = [self contentRectForFrameRect:[self frame]];
2804 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2806 maxSize = [self contentMaxSize];
2807 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2808 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2810 // Try to keep the top-left corner where it is.
2811 newContentRect.origin.x = NSMinX(currentContentRect);
2812 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2814 // If that pushes the bottom or right off the screen, pull it up and to the left.
2815 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2816 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2817 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2818 if (NSMinY(newContentRect) < NSMinY(screenRect))
2819 newContentRect.origin.y = NSMinY(screenRect);
2821 // If that pushes the top or left off the screen, push it down and the right
2822 // again. Do this last because the top-left corner is more important than the
2824 if (NSMinX(newContentRect) < NSMinX(screenRect))
2825 newContentRect.origin.x = NSMinX(screenRect);
2826 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2827 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2829 return [self frameRectForContentRect:newContentRect];
2834 * ---------- NSPasteboardOwner methods ----------
2836 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2838 macdrv_query* query = macdrv_create_query();
2839 query->type = QUERY_PASTEBOARD_DATA;
2840 query->window = (macdrv_window)[self retain];
2841 query->pasteboard_data.type = (CFStringRef)[type copy];
2843 [self.queue query:query timeout:3];
2844 macdrv_release_query(query);
2847 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
2849 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
2850 [queue postEvent:event];
2851 macdrv_release_event(event);
2856 * ---------- NSDraggingDestination methods ----------
2858 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2860 return [self draggingUpdated:sender];
2863 - (void) draggingExited:(id <NSDraggingInfo>)sender
2865 // This isn't really a query. We don't need any response. However, it
2866 // has to be processed in a similar manner as the other drag-and-drop
2867 // queries in order to maintain the proper order of operations.
2868 macdrv_query* query = macdrv_create_query();
2869 query->type = QUERY_DRAG_EXITED;
2870 query->window = (macdrv_window)[self retain];
2872 [self.queue query:query timeout:0.1];
2873 macdrv_release_query(query);
2876 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2878 NSDragOperation ret;
2879 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2880 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
2881 NSPasteboard* pb = [sender draggingPasteboard];
2883 macdrv_query* query = macdrv_create_query();
2884 query->type = QUERY_DRAG_OPERATION;
2885 query->window = (macdrv_window)[self retain];
2886 query->drag_operation.x = floor(cgpt.x);
2887 query->drag_operation.y = floor(cgpt.y);
2888 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2889 query->drag_operation.accepted_op = NSDragOperationNone;
2890 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2892 [self.queue query:query timeout:3];
2893 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2894 macdrv_release_query(query);
2899 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2902 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2903 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
2904 NSPasteboard* pb = [sender draggingPasteboard];
2906 macdrv_query* query = macdrv_create_query();
2907 query->type = QUERY_DRAG_DROP;
2908 query->window = (macdrv_window)[self retain];
2909 query->drag_drop.x = floor(cgpt.x);
2910 query->drag_drop.y = floor(cgpt.y);
2911 query->drag_drop.op = [sender draggingSourceOperationMask];
2912 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2914 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
2915 ret = query->status;
2916 macdrv_release_query(query);
2921 - (BOOL) wantsPeriodicDraggingUpdates
2929 /***********************************************************************
2930 * macdrv_create_cocoa_window
2932 * Create a Cocoa window with the given content frame and features (e.g.
2933 * title bar, close box, etc.).
2935 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2936 CGRect frame, void* hwnd, macdrv_event_queue queue)
2938 __block WineWindow* window;
2941 window = [[WineWindow createWindowWithFeatures:wf
2942 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
2944 queue:(WineEventQueue*)queue] retain];
2947 return (macdrv_window)window;
2950 /***********************************************************************
2951 * macdrv_destroy_cocoa_window
2953 * Destroy a Cocoa window.
2955 void macdrv_destroy_cocoa_window(macdrv_window w)
2957 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2958 WineWindow* window = (WineWindow*)w;
2961 [window doOrderOut];
2964 [window.queue discardEventsMatchingMask:-1 forWindow:window];
2970 /***********************************************************************
2971 * macdrv_get_window_hwnd
2973 * Get the hwnd that was set for the window at creation.
2975 void* macdrv_get_window_hwnd(macdrv_window w)
2977 WineWindow* window = (WineWindow*)w;
2981 /***********************************************************************
2982 * macdrv_set_cocoa_window_features
2984 * Update a Cocoa window's features.
2986 void macdrv_set_cocoa_window_features(macdrv_window w,
2987 const struct macdrv_window_features* wf)
2989 WineWindow* window = (WineWindow*)w;
2992 [window setWindowFeatures:wf];
2996 /***********************************************************************
2997 * macdrv_set_cocoa_window_state
2999 * Update a Cocoa window's state.
3001 void macdrv_set_cocoa_window_state(macdrv_window w,
3002 const struct macdrv_window_state* state)
3004 WineWindow* window = (WineWindow*)w;
3007 [window setMacDrvState:state];
3011 /***********************************************************************
3012 * macdrv_set_cocoa_window_title
3014 * Set a Cocoa window's title.
3016 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3019 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3020 WineWindow* window = (WineWindow*)w;
3021 NSString* titleString;
3024 titleString = [NSString stringWithCharacters:title length:length];
3027 OnMainThreadAsync(^{
3028 [window setTitle:titleString];
3029 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3030 [NSApp changeWindowsItem:window title:titleString filename:NO];
3036 /***********************************************************************
3037 * macdrv_order_cocoa_window
3039 * Reorder a Cocoa window relative to other windows. If prev is
3040 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3041 * it is ordered above that window. Otherwise, it is ordered to the
3044 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3045 macdrv_window n, int activate)
3047 WineWindow* window = (WineWindow*)w;
3048 WineWindow* prev = (WineWindow*)p;
3049 WineWindow* next = (WineWindow*)n;
3051 OnMainThreadAsync(^{
3052 [window orderBelow:prev
3056 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3058 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3062 /***********************************************************************
3063 * macdrv_hide_cocoa_window
3065 * Hides a Cocoa window.
3067 void macdrv_hide_cocoa_window(macdrv_window w)
3069 WineWindow* window = (WineWindow*)w;
3072 [window doOrderOut];
3076 /***********************************************************************
3077 * macdrv_set_cocoa_window_frame
3079 * Move a Cocoa window.
3081 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3083 WineWindow* window = (WineWindow*)w;
3086 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3090 /***********************************************************************
3091 * macdrv_get_cocoa_window_frame
3093 * Gets the frame of a Cocoa window.
3095 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3097 WineWindow* window = (WineWindow*)w;
3102 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3103 [[WineApplicationController sharedController] flipRect:&frame];
3104 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3108 /***********************************************************************
3109 * macdrv_set_cocoa_parent_window
3111 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3112 * the parent window.
3114 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3116 WineWindow* window = (WineWindow*)w;
3119 [window setMacDrvParentWindow:(WineWindow*)parent];
3123 /***********************************************************************
3124 * macdrv_set_window_surface
3126 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3128 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3129 WineWindow* window = (WineWindow*)w;
3132 window.surface = surface;
3133 window.surface_mutex = mutex;
3139 /***********************************************************************
3140 * macdrv_window_needs_display
3142 * Mark a window as needing display in a specified rect (in non-client
3143 * area coordinates).
3145 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3147 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3148 WineWindow* window = (WineWindow*)w;
3150 OnMainThreadAsync(^{
3151 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3157 /***********************************************************************
3158 * macdrv_set_window_shape
3160 * Sets the shape of a Cocoa window from an array of rectangles. If
3161 * rects is NULL, resets the window's shape to its frame.
3163 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3165 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3166 WineWindow* window = (WineWindow*)w;
3169 if (!rects || !count)
3172 window.shapeData = nil;
3173 [window checkEmptyShaped];
3177 size_t length = sizeof(*rects) * count;
3178 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3183 path = [NSBezierPath bezierPath];
3184 for (i = 0; i < count; i++)
3185 [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3186 window.shape = path;
3187 window.shapeData = [NSData dataWithBytes:rects length:length];
3188 [window checkEmptyShaped];
3196 /***********************************************************************
3197 * macdrv_set_window_alpha
3199 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3201 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3202 WineWindow* window = (WineWindow*)w;
3204 [window setAlphaValue:alpha];
3209 /***********************************************************************
3210 * macdrv_set_window_color_key
3212 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3215 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3216 WineWindow* window = (WineWindow*)w;
3219 window.colorKeyed = TRUE;
3220 window.colorKeyRed = keyRed;
3221 window.colorKeyGreen = keyGreen;
3222 window.colorKeyBlue = keyBlue;
3223 [window checkTransparency];
3229 /***********************************************************************
3230 * macdrv_clear_window_color_key
3232 void macdrv_clear_window_color_key(macdrv_window w)
3234 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3235 WineWindow* window = (WineWindow*)w;
3238 window.colorKeyed = FALSE;
3239 [window checkTransparency];
3245 /***********************************************************************
3246 * macdrv_window_use_per_pixel_alpha
3248 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3250 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3251 WineWindow* window = (WineWindow*)w;
3254 window.usePerPixelAlpha = use_per_pixel_alpha;
3255 [window checkTransparency];
3261 /***********************************************************************
3262 * macdrv_give_cocoa_window_focus
3264 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3265 * orders it front and, if its frame was not within the desktop bounds,
3266 * Cocoa will typically move it on-screen.
3268 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3270 WineWindow* window = (WineWindow*)w;
3273 [window makeFocused:activate];
3277 /***********************************************************************
3278 * macdrv_set_window_min_max_sizes
3280 * Sets the window's minimum and maximum content sizes.
3282 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3284 WineWindow* window = (WineWindow*)w;
3287 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3291 /***********************************************************************
3292 * macdrv_create_view
3294 * Creates and returns a view with the specified frame rect. The
3295 * caller is responsible for calling macdrv_dispose_view() on the view
3296 * when it is done with it.
3298 macdrv_view macdrv_create_view(CGRect rect)
3300 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3301 __block WineContentView* view;
3303 if (CGRectIsNull(rect)) rect = CGRectZero;
3306 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3308 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3309 [view setAutoresizesSubviews:NO];
3310 [view setHidden:YES];
3311 [nc addObserver:view
3312 selector:@selector(updateGLContexts)
3313 name:NSViewGlobalFrameDidChangeNotification
3315 [nc addObserver:view
3316 selector:@selector(updateGLContexts)
3317 name:NSApplicationDidChangeScreenParametersNotification
3322 return (macdrv_view)view;
3325 /***********************************************************************
3326 * macdrv_dispose_view
3328 * Destroys a view previously returned by macdrv_create_view.
3330 void macdrv_dispose_view(macdrv_view v)
3332 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3333 WineContentView* view = (WineContentView*)v;
3336 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3337 WineWindow* window = (WineWindow*)[view window];
3339 [nc removeObserver:view
3340 name:NSViewGlobalFrameDidChangeNotification
3342 [nc removeObserver:view
3343 name:NSApplicationDidChangeScreenParametersNotification
3345 [view removeFromSuperview];
3347 [window updateForGLSubviews];
3353 /***********************************************************************
3354 * macdrv_set_view_frame
3356 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3358 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3359 WineContentView* view = (WineContentView*)v;
3361 if (CGRectIsNull(rect)) rect = CGRectZero;
3363 OnMainThreadAsync(^{
3364 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3365 NSRect oldFrame = [view frame];
3367 if (!NSEqualRects(oldFrame, newFrame))
3369 [[view superview] setNeedsDisplayInRect:oldFrame];
3370 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3371 [view setFrameSize:newFrame.size];
3372 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3373 [view setFrameOrigin:newFrame.origin];
3375 [view setFrame:newFrame];
3376 [view setNeedsDisplay:YES];
3380 int backing_size[2] = { 0 };
3381 [view wine_setBackingSize:backing_size];
3383 [(WineWindow*)[view window] updateForGLSubviews];
3390 /***********************************************************************
3391 * macdrv_set_view_superview
3393 * Move a view to a new superview and position it relative to its
3394 * siblings. If p is non-NULL, the view is ordered behind it.
3395 * Otherwise, the view is ordered above n. If s is NULL, use the
3396 * content view of w as the new superview.
3398 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3400 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3401 WineContentView* view = (WineContentView*)v;
3402 WineContentView* superview = (WineContentView*)s;
3403 WineWindow* window = (WineWindow*)w;
3404 WineContentView* prev = (WineContentView*)p;
3405 WineContentView* next = (WineContentView*)n;
3408 superview = [window contentView];
3410 OnMainThreadAsync(^{
3411 if (superview == [view superview])
3413 NSArray* subviews = [superview subviews];
3414 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3415 if (!prev && !next && index == [subviews count] - 1)
3417 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3419 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3423 WineWindow* oldWindow = (WineWindow*)[view window];
3424 WineWindow* newWindow = (WineWindow*)[superview window];
3426 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3427 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3428 [view removeFromSuperview];
3431 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3433 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3435 if (oldWindow != newWindow)
3437 [oldWindow updateForGLSubviews];
3438 [newWindow updateForGLSubviews];
3445 /***********************************************************************
3446 * macdrv_set_view_hidden
3448 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3450 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3451 WineContentView* view = (WineContentView*)v;
3453 OnMainThreadAsync(^{
3454 [view setHidden:hidden];
3455 [(WineWindow*)view.window updateForGLSubviews];
3461 /***********************************************************************
3462 * macdrv_add_view_opengl_context
3464 * Add an OpenGL context to the list being tracked for each view.
3466 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3468 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3469 WineContentView* view = (WineContentView*)v;
3470 WineOpenGLContext *context = (WineOpenGLContext*)c;
3473 [view addGLContext:context];
3479 /***********************************************************************
3480 * macdrv_remove_view_opengl_context
3482 * Add an OpenGL context to the list being tracked for each view.
3484 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3486 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3487 WineContentView* view = (WineContentView*)v;
3488 WineOpenGLContext *context = (WineOpenGLContext*)c;
3490 OnMainThreadAsync(^{
3491 [view removeGLContext:context];
3497 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3499 WineContentView* view = (WineContentView*)v;
3501 if (![view isKindOfClass:[WineContentView class]])
3504 [view wine_getBackingSize:backing_size];
3508 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3510 WineContentView* view = (WineContentView*)v;
3512 if ([view isKindOfClass:[WineContentView class]])
3513 [view wine_setBackingSize:backing_size];
3516 /***********************************************************************
3517 * macdrv_window_background_color
3519 * Returns the standard Mac window background color as a 32-bit value of
3520 * the form 0x00rrggbb.
3522 uint32_t macdrv_window_background_color(void)
3524 static uint32_t result;
3525 static dispatch_once_t once;
3527 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3528 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3529 // of it is to draw with it.
3530 dispatch_once(&once, ^{
3532 unsigned char rgbx[4];
3533 unsigned char *planes = rgbx;
3534 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3541 colorSpaceName:NSCalibratedRGBColorSpace
3545 [NSGraphicsContext saveGraphicsState];
3546 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3547 [[NSColor windowBackgroundColor] set];
3548 NSRectFill(NSMakeRect(0, 0, 1, 1));
3549 [NSGraphicsContext restoreGraphicsState];
3551 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3558 /***********************************************************************
3559 * macdrv_send_text_input_event
3561 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3563 OnMainThreadAsync(^{
3565 macdrv_event* event;
3566 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3567 if (![window isKindOfClass:[WineWindow class]])
3569 window = (WineWindow*)[NSApp mainWindow];
3570 if (![window isKindOfClass:[WineWindow class]])
3571 window = [[WineApplicationController sharedController] frontWineWindow];
3576 NSUInteger localFlags = flags;
3580 window.imeData = data;
3581 fix_device_modifiers_by_generic(&localFlags);
3583 // An NSEvent created with +keyEventWithType:... is internally marked
3584 // as synthetic and doesn't get sent through input methods. But one
3585 // created from a CGEvent doesn't have that problem.
3586 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3587 CGEventSetFlags(c, localFlags);
3588 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3589 event = [NSEvent eventWithCGEvent:c];
3592 window.commandDone = FALSE;
3593 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3598 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3599 event->sent_text_input.handled = ret;
3600 event->sent_text_input.done = done;
3601 [[window queue] postEvent:event];
3602 macdrv_release_event(event);