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
222 @synchronized(self) {
223 needsStart = !_windows.count;
224 [_windows addObject:window];
227 CVDisplayLinkStart(_link);
230 - (void) removeWindow:(WineWindow*)window
232 BOOL shouldStop = FALSE;
233 @synchronized(self) {
234 BOOL wasRunning = _windows.count > 0;
235 [_windows removeObject:window];
236 if (wasRunning && !_windows.count)
240 CVDisplayLinkStop(_link);
246 @synchronized(self) {
247 windows = [_windows copy];
249 dispatch_async(dispatch_get_main_queue(), ^{
250 BOOL anyDisplayed = FALSE;
251 for (WineWindow* window in windows)
253 if ([window viewsNeedDisplay])
255 [window displayIfNeeded];
260 CVDisplayLinkStop(_link);
265 - (NSTimeInterval) refreshPeriod
267 if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
268 return _actualRefreshPeriod;
270 if (_nominalRefreshPeriod)
271 return _nominalRefreshPeriod;
273 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
274 if (time.flags & kCVTimeIsIndefinite)
276 _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
277 return _nominalRefreshPeriod;
282 CVDisplayLinkStart(_link);
285 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
287 WineDisplayLink* link = displayLinkContext;
289 return kCVReturnSuccess;
295 @interface WineContentView : NSView <NSTextInputClient>
297 NSMutableArray* glContexts;
298 NSMutableArray* pendingGlContexts;
299 BOOL _cachedHasGLDescendant;
300 BOOL _cachedHasGLDescendantValid;
301 BOOL clearedGlSurface;
303 NSMutableAttributedString* markedText;
304 NSRange markedTextSelection;
309 - (void) addGLContext:(WineOpenGLContext*)context;
310 - (void) removeGLContext:(WineOpenGLContext*)context;
311 - (void) updateGLContexts;
313 - (void) wine_getBackingSize:(int*)outBackingSize;
314 - (void) wine_setBackingSize:(const int*)newBackingSize;
319 @interface WineWindow ()
321 @property (readwrite, nonatomic) BOOL disabled;
322 @property (readwrite, nonatomic) BOOL noActivate;
323 @property (readwrite, nonatomic) BOOL floating;
324 @property (readwrite, nonatomic) BOOL drawnSinceShown;
325 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
326 @property (retain, nonatomic) NSWindow* latentParentWindow;
328 @property (nonatomic) void* hwnd;
329 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
331 @property (nonatomic) void* surface;
332 @property (nonatomic) pthread_mutex_t* surface_mutex;
334 @property (copy, nonatomic) NSBezierPath* shape;
335 @property (copy, nonatomic) NSData* shapeData;
336 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
337 @property (readonly, nonatomic) BOOL needsTransparency;
339 @property (nonatomic) BOOL colorKeyed;
340 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
341 @property (nonatomic) BOOL usePerPixelAlpha;
343 @property (assign, nonatomic) void* imeData;
344 @property (nonatomic) BOOL commandDone;
346 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
348 - (void) updateColorSpace;
349 - (void) updateForGLSubviews;
351 - (BOOL) becameEligibleParentOrChild;
352 - (void) becameIneligibleChild;
354 - (void) windowDidDrawContent;
359 @implementation WineContentView
363 [markedText release];
364 [glContexts release];
365 [pendingGlContexts release];
374 - (void) drawRect:(NSRect)rect
376 WineWindow* window = (WineWindow*)[self window];
378 for (WineOpenGLContext* context in pendingGlContexts)
380 if (!clearedGlSurface)
382 context.shouldClearToBlack = TRUE;
383 clearedGlSurface = TRUE;
385 context.needsUpdate = TRUE;
387 [glContexts addObjectsFromArray:pendingGlContexts];
388 [pendingGlContexts removeAllObjects];
390 if ([window contentView] != self)
393 if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
395 [[NSColor clearColor] setFill];
398 [window.shape addClip];
400 [[NSColor windowBackgroundColor] setFill];
404 if (window.surface && window.surface_mutex &&
405 !pthread_mutex_lock(window.surface_mutex))
410 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
412 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
413 CGContextRef context;
416 [window.shape addClip];
418 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
419 CGContextSetBlendMode(context, kCGBlendModeCopy);
420 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
422 for (i = 0; i < count; i++)
427 imageRect = CGRectIntersection(rects[i], dirtyRect);
428 image = create_surface_image(window.surface, &imageRect, FALSE);
432 if (window.colorKeyed)
434 CGImageRef maskedImage;
435 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
436 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
437 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
438 maskedImage = CGImageCreateWithMaskingColors(image, components);
441 CGImageRelease(image);
446 CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
448 CGImageRelease(image);
452 [window windowDidDrawContent];
455 pthread_mutex_unlock(window.surface_mutex);
458 // If the window may be transparent, then we have to invalidate the
459 // shadow every time we draw. Also, if this is the first time we've
460 // drawn since changing from transparent to opaque.
461 if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
463 window.shapeChangedSinceLastDraw = FALSE;
464 [window invalidateShadow];
468 - (void) addGLContext:(WineOpenGLContext*)context
470 BOOL hadContext = [self hasGLContext];
472 glContexts = [[NSMutableArray alloc] init];
473 if (!pendingGlContexts)
474 pendingGlContexts = [[NSMutableArray alloc] init];
476 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
478 [glContexts addObject:context];
479 if (!clearedGlSurface)
481 context.shouldClearToBlack = TRUE;
482 clearedGlSurface = TRUE;
484 context.needsUpdate = TRUE;
488 [pendingGlContexts addObject:context];
489 [self setNeedsDisplay:YES];
493 [self invalidateHasGLDescendant];
494 [(WineWindow*)[self window] updateForGLSubviews];
497 - (void) removeGLContext:(WineOpenGLContext*)context
499 BOOL hadContext = [self hasGLContext];
500 [glContexts removeObjectIdenticalTo:context];
501 [pendingGlContexts removeObjectIdenticalTo:context];
502 if (hadContext && ![self hasGLContext])
503 [self invalidateHasGLDescendant];
504 [(WineWindow*)[self window] updateForGLSubviews];
507 - (void) updateGLContexts:(BOOL)reattach
509 for (WineOpenGLContext* context in glContexts)
511 context.needsUpdate = TRUE;
513 context.needsReattach = TRUE;
517 - (void) updateGLContexts
519 [self updateGLContexts:NO];
522 - (BOOL) hasGLContext
524 return [glContexts count] || [pendingGlContexts count];
527 - (BOOL) _hasGLDescendant
531 if ([self hasGLContext])
533 for (WineContentView* view in [self subviews])
535 if ([view hasGLDescendant])
541 - (BOOL) hasGLDescendant
543 if (!_cachedHasGLDescendantValid)
545 _cachedHasGLDescendant = [self _hasGLDescendant];
546 _cachedHasGLDescendantValid = YES;
548 return _cachedHasGLDescendant;
551 - (void) invalidateHasGLDescendant
553 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
554 _cachedHasGLDescendantValid = NO;
555 if (invalidateAncestors && self != [[self window] contentView])
557 WineContentView* superview = (WineContentView*)[self superview];
558 if ([superview isKindOfClass:[WineContentView class]])
559 [superview invalidateHasGLDescendant];
563 - (void) wine_getBackingSize:(int*)outBackingSize
565 @synchronized(self) {
566 memcpy(outBackingSize, backingSize, sizeof(backingSize));
569 - (void) wine_setBackingSize:(const int*)newBackingSize
571 @synchronized(self) {
572 memcpy(backingSize, newBackingSize, sizeof(backingSize));
576 - (void) setRetinaMode:(int)mode
578 double scale = mode ? 0.5 : 2.0;
579 NSRect frame = self.frame;
580 frame.origin.x *= scale;
581 frame.origin.y *= scale;
582 frame.size.width *= scale;
583 frame.size.height *= scale;
584 [self setFrame:frame];
585 [self updateGLContexts];
587 for (WineContentView* subview in [self subviews])
589 if ([subview isKindOfClass:[WineContentView class]])
590 [subview setRetinaMode:mode];
594 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
599 - (BOOL) preservesContentDuringLiveResize
601 // Returning YES from this tells Cocoa to keep our view's content during
602 // a Cocoa-driven resize. In theory, we're also supposed to override
603 // -setFrameSize: to mark exposed sections as needing redisplay, but
604 // user32 will take care of that in a roundabout way. This way, we don't
605 // redraw until the window surface is flushed.
607 // This doesn't do anything when we resize the window ourselves.
611 - (BOOL)acceptsFirstResponder
613 return [[self window] contentView] == self;
616 - (BOOL) mouseDownCanMoveWindow
624 [self invalidateHasGLDescendant];
627 - (void) viewDidUnhide
629 [super viewDidUnhide];
630 [self updateGLContexts:YES];
631 [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 &&
986 !(self.parentWindow || self.latentParentWindow))
988 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
989 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
993 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
994 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
995 if (style & NSFullScreenWindowMask)
996 [super toggleFullScreen:nil];
1000 if (behavior != [self collectionBehavior])
1002 [self setCollectionBehavior:behavior];
1003 [self adjustFeaturesForState];
1007 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1009 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1010 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1011 NSUInteger currentStyle = [self styleMask];
1012 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1014 if (newStyle != currentStyle)
1016 NSString* title = [[[self title] copy] autorelease];
1017 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1018 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1019 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1021 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1022 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1023 // just NSTitledWindowMask, the window buttons should disappear rather
1024 // than just being disabled. But they don't. Similarly in reverse.
1025 // The workaround is to also toggle NSClosableWindowMask at the same time.
1026 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1028 [self setStyleMask:newStyle];
1030 // -setStyleMask: resets the firstResponder to the window. Set it
1031 // back to the content view.
1032 if ([[self contentView] acceptsFirstResponder])
1033 [self makeFirstResponder:[self contentView]];
1035 [self adjustFullScreenBehavior:[self collectionBehavior]];
1037 if ([[self title] length] == 0 && [title length] > 0)
1038 [self setTitle:title];
1041 resizable = wf->resizable;
1042 [self adjustFeaturesForState];
1043 [self setHasShadow:wf->shadow];
1046 // Indicates if the window would be visible if the app were not hidden.
1047 - (BOOL) wouldBeVisible
1049 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1052 - (BOOL) isOrderedIn
1054 return [self wouldBeVisible] || [self isMiniaturized];
1057 - (NSInteger) minimumLevelForActive:(BOOL)active
1061 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1062 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1063 level = NSFloatingWindowLevel;
1065 level = NSNormalWindowLevel;
1071 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1073 if (captured || fullscreen)
1076 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1078 level = NSStatusWindowLevel + 1;
1088 - (void) postDidUnminimizeEvent
1090 macdrv_event* event;
1092 /* Coalesce events by discarding any previous ones still in the queue. */
1093 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1096 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1097 [queue postEvent:event];
1098 macdrv_release_event(event);
1101 - (void) sendResizeStartQuery
1103 macdrv_query* query = macdrv_create_query();
1104 query->type = QUERY_RESIZE_START;
1105 query->window = (macdrv_window)[self retain];
1107 [self.queue query:query timeout:0.3];
1108 macdrv_release_query(query);
1111 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1113 NSWindowCollectionBehavior behavior;
1115 self.disabled = state->disabled;
1116 self.noActivate = state->no_activate;
1118 if (self.floating != state->floating)
1120 self.floating = state->floating;
1121 if (state->floating)
1123 // Became floating. If child of non-floating window, make that
1124 // relationship latent.
1125 WineWindow* parent = (WineWindow*)[self parentWindow];
1126 if (parent && !parent.floating)
1127 [self becameIneligibleChild];
1131 // Became non-floating. If parent of floating children, make that
1132 // relationship latent.
1134 for (child in [self childWineWindows])
1137 [child becameIneligibleChild];
1141 // Check our latent relationships. If floating status was the only
1142 // reason they were latent, then make them active.
1143 if ([self isVisible])
1144 [self becameEligibleParentOrChild];
1146 [[WineApplicationController sharedController] adjustWindowLevels];
1149 if (state->minimized_valid)
1151 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1153 pendingMinimize = FALSE;
1154 if (state->minimized && ![self isMiniaturized])
1156 if ([self wouldBeVisible])
1158 if ([self styleMask] & NSFullScreenWindowMask)
1160 [self postDidUnminimizeEvent];
1161 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1165 [super miniaturize:nil];
1166 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1167 event_mask_for_type(WINDOW_GOT_FOCUS) |
1168 event_mask_for_type(WINDOW_LOST_FOCUS);
1172 pendingMinimize = TRUE;
1174 else if (!state->minimized && [self isMiniaturized])
1176 ignore_windowDeminiaturize = TRUE;
1177 [self deminiaturize:nil];
1178 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1182 [queue discardEventsMatchingMask:discard forWindow:self];
1185 if (state->maximized != maximized)
1187 maximized = state->maximized;
1188 [self adjustFeaturesForState];
1190 if (!maximized && [self inLiveResize])
1191 [self sendResizeStartQuery];
1194 behavior = NSWindowCollectionBehaviorDefault;
1195 if (state->excluded_by_expose)
1196 behavior |= NSWindowCollectionBehaviorTransient;
1198 behavior |= NSWindowCollectionBehaviorManaged;
1199 if (state->excluded_by_cycle)
1201 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1202 if ([self isOrderedIn])
1203 [NSApp removeWindowsItem:self];
1207 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1208 if ([self isOrderedIn])
1209 [NSApp addWindowsItem:self title:[self title] filename:NO];
1211 [self adjustFullScreenBehavior:behavior];
1214 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1216 BOOL reordered = FALSE;
1218 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1220 if ([self level] > [child level])
1221 [child setLevel:[self level]];
1222 if (![child isVisible])
1223 [child setAutodisplay:YES];
1224 [self addChildWindow:child ordered:NSWindowAbove];
1225 [child checkWineDisplayLink];
1226 [latentChildWindows removeObjectIdenticalTo:child];
1227 child.latentParentWindow = nil;
1232 if (!latentChildWindows)
1233 latentChildWindows = [[NSMutableArray alloc] init];
1234 if (![latentChildWindows containsObject:child])
1235 [latentChildWindows addObject:child];
1236 child.latentParentWindow = self;
1242 - (BOOL) addChildWineWindow:(WineWindow*)child
1244 return [self addChildWineWindow:child assumeVisible:FALSE];
1247 - (void) removeChildWineWindow:(WineWindow*)child
1249 [self removeChildWindow:child];
1250 if (child.latentParentWindow == self)
1251 child.latentParentWindow = nil;
1252 [latentChildWindows removeObjectIdenticalTo:child];
1255 - (void) setChildWineWindows:(NSArray*)childWindows
1257 NSArray* origChildren;
1258 NSUInteger count, start, limit, i;
1260 origChildren = self.childWineWindows;
1262 // If the current and desired children arrays match up to a point, leave
1263 // those matching children alone.
1264 count = childWindows.count;
1265 limit = MIN(origChildren.count, count);
1266 for (start = 0; start < limit; start++)
1268 if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1272 // Remove all of the child windows and re-add them back-to-front so they
1273 // are in the desired order.
1274 for (i = start; i < count; i++)
1276 WineWindow* child = [childWindows objectAtIndex:i];
1277 [self removeChildWindow:child];
1279 for (i = start; i < count; i++)
1281 WineWindow* child = [childWindows objectAtIndex:i];
1282 [self addChildWindow:child ordered:NSWindowAbove];
1286 static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1288 NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1289 NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1290 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1291 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1292 if (index1 == NSNotFound)
1294 if (index2 == NSNotFound)
1295 return NSOrderedSame;
1297 return NSOrderedAscending;
1299 else if (index2 == NSNotFound)
1300 return NSOrderedDescending;
1301 else if (index1 < index2)
1302 return NSOrderedDescending;
1303 else if (index2 < index1)
1304 return NSOrderedAscending;
1306 return NSOrderedSame;
1309 - (BOOL) becameEligibleParentOrChild
1311 BOOL reordered = FALSE;
1314 if (latentParentWindow.floating || !self.floating)
1316 // If we aren't visible currently, we assume that we should be and soon
1317 // will be. So, if the latent parent is visible that's enough to assume
1318 // we can establish the parent-child relationship in Cocoa. That will
1319 // actually make us visible, which is fine.
1320 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1324 // Here, though, we may not actually be visible yet and adding a child
1325 // won't make us visible. The caller will have to call this method
1326 // again after actually making us visible.
1327 if ([self isVisible] && (count = [latentChildWindows count]))
1329 NSMutableArray* windowNumbers;
1330 NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1331 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1334 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1336 for (i = 0; i < count; i++)
1338 WineWindow* child = [latentChildWindows objectAtIndex:i];
1339 if ([child isVisible] && (self.floating || !child.floating))
1341 if (child.latentParentWindow == self)
1343 if ([self level] > [child level])
1344 [child setLevel:[self level]];
1345 [childWindows addObject:child];
1346 child.latentParentWindow = nil;
1350 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1351 [indexesToRemove addIndex:i];
1355 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1357 [childWindows sortWithOptions:NSSortStable
1358 usingComparator:^NSComparisonResult(id obj1, id obj2){
1359 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1361 [self setChildWineWindows:childWindows];
1367 - (void) becameIneligibleChild
1369 WineWindow* parent = (WineWindow*)[self parentWindow];
1372 if (!parent->latentChildWindows)
1373 parent->latentChildWindows = [[NSMutableArray alloc] init];
1374 [parent->latentChildWindows insertObject:self atIndex:0];
1375 self.latentParentWindow = parent;
1376 [parent removeChildWindow:self];
1380 - (void) becameIneligibleParentOrChild
1382 NSArray* childWindows = [self childWineWindows];
1384 [self becameIneligibleChild];
1386 if ([childWindows count])
1390 for (child in childWindows)
1392 child.latentParentWindow = self;
1393 [self removeChildWindow:child];
1396 if (latentChildWindows)
1397 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1399 latentChildWindows = [childWindows mutableCopy];
1403 // Determine if, among Wine windows, this window is directly above or below
1404 // a given other Wine window with no other Wine window intervening.
1405 // Intervening non-Wine windows are ignored.
1406 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1408 NSNumber* windowNumber;
1409 NSNumber* otherWindowNumber;
1410 NSArray* windowNumbers;
1411 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1413 if (![self isVisible] || ![otherWindow isVisible])
1416 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1417 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1418 windowNumbers = [[self class] windowNumbersWithOptions:0];
1419 windowIndex = [windowNumbers indexOfObject:windowNumber];
1420 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1422 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1425 if (orderingMode == NSWindowAbove)
1427 lowIndex = windowIndex;
1428 highIndex = otherWindowIndex;
1430 else if (orderingMode == NSWindowBelow)
1432 lowIndex = otherWindowIndex;
1433 highIndex = windowIndex;
1438 if (highIndex <= lowIndex)
1441 for (i = lowIndex + 1; i < highIndex; i++)
1443 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1444 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1445 if ([interveningWindow isKindOfClass:[WineWindow class]])
1452 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1454 NSMutableArray* windowNumbers;
1455 NSNumber* childWindowNumber;
1456 NSUInteger otherIndex;
1457 NSArray* origChildren;
1458 NSMutableArray* children;
1460 // Get the z-order from the window server and modify it to reflect the
1461 // requested window ordering.
1462 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1463 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1464 [windowNumbers removeObject:childWindowNumber];
1467 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1468 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1470 else if (mode == NSWindowAbove)
1471 [windowNumbers insertObject:childWindowNumber atIndex:0];
1473 [windowNumbers addObject:childWindowNumber];
1475 // Get our child windows and sort them in the reverse of the desired
1476 // z-order (back-to-front).
1477 origChildren = [self childWineWindows];
1478 children = [[origChildren mutableCopy] autorelease];
1479 [children sortWithOptions:NSSortStable
1480 usingComparator:^NSComparisonResult(id obj1, id obj2){
1481 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1484 [self setChildWineWindows:children];
1487 // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1488 // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1489 // (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
1490 // if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1491 // otherwise, the two ancestors are each roots of disjoint window trees
1492 // (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
1493 // (non-nil, nil) self is a descendent of other and *ancestor is the direct child
1494 - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1496 NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1500 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1506 *ancestorOfOther = child;
1510 [otherAncestors addObject:parent];
1514 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1517 NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1518 if (index != NSNotFound)
1522 *ancestorOfOther = nil;
1524 *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1530 *ancestorOfOther = otherAncestors.lastObject;;
1533 /* Returns whether or not the window was ordered in, which depends on if
1534 its frame intersects any screen. */
1535 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1537 WineApplicationController* controller = [WineApplicationController sharedController];
1538 if (![self isMiniaturized])
1540 BOOL needAdjustWindowLevels = FALSE;
1545 [controller transformProcessToForeground];
1546 if ([NSApp isHidden])
1548 wasVisible = [self isVisible];
1551 [NSApp activateIgnoringOtherApps:YES];
1553 NSDisableScreenUpdates();
1555 if ([self becameEligibleParentOrChild])
1556 needAdjustWindowLevels = TRUE;
1560 WineWindow* other = [prev isVisible] ? prev : next;
1561 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1563 if (![self isOrdered:orderingMode relativeTo:other])
1565 WineWindow* ancestor;
1566 WineWindow* ancestorOfOther;
1568 [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1571 [self setAutodisplay:YES];
1572 if (ancestorOfOther)
1574 // This window level may not be right for this window based
1575 // on floating-ness, fullscreen-ness, etc. But we set it
1576 // temporarily to allow us to order the windows properly.
1577 // Then the levels get fixed by -adjustWindowLevels.
1578 if ([ancestor level] != [ancestorOfOther level])
1579 [ancestor setLevel:[ancestorOfOther level]];
1581 parent = (WineWindow*)ancestor.parentWindow;
1582 if ([parent isKindOfClass:[WineWindow class]])
1583 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1585 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1589 (parent = (WineWindow*)child.parentWindow);
1592 if ([parent isKindOfClass:[WineWindow class]])
1593 [parent order:-orderingMode childWindow:child relativeTo:nil];
1594 if (parent == ancestor)
1598 [self checkWineDisplayLink];
1599 needAdjustWindowLevels = TRUE;
1606 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1609 [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1612 // Again, temporarily set level to make sure we can order to
1614 next = [controller frontWineWindow];
1615 if (next && [self level] < [next level])
1616 [self setLevel:[next level]];
1617 [self setAutodisplay:YES];
1618 [self orderFront:nil];
1619 [self checkWineDisplayLink];
1620 needAdjustWindowLevels = TRUE;
1622 pendingOrderOut = FALSE;
1624 if ([self becameEligibleParentOrChild])
1625 needAdjustWindowLevels = TRUE;
1627 if (needAdjustWindowLevels)
1629 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1630 [controller updateFullscreenWindows];
1631 [controller adjustWindowLevels];
1634 if (pendingMinimize)
1636 [super miniaturize:nil];
1637 pendingMinimize = FALSE;
1640 NSEnableScreenUpdates();
1642 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1643 Generate a frame-changed event just in case. The back end will ignore
1644 it if nothing actually changed. */
1645 [self windowDidResize:nil];
1647 if (![self isExcludedFromWindowsMenu])
1648 [NSApp addWindowsItem:self title:[self title] filename:NO];
1654 WineApplicationController* controller = [WineApplicationController sharedController];
1655 BOOL wasVisible = [self isVisible];
1656 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1658 if (enteringFullScreen || exitingFullScreen)
1660 pendingOrderOut = TRUE;
1661 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1662 event_mask_for_type(WINDOW_GOT_FOCUS) |
1663 event_mask_for_type(WINDOW_LOST_FOCUS) |
1664 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1665 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1666 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1671 pendingOrderOut = FALSE;
1673 if ([self isMiniaturized])
1674 pendingMinimize = TRUE;
1676 WineWindow* parent = (WineWindow*)self.parentWindow;
1677 if ([parent isKindOfClass:[WineWindow class]])
1678 [parent grabDockIconSnapshotFromWindow:self force:NO];
1680 [self becameIneligibleParentOrChild];
1681 if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1685 fakingClose = FALSE;
1688 [self orderOut:nil];
1689 [self checkWineDisplayLink];
1690 [self setBackgroundColor:[NSColor clearColor]];
1691 [self setOpaque:NO];
1692 drawnSinceShown = NO;
1693 savedVisibleState = FALSE;
1694 if (wasVisible && wasOnActiveSpace && fullscreen)
1695 [controller updateFullscreenWindows];
1696 [controller adjustWindowLevels];
1697 [NSApp removeWindowsItem:self];
1699 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1700 event_mask_for_type(WINDOW_GOT_FOCUS) |
1701 event_mask_for_type(WINDOW_LOST_FOCUS) |
1702 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1703 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1704 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1708 - (void) updateFullscreen
1710 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1711 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1713 if (nowFullscreen != fullscreen)
1715 WineApplicationController* controller = [WineApplicationController sharedController];
1717 fullscreen = nowFullscreen;
1718 if ([self isVisible] && [self isOnActiveSpace])
1719 [controller updateFullscreenWindows];
1721 [controller adjustWindowLevels];
1725 - (void) setFrameAndWineFrame:(NSRect)frame
1727 [self setFrame:frame display:YES];
1730 roundedWineFrame = self.frame;
1732 #if CGFLOAT_IS_DOUBLE
1733 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1734 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1735 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1736 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1737 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1738 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1739 roundedWineFrame = wineFrame;
1741 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1742 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1743 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1744 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1745 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1746 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1747 roundedWineFrame = wineFrame;
1751 - (void) setFrameFromWine:(NSRect)contentRect
1753 /* Origin is (left, top) in a top-down space. Need to convert it to
1754 (left, bottom) in a bottom-up space. */
1755 [[WineApplicationController sharedController] flipRect:&contentRect];
1757 /* The back end is establishing a new window size and position. It's
1758 not interested in any stale events regarding those that may be sitting
1760 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1763 if (!NSIsEmptyRect(contentRect))
1765 NSRect frame, oldFrame;
1767 oldFrame = self.wine_fractionalFrame;
1768 frame = [self frameRectForContentRect:contentRect];
1769 if (!NSEqualRects(frame, oldFrame))
1771 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1772 BOOL needEnableScreenUpdates = FALSE;
1774 if ([self preventResizing])
1776 // Allow the following calls to -setFrame:display: to work even
1777 // if they would violate the content size constraints. This
1778 // shouldn't be necessary since the content size constraints are
1779 // documented to not constrain that method, but it seems to be.
1780 [self setContentMinSize:NSZeroSize];
1781 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1784 if (equalSizes && [[self childWineWindows] count])
1786 // If we change the window frame such that the origin moves
1787 // but the size doesn't change, then Cocoa moves child
1788 // windows with the parent. We don't want that so we fake
1789 // a change of the size and then change it back.
1790 NSRect bogusFrame = frame;
1791 bogusFrame.size.width++;
1793 NSDisableScreenUpdates();
1794 needEnableScreenUpdates = TRUE;
1796 ignore_windowResize = TRUE;
1797 [self setFrame:bogusFrame display:NO];
1798 ignore_windowResize = FALSE;
1801 [self setFrameAndWineFrame:frame];
1802 if ([self preventResizing])
1804 [self setContentMinSize:contentRect.size];
1805 [self setContentMaxSize:contentRect.size];
1808 if (needEnableScreenUpdates)
1809 NSEnableScreenUpdates();
1812 [self updateColorSpace];
1814 if (!enteringFullScreen &&
1815 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1816 nonFullscreenFrame = frame;
1818 [self updateFullscreen];
1820 if ([self isOrderedIn])
1822 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1823 event. The back end will ignore it if nothing actually changed. */
1824 [self windowDidResize:nil];
1830 - (NSRect) wine_fractionalFrame
1832 NSRect frame = self.frame;
1833 if (NSEqualRects(frame, roundedWineFrame))
1838 - (void) setMacDrvParentWindow:(WineWindow*)parent
1840 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1841 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1843 [oldParent removeChildWineWindow:self];
1844 [latentParentWindow removeChildWineWindow:self];
1845 if ([parent addChildWineWindow:self])
1846 [[WineApplicationController sharedController] adjustWindowLevels];
1847 [self adjustFullScreenBehavior:[self collectionBehavior]];
1851 - (void) setDisabled:(BOOL)newValue
1853 if (disabled != newValue)
1855 disabled = newValue;
1856 [self adjustFeaturesForState];
1860 - (BOOL) needsTransparency
1862 return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1863 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1866 - (void) checkTransparency
1868 if (![self isOpaque] && !self.needsTransparency)
1870 self.shapeChangedSinceLastDraw = TRUE;
1871 [[self contentView] setNeedsDisplay:YES];
1872 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1873 [self setOpaque:YES];
1875 else if ([self isOpaque] && self.needsTransparency)
1877 self.shapeChangedSinceLastDraw = TRUE;
1878 [[self contentView] setNeedsDisplay:YES];
1879 [self setBackgroundColor:[NSColor clearColor]];
1880 [self setOpaque:NO];
1884 - (void) setShape:(NSBezierPath*)newShape
1886 if (shape == newShape) return;
1890 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1894 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1896 shape = [newShape copy];
1897 self.shapeChangedSinceLastDraw = TRUE;
1899 [self checkTransparency];
1902 - (void) makeFocused:(BOOL)activate
1906 [[WineApplicationController sharedController] transformProcessToForeground];
1907 [NSApp activateIgnoringOtherApps:YES];
1910 causing_becomeKeyWindow = self;
1911 [self makeKeyWindow];
1912 causing_becomeKeyWindow = nil;
1914 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1915 event_mask_for_type(WINDOW_LOST_FOCUS)
1919 - (void) postKey:(uint16_t)keyCode
1920 pressed:(BOOL)pressed
1921 modifiers:(NSUInteger)modifiers
1922 event:(NSEvent*)theEvent
1924 macdrv_event* event;
1926 WineApplicationController* controller = [WineApplicationController sharedController];
1928 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1929 event->key.keycode = keyCode;
1930 event->key.modifiers = modifiers;
1931 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1933 if ((cgevent = [theEvent CGEvent]))
1935 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1936 kCGKeyboardEventKeyboardType);
1937 if (keyboardType != controller.keyboardType)
1939 controller.keyboardType = keyboardType;
1940 [controller keyboardSelectionDidChange];
1944 [queue postEvent:event];
1946 macdrv_release_event(event);
1948 [controller noteKey:keyCode pressed:pressed];
1951 - (void) postKeyEvent:(NSEvent *)theEvent
1953 [self flagsChanged:theEvent];
1954 [self postKey:[theEvent keyCode]
1955 pressed:[theEvent type] == NSKeyDown
1956 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1960 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1962 savedContentMinSize = minSize;
1963 savedContentMaxSize = maxSize;
1964 if (![self preventResizing])
1966 [self setContentMinSize:minSize];
1967 [self setContentMaxSize:maxSize];
1971 - (WineWindow*) ancestorWineWindow
1973 WineWindow* ancestor = self;
1976 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1977 if ([parent isKindOfClass:[WineWindow class]])
1985 - (void) postBroughtForwardEvent
1987 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1988 [queue postEvent:event];
1989 macdrv_release_event(event);
1992 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
1994 macdrv_event* event;
1995 NSUInteger style = self.styleMask;
1998 style |= NSFullScreenWindowMask;
2000 style &= ~NSFullScreenWindowMask;
2001 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2002 [[WineApplicationController sharedController] flipRect:&frame];
2004 /* Coalesce events by discarding any previous ones still in the queue. */
2005 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2008 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2009 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2010 event->window_frame_changed.fullscreen = isFullscreen;
2011 event->window_frame_changed.in_resize = resizing;
2012 [queue postEvent:event];
2013 macdrv_release_event(event);
2016 - (void) updateForCursorClipping
2018 [self adjustFeaturesForState];
2021 - (void) endWindowDragging
2025 if (draggingPhase == 3)
2027 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2028 [queue postEvent:event];
2029 macdrv_release_event(event);
2033 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2037 - (NSMutableDictionary*) displayIDToDisplayLinkMap
2039 static NSMutableDictionary* displayIDToDisplayLinkMap;
2040 if (!displayIDToDisplayLinkMap)
2042 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2044 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2047 usingBlock:^(NSNotification *note){
2048 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2049 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2050 [badDisplayIDs minusSet:validDisplayIDs];
2051 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2054 return displayIDToDisplayLinkMap;
2057 - (WineDisplayLink*) wineDisplayLink
2059 if (!_lastDisplayID)
2062 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2063 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2066 - (void) checkWineDisplayLink
2068 NSScreen* screen = self.screen;
2069 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2071 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2072 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2076 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2077 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2078 if (displayID == _lastDisplayID)
2081 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2085 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2086 [link removeWindow:self];
2090 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2093 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2094 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2096 [link addWindow:self];
2097 [self displayIfNeeded];
2099 _lastDisplayID = displayID;
2102 - (BOOL) isEmptyShaped
2104 return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2107 - (BOOL) canProvideSnapshot
2109 return (self.windowNumber > 0 && ![self isEmptyShaped]);
2112 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2114 if (![self isEmptyShaped])
2117 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2118 if (!force && now < lastDockIconSnapshot + 1)
2123 if (![window canProvideSnapshot])
2129 for (WineWindow* childWindow in self.childWindows)
2131 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2134 NSSize size = childWindow.frame.size;
2135 CGFloat area = size.width * size.height;
2136 if (!window || area > bestArea)
2138 window = childWindow;
2147 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2148 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2149 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2150 CFRelease(windowIDs);
2154 NSImage* appImage = [NSApp applicationIconImage];
2156 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2158 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2159 [dockIcon lockFocus];
2161 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2163 CGRect rect = CGRectMake(8, 8, 240, 240);
2164 size_t width = CGImageGetWidth(windowImage);
2165 size_t height = CGImageGetHeight(windowImage);
2168 rect.size.height *= height / (double)width;
2169 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2171 else if (width != height)
2173 rect.size.width *= width / (double)height;
2174 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2177 CGContextDrawImage(cgcontext, rect, windowImage);
2178 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2180 operation:NSCompositeSourceOver
2185 [dockIcon unlockFocus];
2187 CGImageRelease(windowImage);
2189 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2190 if (![imageView isKindOfClass:[NSImageView class]])
2192 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2193 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2194 self.dockTile.contentView = imageView;
2196 imageView.image = dockIcon;
2197 [self.dockTile display];
2198 lastDockIconSnapshot = now;
2201 - (void) checkEmptyShaped
2203 if (self.dockTile.contentView && ![self isEmptyShaped])
2205 self.dockTile.contentView = nil;
2206 lastDockIconSnapshot = 0;
2208 [self checkWineDisplayLink];
2213 * ---------- NSWindow method overrides ----------
2215 - (BOOL) canBecomeKeyWindow
2217 if (causing_becomeKeyWindow == self) return YES;
2218 if (self.disabled || self.noActivate) return NO;
2219 return [self isKeyWindow];
2222 - (BOOL) canBecomeMainWindow
2224 return [self canBecomeKeyWindow];
2227 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2229 // If a window is sized to completely cover a screen, then it's in
2230 // full-screen mode. In that case, we don't allow NSWindow to constrain
2232 NSArray* screens = [NSScreen screens];
2233 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2234 if (!screen_covered_by_rect(contentRect, screens) &&
2235 frame_intersects_screens(frameRect, screens))
2236 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2240 // This private method of NSWindow is called as Cocoa reacts to the display
2241 // configuration changing. Among other things, it adjusts the window's
2242 // frame based on how the screen(s) changed size. That tells Wine that the
2243 // window has been moved. We don't want that. Rather, we want to make
2244 // sure that the WinAPI notion of the window position is maintained/
2245 // restored, possibly undoing or overriding Cocoa's adjustment.
2247 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2248 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2249 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2250 // reassert its notion of the position. That call won't get processed
2251 // until after this method returns, so it will override whatever this
2252 // method does to the window position. It will also discard any pending
2253 // WINDOW_FRAME_CHANGED events.
2255 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2256 // adjust the window's position due to a display change is to hook into
2257 // this private method. This private method has remained stable from 10.6
2258 // through 10.11. If it does change, the most likely thing is that it
2259 // will be removed and no longer called and this fix will simply stop
2260 // working. The only real danger would be if Apple changed the return type
2261 // to a struct or floating-point type, which would change the calling
2263 - (id) _displayChanged
2265 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2266 [queue postEvent:event];
2267 macdrv_release_event(event);
2269 return [super _displayChanged];
2272 - (BOOL) isExcludedFromWindowsMenu
2274 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2277 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2279 BOOL ret = [super validateMenuItem:menuItem];
2281 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2282 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2283 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2289 /* We don't call this. It's the action method of the items in the Window menu. */
2290 - (void) makeKeyAndOrderFront:(id)sender
2292 if ([self isMiniaturized])
2293 [self deminiaturize:nil];
2294 [self orderBelow:nil orAbove:nil activate:NO];
2295 [[self ancestorWineWindow] postBroughtForwardEvent];
2297 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2298 [[WineApplicationController sharedController] windowGotFocus:self];
2301 - (void) sendEvent:(NSEvent*)event
2303 NSEventType type = event.type;
2305 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2306 interface control. For example, Control-Tab switches focus among
2307 views. We want to bypass that feature, so directly route key-down
2308 events to -keyDown:. */
2309 if (type == NSKeyDown)
2310 [[self firstResponder] keyDown:event];
2313 if (!draggingPhase && maximized && ![self isMovable] &&
2314 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2315 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2317 NSRect titleBar = self.frame;
2318 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2319 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2320 titleBar.origin.y = NSMaxY(contentRect);
2322 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2324 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2326 static const NSWindowButton buttons[] = {
2327 NSWindowCloseButton,
2328 NSWindowMiniaturizeButton,
2330 NSWindowFullScreenButton,
2332 BOOL hitButton = NO;
2335 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2339 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2342 button = [self standardWindowButton:buttons[i]];
2343 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2353 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2354 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2358 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2360 if ([self isMovable])
2362 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2363 NSPoint newTopLeft = dragWindowStartPosition;
2365 newTopLeft.x += point.x - dragStartPosition.x;
2366 newTopLeft.y += point.y - dragStartPosition.y;
2368 if (draggingPhase == 2)
2370 macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2371 mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2372 [queue postEvent:mevent];
2373 macdrv_release_event(mevent);
2378 [self setFrameTopLeftPoint:newTopLeft];
2380 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2382 macdrv_event* event;
2383 NSRect frame = [self contentRectForFrameRect:self.frame];
2385 [[WineApplicationController sharedController] flipRect:&frame];
2387 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2388 event->window_restore_requested.keep_frame = TRUE;
2389 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2390 [queue postEvent:event];
2391 macdrv_release_event(event);
2396 if (type == NSLeftMouseUp)
2397 [self endWindowDragging];
2400 [super sendEvent:event];
2404 - (void) miniaturize:(id)sender
2406 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2407 [queue postEvent:event];
2408 macdrv_release_event(event);
2410 WineWindow* parent = (WineWindow*)self.parentWindow;
2411 if ([parent isKindOfClass:[WineWindow class]])
2412 [parent grabDockIconSnapshotFromWindow:self force:YES];
2415 - (void) toggleFullScreen:(id)sender
2417 if (!self.disabled && !maximized)
2418 [super toggleFullScreen:sender];
2421 - (void) setViewsNeedDisplay:(BOOL)value
2423 if (value && ![self viewsNeedDisplay])
2425 WineDisplayLink* link = [self wineDisplayLink];
2428 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2429 if (_lastDisplayTime + [link refreshPeriod] < now)
2430 [self setAutodisplay:YES];
2434 _lastDisplayTime = now;
2438 [super setViewsNeedDisplay:value];
2443 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2445 [self setAutodisplay:NO];
2448 - (void) displayIfNeeded
2450 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2451 [super displayIfNeeded];
2452 [self setAutodisplay:NO];
2455 - (void) windowDidDrawContent
2457 if (!drawnSinceShown)
2459 drawnSinceShown = YES;
2460 dispatch_async(dispatch_get_main_queue(), ^{
2461 [self checkTransparency];
2466 - (NSArray*) childWineWindows
2468 NSArray* childWindows = self.childWindows;
2469 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2470 return [child isKindOfClass:[WineWindow class]];
2472 return [childWindows objectsAtIndexes:indexes];
2475 // We normally use the generic/calibrated RGB color space for the window,
2476 // rather than the device color space, to avoid expensive color conversion
2477 // which slows down drawing. However, for windows displaying OpenGL, having
2478 // a different color space than the screen greatly reduces frame rates, often
2479 // limiting it to the display refresh rate.
2481 // To avoid this, we switch back to the screen color space whenever the
2482 // window is covered by a view with an attached OpenGL context.
2483 - (void) updateColorSpace
2485 NSRect contentRect = [[self contentView] frame];
2486 BOOL coveredByGLView = FALSE;
2487 WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2488 if ([view isKindOfClass:[WineContentView class]] && [view hasGLContext])
2490 NSRect frame = [view convertRect:[view bounds] toView:nil];
2491 if (NSContainsRect(frame, contentRect))
2492 coveredByGLView = TRUE;
2495 if (coveredByGLView)
2496 [self setColorSpace:nil];
2498 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2501 - (void) updateForGLSubviews
2503 [self updateColorSpace];
2504 if (gl_surface_mode == GL_SURFACE_BEHIND)
2505 [self checkTransparency];
2508 - (void) setRetinaMode:(int)mode
2511 double scale = mode ? 0.5 : 2.0;
2512 NSAffineTransform* transform = [NSAffineTransform transform];
2514 [transform scaleBy:scale];
2517 [shape transformUsingAffineTransform:transform];
2519 for (WineContentView* subview in [self.contentView subviews])
2521 if ([subview isKindOfClass:[WineContentView class]])
2522 [subview setRetinaMode:mode];
2525 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2526 frame.origin.x *= scale;
2527 frame.origin.y *= scale;
2528 frame.size.width *= scale;
2529 frame.size.height *= scale;
2530 frame = [self frameRectForContentRect:frame];
2532 savedContentMinSize = [transform transformSize:savedContentMinSize];
2533 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2534 savedContentMaxSize.width *= scale;
2535 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2536 savedContentMaxSize.height *= scale;
2538 self.contentMinSize = [transform transformSize:self.contentMinSize];
2539 NSSize temp = self.contentMaxSize;
2540 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2541 temp.width *= scale;
2542 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2543 temp.height *= scale;
2544 self.contentMaxSize = temp;
2546 ignore_windowResize = TRUE;
2547 [self setFrameAndWineFrame:frame];
2548 ignore_windowResize = FALSE;
2553 * ---------- NSResponder method overrides ----------
2555 - (void) keyDown:(NSEvent *)theEvent
2557 if ([theEvent isARepeat])
2559 if (!allowKeyRepeats)
2563 allowKeyRepeats = YES;
2565 [self postKeyEvent:theEvent];
2568 - (void) flagsChanged:(NSEvent *)theEvent
2570 static const struct {
2574 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2575 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2576 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2577 { NX_DEVICELCTLKEYMASK, kVK_Control },
2578 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2579 { NX_DEVICELALTKEYMASK, kVK_Option },
2580 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2581 { NX_DEVICELCMDKEYMASK, kVK_Command },
2582 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2585 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2587 int i, last_changed;
2589 fix_device_modifiers_by_generic(&modifierFlags);
2590 changed = modifierFlags ^ lastModifierFlags;
2593 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2594 if (changed & modifiers[i].mask)
2597 for (i = 0; i <= last_changed; i++)
2599 if (changed & modifiers[i].mask)
2601 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2604 allowKeyRepeats = NO;
2606 if (i == last_changed)
2607 lastModifierFlags = modifierFlags;
2610 lastModifierFlags ^= modifiers[i].mask;
2611 fix_generic_modifiers_by_device(&lastModifierFlags);
2614 // Caps lock generates one event for each press-release action.
2615 // We need to simulate a pair of events for each actual event.
2616 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2618 [self postKey:modifiers[i].keycode
2620 modifiers:lastModifierFlags
2621 event:(NSEvent*)theEvent];
2625 [self postKey:modifiers[i].keycode
2627 modifiers:lastModifierFlags
2628 event:(NSEvent*)theEvent];
2633 - (void) applicationWillHide
2635 savedVisibleState = [self isVisible];
2638 - (void) applicationDidUnhide
2640 if ([self isVisible])
2641 [self becameEligibleParentOrChild];
2646 * ---------- NSWindowDelegate methods ----------
2648 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2650 macdrv_query* query;
2653 query = macdrv_create_query();
2654 query->type = QUERY_MIN_MAX_INFO;
2655 query->window = (macdrv_window)[self retain];
2656 [self.queue query:query timeout:0.5];
2657 macdrv_release_query(query);
2659 size = [self contentMaxSize];
2660 if (proposedSize.width < size.width)
2661 size.width = proposedSize.width;
2662 if (proposedSize.height < size.height)
2663 size.height = proposedSize.height;
2667 - (void)windowDidBecomeKey:(NSNotification *)notification
2669 WineApplicationController* controller = [WineApplicationController sharedController];
2670 NSEvent* event = [controller lastFlagsChanged];
2672 [self flagsChanged:event];
2674 if (causing_becomeKeyWindow == self) return;
2676 [controller windowGotFocus:self];
2679 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2681 [self checkWineDisplayLink];
2684 - (void) windowDidChangeScreen:(NSNotification*)notification
2686 [self checkWineDisplayLink];
2689 - (void)windowDidDeminiaturize:(NSNotification *)notification
2691 WineApplicationController* controller = [WineApplicationController sharedController];
2693 if (!ignore_windowDeminiaturize)
2694 [self postDidUnminimizeEvent];
2695 ignore_windowDeminiaturize = FALSE;
2697 [self becameEligibleParentOrChild];
2699 if (fullscreen && [self isOnActiveSpace])
2700 [controller updateFullscreenWindows];
2701 [controller adjustWindowLevels];
2703 if (![self parentWindow])
2704 [self postBroughtForwardEvent];
2706 if (!self.disabled && !self.noActivate)
2708 causing_becomeKeyWindow = self;
2709 [self makeKeyWindow];
2710 causing_becomeKeyWindow = nil;
2711 [controller windowGotFocus:self];
2714 [self windowDidResize:notification];
2715 [self checkWineDisplayLink];
2718 - (void) windowDidEndLiveResize:(NSNotification *)notification
2722 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2723 [queue postEvent:event];
2724 macdrv_release_event(event);
2728 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2730 enteringFullScreen = FALSE;
2731 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2732 if (pendingOrderOut)
2736 - (void) windowDidExitFullScreen:(NSNotification*)notification
2738 exitingFullScreen = FALSE;
2739 [self setFrameAndWineFrame:nonFullscreenFrame];
2740 [self windowDidResize:nil];
2741 if (pendingOrderOut)
2745 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2747 enteringFullScreen = FALSE;
2748 enteredFullScreenTime = 0;
2749 if (pendingOrderOut)
2753 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2755 exitingFullScreen = FALSE;
2756 [self windowDidResize:nil];
2757 if (pendingOrderOut)
2761 - (void)windowDidMiniaturize:(NSNotification *)notification
2763 if (fullscreen && [self isOnActiveSpace])
2764 [[WineApplicationController sharedController] updateFullscreenWindows];
2765 [self checkWineDisplayLink];
2768 - (void)windowDidMove:(NSNotification *)notification
2770 [self windowDidResize:notification];
2773 - (void)windowDidResignKey:(NSNotification *)notification
2775 macdrv_event* event;
2777 if (causing_becomeKeyWindow) return;
2779 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2780 [queue postEvent:event];
2781 macdrv_release_event(event);
2784 - (void)windowDidResize:(NSNotification *)notification
2786 NSRect frame = self.wine_fractionalFrame;
2788 if ([self inLiveResize])
2790 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2791 resizingFromLeft = TRUE;
2792 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2793 resizingFromTop = TRUE;
2796 if (ignore_windowResize || exitingFullScreen) return;
2798 if ([self preventResizing])
2800 NSRect contentRect = [self contentRectForFrameRect:frame];
2801 [self setContentMinSize:contentRect.size];
2802 [self setContentMaxSize:contentRect.size];
2805 [self postWindowFrameChanged:frame
2806 fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2807 resizing:[self inLiveResize]];
2809 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2810 [self updateFullscreen];
2813 - (BOOL)windowShouldClose:(id)sender
2815 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2816 [queue postEvent:event];
2817 macdrv_release_event(event);
2821 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2825 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2826 [queue postEvent:event];
2827 macdrv_release_event(event);
2830 else if (!resizable)
2832 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2833 [queue postEvent:event];
2834 macdrv_release_event(event);
2841 - (void) windowWillClose:(NSNotification*)notification
2845 if (fakingClose) return;
2846 if (latentParentWindow)
2848 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2849 self.latentParentWindow = nil;
2852 for (child in latentChildWindows)
2854 if (child.latentParentWindow == self)
2855 child.latentParentWindow = nil;
2857 [latentChildWindows removeAllObjects];
2860 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2862 enteringFullScreen = TRUE;
2863 nonFullscreenFrame = self.wine_fractionalFrame;
2866 - (void) windowWillExitFullScreen:(NSNotification*)notification
2868 exitingFullScreen = TRUE;
2869 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2872 - (void)windowWillMiniaturize:(NSNotification *)notification
2874 [self becameIneligibleParentOrChild];
2875 [self grabDockIconSnapshotFromWindow:nil force:NO];
2878 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2880 if ([self inLiveResize])
2883 return self.wine_fractionalFrame.size;
2886 macdrv_query* query;
2888 rect = [self frame];
2889 if (resizingFromLeft)
2890 rect.origin.x = NSMaxX(rect) - frameSize.width;
2891 if (!resizingFromTop)
2892 rect.origin.y = NSMaxY(rect) - frameSize.height;
2893 rect.size = frameSize;
2894 rect = [self contentRectForFrameRect:rect];
2895 [[WineApplicationController sharedController] flipRect:&rect];
2897 query = macdrv_create_query();
2898 query->type = QUERY_RESIZE_SIZE;
2899 query->window = (macdrv_window)[self retain];
2900 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2901 query->resize_size.from_left = resizingFromLeft;
2902 query->resize_size.from_top = resizingFromTop;
2904 if ([self.queue query:query timeout:0.1])
2906 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2907 rect = [self frameRectForContentRect:rect];
2908 frameSize = rect.size;
2911 macdrv_release_query(query);
2917 - (void) windowWillStartLiveResize:(NSNotification *)notification
2919 [self endWindowDragging];
2923 macdrv_event* event;
2924 NSRect frame = [self contentRectForFrameRect:self.frame];
2926 [[WineApplicationController sharedController] flipRect:&frame];
2928 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2929 event->window_restore_requested.keep_frame = TRUE;
2930 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2931 [queue postEvent:event];
2932 macdrv_release_event(event);
2935 [self sendResizeStartQuery];
2937 frameAtResizeStart = [self frame];
2938 resizingFromLeft = resizingFromTop = FALSE;
2941 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2943 macdrv_query* query;
2944 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2947 query = macdrv_create_query();
2948 query->type = QUERY_MIN_MAX_INFO;
2949 query->window = (macdrv_window)[self retain];
2950 [self.queue query:query timeout:0.5];
2951 macdrv_release_query(query);
2953 currentContentRect = [self contentRectForFrameRect:[self frame]];
2954 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2956 maxSize = [self contentMaxSize];
2957 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2958 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2960 // Try to keep the top-left corner where it is.
2961 newContentRect.origin.x = NSMinX(currentContentRect);
2962 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2964 // If that pushes the bottom or right off the screen, pull it up and to the left.
2965 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2966 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2967 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2968 if (NSMinY(newContentRect) < NSMinY(screenRect))
2969 newContentRect.origin.y = NSMinY(screenRect);
2971 // If that pushes the top or left off the screen, push it down and the right
2972 // again. Do this last because the top-left corner is more important than the
2974 if (NSMinX(newContentRect) < NSMinX(screenRect))
2975 newContentRect.origin.x = NSMinX(screenRect);
2976 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2977 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2979 return [self frameRectForContentRect:newContentRect];
2984 * ---------- NSPasteboardOwner methods ----------
2986 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2988 macdrv_query* query = macdrv_create_query();
2989 query->type = QUERY_PASTEBOARD_DATA;
2990 query->window = (macdrv_window)[self retain];
2991 query->pasteboard_data.type = (CFStringRef)[type copy];
2993 [self.queue query:query timeout:3];
2994 macdrv_release_query(query);
2997 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
2999 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3000 [queue postEvent:event];
3001 macdrv_release_event(event);
3006 * ---------- NSDraggingDestination methods ----------
3008 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3010 return [self draggingUpdated:sender];
3013 - (void) draggingExited:(id <NSDraggingInfo>)sender
3015 // This isn't really a query. We don't need any response. However, it
3016 // has to be processed in a similar manner as the other drag-and-drop
3017 // queries in order to maintain the proper order of operations.
3018 macdrv_query* query = macdrv_create_query();
3019 query->type = QUERY_DRAG_EXITED;
3020 query->window = (macdrv_window)[self retain];
3022 [self.queue query:query timeout:0.1];
3023 macdrv_release_query(query);
3026 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3028 NSDragOperation ret;
3029 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3030 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3031 NSPasteboard* pb = [sender draggingPasteboard];
3033 macdrv_query* query = macdrv_create_query();
3034 query->type = QUERY_DRAG_OPERATION;
3035 query->window = (macdrv_window)[self retain];
3036 query->drag_operation.x = floor(cgpt.x);
3037 query->drag_operation.y = floor(cgpt.y);
3038 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3039 query->drag_operation.accepted_op = NSDragOperationNone;
3040 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3042 [self.queue query:query timeout:3];
3043 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3044 macdrv_release_query(query);
3049 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3052 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3053 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3054 NSPasteboard* pb = [sender draggingPasteboard];
3056 macdrv_query* query = macdrv_create_query();
3057 query->type = QUERY_DRAG_DROP;
3058 query->window = (macdrv_window)[self retain];
3059 query->drag_drop.x = floor(cgpt.x);
3060 query->drag_drop.y = floor(cgpt.y);
3061 query->drag_drop.op = [sender draggingSourceOperationMask];
3062 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3064 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3065 ret = query->status;
3066 macdrv_release_query(query);
3071 - (BOOL) wantsPeriodicDraggingUpdates
3079 /***********************************************************************
3080 * macdrv_create_cocoa_window
3082 * Create a Cocoa window with the given content frame and features (e.g.
3083 * title bar, close box, etc.).
3085 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3086 CGRect frame, void* hwnd, macdrv_event_queue queue)
3088 __block WineWindow* window;
3091 window = [[WineWindow createWindowWithFeatures:wf
3092 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3094 queue:(WineEventQueue*)queue] retain];
3097 return (macdrv_window)window;
3100 /***********************************************************************
3101 * macdrv_destroy_cocoa_window
3103 * Destroy a Cocoa window.
3105 void macdrv_destroy_cocoa_window(macdrv_window w)
3107 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3108 WineWindow* window = (WineWindow*)w;
3111 [window doOrderOut];
3114 [window.queue discardEventsMatchingMask:-1 forWindow:window];
3120 /***********************************************************************
3121 * macdrv_get_window_hwnd
3123 * Get the hwnd that was set for the window at creation.
3125 void* macdrv_get_window_hwnd(macdrv_window w)
3127 WineWindow* window = (WineWindow*)w;
3131 /***********************************************************************
3132 * macdrv_set_cocoa_window_features
3134 * Update a Cocoa window's features.
3136 void macdrv_set_cocoa_window_features(macdrv_window w,
3137 const struct macdrv_window_features* wf)
3139 WineWindow* window = (WineWindow*)w;
3142 [window setWindowFeatures:wf];
3146 /***********************************************************************
3147 * macdrv_set_cocoa_window_state
3149 * Update a Cocoa window's state.
3151 void macdrv_set_cocoa_window_state(macdrv_window w,
3152 const struct macdrv_window_state* state)
3154 WineWindow* window = (WineWindow*)w;
3157 [window setMacDrvState:state];
3161 /***********************************************************************
3162 * macdrv_set_cocoa_window_title
3164 * Set a Cocoa window's title.
3166 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3169 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3170 WineWindow* window = (WineWindow*)w;
3171 NSString* titleString;
3174 titleString = [NSString stringWithCharacters:title length:length];
3177 OnMainThreadAsync(^{
3178 [window setTitle:titleString];
3179 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3180 [NSApp changeWindowsItem:window title:titleString filename:NO];
3186 /***********************************************************************
3187 * macdrv_order_cocoa_window
3189 * Reorder a Cocoa window relative to other windows. If prev is
3190 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3191 * it is ordered above that window. Otherwise, it is ordered to the
3194 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3195 macdrv_window n, int activate)
3197 WineWindow* window = (WineWindow*)w;
3198 WineWindow* prev = (WineWindow*)p;
3199 WineWindow* next = (WineWindow*)n;
3201 OnMainThreadAsync(^{
3202 [window orderBelow:prev
3206 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3208 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3212 /***********************************************************************
3213 * macdrv_hide_cocoa_window
3215 * Hides a Cocoa window.
3217 void macdrv_hide_cocoa_window(macdrv_window w)
3219 WineWindow* window = (WineWindow*)w;
3222 [window doOrderOut];
3226 /***********************************************************************
3227 * macdrv_set_cocoa_window_frame
3229 * Move a Cocoa window.
3231 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3233 WineWindow* window = (WineWindow*)w;
3236 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3240 /***********************************************************************
3241 * macdrv_get_cocoa_window_frame
3243 * Gets the frame of a Cocoa window.
3245 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3247 WineWindow* window = (WineWindow*)w;
3252 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3253 [[WineApplicationController sharedController] flipRect:&frame];
3254 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3258 /***********************************************************************
3259 * macdrv_set_cocoa_parent_window
3261 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3262 * the parent window.
3264 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3266 WineWindow* window = (WineWindow*)w;
3269 [window setMacDrvParentWindow:(WineWindow*)parent];
3273 /***********************************************************************
3274 * macdrv_set_window_surface
3276 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3278 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3279 WineWindow* window = (WineWindow*)w;
3282 window.surface = surface;
3283 window.surface_mutex = mutex;
3289 /***********************************************************************
3290 * macdrv_window_needs_display
3292 * Mark a window as needing display in a specified rect (in non-client
3293 * area coordinates).
3295 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3297 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3298 WineWindow* window = (WineWindow*)w;
3300 OnMainThreadAsync(^{
3301 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3307 /***********************************************************************
3308 * macdrv_set_window_shape
3310 * Sets the shape of a Cocoa window from an array of rectangles. If
3311 * rects is NULL, resets the window's shape to its frame.
3313 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3315 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3316 WineWindow* window = (WineWindow*)w;
3319 if (!rects || !count)
3322 window.shapeData = nil;
3323 [window checkEmptyShaped];
3327 size_t length = sizeof(*rects) * count;
3328 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3333 path = [NSBezierPath bezierPath];
3334 for (i = 0; i < count; i++)
3335 [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3336 window.shape = path;
3337 window.shapeData = [NSData dataWithBytes:rects length:length];
3338 [window checkEmptyShaped];
3346 /***********************************************************************
3347 * macdrv_set_window_alpha
3349 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3351 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3352 WineWindow* window = (WineWindow*)w;
3354 [window setAlphaValue:alpha];
3359 /***********************************************************************
3360 * macdrv_set_window_color_key
3362 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3365 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3366 WineWindow* window = (WineWindow*)w;
3369 window.colorKeyed = TRUE;
3370 window.colorKeyRed = keyRed;
3371 window.colorKeyGreen = keyGreen;
3372 window.colorKeyBlue = keyBlue;
3373 [window checkTransparency];
3379 /***********************************************************************
3380 * macdrv_clear_window_color_key
3382 void macdrv_clear_window_color_key(macdrv_window w)
3384 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3385 WineWindow* window = (WineWindow*)w;
3388 window.colorKeyed = FALSE;
3389 [window checkTransparency];
3395 /***********************************************************************
3396 * macdrv_window_use_per_pixel_alpha
3398 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3400 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3401 WineWindow* window = (WineWindow*)w;
3404 window.usePerPixelAlpha = use_per_pixel_alpha;
3405 [window checkTransparency];
3411 /***********************************************************************
3412 * macdrv_give_cocoa_window_focus
3414 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3415 * orders it front and, if its frame was not within the desktop bounds,
3416 * Cocoa will typically move it on-screen.
3418 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3420 WineWindow* window = (WineWindow*)w;
3423 [window makeFocused:activate];
3427 /***********************************************************************
3428 * macdrv_set_window_min_max_sizes
3430 * Sets the window's minimum and maximum content sizes.
3432 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3434 WineWindow* window = (WineWindow*)w;
3437 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3441 /***********************************************************************
3442 * macdrv_create_view
3444 * Creates and returns a view with the specified frame rect. The
3445 * caller is responsible for calling macdrv_dispose_view() on the view
3446 * when it is done with it.
3448 macdrv_view macdrv_create_view(CGRect rect)
3450 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3451 __block WineContentView* view;
3453 if (CGRectIsNull(rect)) rect = CGRectZero;
3456 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3458 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3459 [view setAutoresizesSubviews:NO];
3460 [view setHidden:YES];
3461 [nc addObserver:view
3462 selector:@selector(updateGLContexts)
3463 name:NSViewGlobalFrameDidChangeNotification
3465 [nc addObserver:view
3466 selector:@selector(updateGLContexts)
3467 name:NSApplicationDidChangeScreenParametersNotification
3472 return (macdrv_view)view;
3475 /***********************************************************************
3476 * macdrv_dispose_view
3478 * Destroys a view previously returned by macdrv_create_view.
3480 void macdrv_dispose_view(macdrv_view v)
3482 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3483 WineContentView* view = (WineContentView*)v;
3486 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3487 WineWindow* window = (WineWindow*)[view window];
3489 [nc removeObserver:view
3490 name:NSViewGlobalFrameDidChangeNotification
3492 [nc removeObserver:view
3493 name:NSApplicationDidChangeScreenParametersNotification
3495 [view removeFromSuperview];
3497 [window updateForGLSubviews];
3503 /***********************************************************************
3504 * macdrv_set_view_frame
3506 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3508 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3509 WineContentView* view = (WineContentView*)v;
3511 if (CGRectIsNull(rect)) rect = CGRectZero;
3513 OnMainThreadAsync(^{
3514 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3515 NSRect oldFrame = [view frame];
3517 if (!NSEqualRects(oldFrame, newFrame))
3519 [[view superview] setNeedsDisplayInRect:oldFrame];
3520 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3521 [view setFrameSize:newFrame.size];
3522 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3523 [view setFrameOrigin:newFrame.origin];
3525 [view setFrame:newFrame];
3526 [view setNeedsDisplay:YES];
3530 int backing_size[2] = { 0 };
3531 [view wine_setBackingSize:backing_size];
3533 [(WineWindow*)[view window] updateForGLSubviews];
3540 /***********************************************************************
3541 * macdrv_set_view_superview
3543 * Move a view to a new superview and position it relative to its
3544 * siblings. If p is non-NULL, the view is ordered behind it.
3545 * Otherwise, the view is ordered above n. If s is NULL, use the
3546 * content view of w as the new superview.
3548 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3550 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3551 WineContentView* view = (WineContentView*)v;
3552 WineContentView* superview = (WineContentView*)s;
3553 WineWindow* window = (WineWindow*)w;
3554 WineContentView* prev = (WineContentView*)p;
3555 WineContentView* next = (WineContentView*)n;
3558 superview = [window contentView];
3560 OnMainThreadAsync(^{
3561 if (superview == [view superview])
3563 NSArray* subviews = [superview subviews];
3564 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3565 if (!prev && !next && index == [subviews count] - 1)
3567 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3569 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3573 WineWindow* oldWindow = (WineWindow*)[view window];
3574 WineWindow* newWindow = (WineWindow*)[superview window];
3576 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3577 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3578 [view removeFromSuperview];
3581 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3583 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3585 if (oldWindow != newWindow)
3587 [oldWindow updateForGLSubviews];
3588 [newWindow updateForGLSubviews];
3595 /***********************************************************************
3596 * macdrv_set_view_hidden
3598 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3600 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3601 WineContentView* view = (WineContentView*)v;
3603 OnMainThreadAsync(^{
3604 [view setHidden:hidden];
3605 [(WineWindow*)view.window updateForGLSubviews];
3611 /***********************************************************************
3612 * macdrv_add_view_opengl_context
3614 * Add an OpenGL context to the list being tracked for each view.
3616 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3618 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3619 WineContentView* view = (WineContentView*)v;
3620 WineOpenGLContext *context = (WineOpenGLContext*)c;
3623 [view addGLContext:context];
3629 /***********************************************************************
3630 * macdrv_remove_view_opengl_context
3632 * Add an OpenGL context to the list being tracked for each view.
3634 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3636 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3637 WineContentView* view = (WineContentView*)v;
3638 WineOpenGLContext *context = (WineOpenGLContext*)c;
3640 OnMainThreadAsync(^{
3641 [view removeGLContext:context];
3647 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3649 WineContentView* view = (WineContentView*)v;
3651 if (![view isKindOfClass:[WineContentView class]])
3654 [view wine_getBackingSize:backing_size];
3658 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3660 WineContentView* view = (WineContentView*)v;
3662 if ([view isKindOfClass:[WineContentView class]])
3663 [view wine_setBackingSize:backing_size];
3666 /***********************************************************************
3667 * macdrv_window_background_color
3669 * Returns the standard Mac window background color as a 32-bit value of
3670 * the form 0x00rrggbb.
3672 uint32_t macdrv_window_background_color(void)
3674 static uint32_t result;
3675 static dispatch_once_t once;
3677 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3678 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3679 // of it is to draw with it.
3680 dispatch_once(&once, ^{
3682 unsigned char rgbx[4];
3683 unsigned char *planes = rgbx;
3684 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3691 colorSpaceName:NSCalibratedRGBColorSpace
3695 [NSGraphicsContext saveGraphicsState];
3696 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3697 [[NSColor windowBackgroundColor] set];
3698 NSRectFill(NSMakeRect(0, 0, 1, 1));
3699 [NSGraphicsContext restoreGraphicsState];
3701 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3708 /***********************************************************************
3709 * macdrv_send_text_input_event
3711 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3713 OnMainThreadAsync(^{
3715 macdrv_event* event;
3716 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3717 if (![window isKindOfClass:[WineWindow class]])
3719 window = (WineWindow*)[NSApp mainWindow];
3720 if (![window isKindOfClass:[WineWindow class]])
3721 window = [[WineApplicationController sharedController] frontWineWindow];
3726 NSUInteger localFlags = flags;
3730 window.imeData = data;
3731 fix_device_modifiers_by_generic(&localFlags);
3733 // An NSEvent created with +keyEventWithType:... is internally marked
3734 // as synthetic and doesn't get sent through input methods. But one
3735 // created from a CGEvent doesn't have that problem.
3736 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3737 CGEventSetFlags(c, localFlags);
3738 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3739 event = [NSEvent eventWithCGEvent:c];
3742 window.commandDone = FALSE;
3743 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3748 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3749 event->sent_text_input.handled = ret;
3750 event->sent_text_input.done = done;
3751 [[window queue] postEvent:event];
3752 macdrv_release_event(event);