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 _everHadGLContext;
300 BOOL _cachedHasGLDescendant;
301 BOOL _cachedHasGLDescendantValid;
302 BOOL clearedGlSurface;
304 NSMutableAttributedString* markedText;
305 NSRange markedTextSelection;
310 @property (readonly, nonatomic) BOOL everHadGLContext;
312 - (void) addGLContext:(WineOpenGLContext*)context;
313 - (void) removeGLContext:(WineOpenGLContext*)context;
314 - (void) updateGLContexts;
316 - (void) wine_getBackingSize:(int*)outBackingSize;
317 - (void) wine_setBackingSize:(const int*)newBackingSize;
322 @interface WineWindow ()
324 @property (readwrite, nonatomic) BOOL disabled;
325 @property (readwrite, nonatomic) BOOL noActivate;
326 @property (readwrite, nonatomic) BOOL floating;
327 @property (readwrite, nonatomic) BOOL drawnSinceShown;
328 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
329 @property (retain, nonatomic) NSWindow* latentParentWindow;
331 @property (nonatomic) void* hwnd;
332 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
334 @property (nonatomic) void* surface;
335 @property (nonatomic) pthread_mutex_t* surface_mutex;
337 @property (copy, nonatomic) NSBezierPath* shape;
338 @property (copy, nonatomic) NSData* shapeData;
339 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
340 @property (readonly, nonatomic) BOOL needsTransparency;
342 @property (nonatomic) BOOL colorKeyed;
343 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
344 @property (nonatomic) BOOL usePerPixelAlpha;
346 @property (assign, nonatomic) void* imeData;
347 @property (nonatomic) BOOL commandDone;
349 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
351 - (void) updateColorSpace;
352 - (void) updateForGLSubviews;
354 - (BOOL) becameEligibleParentOrChild;
355 - (void) becameIneligibleChild;
357 - (void) windowDidDrawContent;
362 @implementation WineContentView
364 @synthesize everHadGLContext = _everHadGLContext;
368 [markedText release];
369 [glContexts release];
370 [pendingGlContexts release];
379 - (void) drawRect:(NSRect)rect
381 WineWindow* window = (WineWindow*)[self window];
383 for (WineOpenGLContext* context in pendingGlContexts)
385 if (!clearedGlSurface)
387 context.shouldClearToBlack = TRUE;
388 clearedGlSurface = TRUE;
390 context.needsUpdate = TRUE;
392 [glContexts addObjectsFromArray:pendingGlContexts];
393 [pendingGlContexts removeAllObjects];
395 if ([window contentView] != self)
398 if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
400 [[NSColor clearColor] setFill];
403 [window.shape addClip];
405 [[NSColor windowBackgroundColor] setFill];
409 if (window.surface && window.surface_mutex &&
410 !pthread_mutex_lock(window.surface_mutex))
415 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
417 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
418 CGContextRef context;
421 [window.shape addClip];
423 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
424 CGContextSetBlendMode(context, kCGBlendModeCopy);
425 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
427 for (i = 0; i < count; i++)
432 imageRect = CGRectIntersection(rects[i], dirtyRect);
433 image = create_surface_image(window.surface, &imageRect, FALSE);
437 if (window.colorKeyed)
439 CGImageRef maskedImage;
440 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
441 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
442 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
443 maskedImage = CGImageCreateWithMaskingColors(image, components);
446 CGImageRelease(image);
451 CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
453 CGImageRelease(image);
457 [window windowDidDrawContent];
460 pthread_mutex_unlock(window.surface_mutex);
463 // If the window may be transparent, then we have to invalidate the
464 // shadow every time we draw. Also, if this is the first time we've
465 // drawn since changing from transparent to opaque.
466 if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
468 window.shapeChangedSinceLastDraw = FALSE;
469 [window invalidateShadow];
473 - (void) addGLContext:(WineOpenGLContext*)context
475 BOOL hadContext = _everHadGLContext;
477 glContexts = [[NSMutableArray alloc] init];
478 if (!pendingGlContexts)
479 pendingGlContexts = [[NSMutableArray alloc] init];
481 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
483 [glContexts addObject:context];
484 if (!clearedGlSurface)
486 context.shouldClearToBlack = TRUE;
487 clearedGlSurface = TRUE;
489 context.needsUpdate = TRUE;
493 [pendingGlContexts addObject:context];
494 [self setNeedsDisplay:YES];
497 _everHadGLContext = YES;
499 [self invalidateHasGLDescendant];
500 [(WineWindow*)[self window] updateForGLSubviews];
503 - (void) removeGLContext:(WineOpenGLContext*)context
505 [glContexts removeObjectIdenticalTo:context];
506 [pendingGlContexts removeObjectIdenticalTo:context];
507 [(WineWindow*)[self window] updateForGLSubviews];
510 - (void) updateGLContexts:(BOOL)reattach
512 for (WineOpenGLContext* context in glContexts)
514 context.needsUpdate = TRUE;
516 context.needsReattach = TRUE;
520 - (void) updateGLContexts
522 [self updateGLContexts:NO];
525 - (BOOL) _hasGLDescendant
529 if (_everHadGLContext)
531 for (WineContentView* view in [self subviews])
533 if ([view hasGLDescendant])
539 - (BOOL) hasGLDescendant
541 if (!_cachedHasGLDescendantValid)
543 _cachedHasGLDescendant = [self _hasGLDescendant];
544 _cachedHasGLDescendantValid = YES;
546 return _cachedHasGLDescendant;
549 - (void) invalidateHasGLDescendant
551 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
552 _cachedHasGLDescendantValid = NO;
553 if (invalidateAncestors && self != [[self window] contentView])
555 WineContentView* superview = (WineContentView*)[self superview];
556 if ([superview isKindOfClass:[WineContentView class]])
557 [superview invalidateHasGLDescendant];
561 - (void) wine_getBackingSize:(int*)outBackingSize
563 @synchronized(self) {
564 memcpy(outBackingSize, backingSize, sizeof(backingSize));
567 - (void) wine_setBackingSize:(const int*)newBackingSize
569 @synchronized(self) {
570 memcpy(backingSize, newBackingSize, sizeof(backingSize));
574 - (void) setRetinaMode:(int)mode
576 double scale = mode ? 0.5 : 2.0;
577 NSRect frame = self.frame;
578 frame.origin.x *= scale;
579 frame.origin.y *= scale;
580 frame.size.width *= scale;
581 frame.size.height *= scale;
582 [self setFrame:frame];
583 [self updateGLContexts];
585 for (WineContentView* subview in [self subviews])
587 if ([subview isKindOfClass:[WineContentView class]])
588 [subview setRetinaMode:mode];
592 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
597 - (BOOL) preservesContentDuringLiveResize
599 // Returning YES from this tells Cocoa to keep our view's content during
600 // a Cocoa-driven resize. In theory, we're also supposed to override
601 // -setFrameSize: to mark exposed sections as needing redisplay, but
602 // user32 will take care of that in a roundabout way. This way, we don't
603 // redraw until the window surface is flushed.
605 // This doesn't do anything when we resize the window ourselves.
609 - (BOOL)acceptsFirstResponder
611 return [[self window] contentView] == self;
614 - (BOOL) mouseDownCanMoveWindow
622 [self invalidateHasGLDescendant];
625 - (void) viewDidUnhide
627 [super viewDidUnhide];
628 [self updateGLContexts:YES];
629 [self invalidateHasGLDescendant];
632 - (void) completeText:(NSString*)text
635 WineWindow* window = (WineWindow*)[self window];
637 event = macdrv_create_event(IM_SET_TEXT, window);
638 event->im_set_text.data = [window imeData];
639 event->im_set_text.text = (CFStringRef)[text copy];
640 event->im_set_text.complete = TRUE;
642 [[window queue] postEvent:event];
644 macdrv_release_event(event);
646 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
647 markedTextSelection = NSMakeRange(0, 0);
648 [[self inputContext] discardMarkedText];
651 - (NSFocusRingType) focusRingType
653 return NSFocusRingTypeNone;
656 - (void) didAddSubview:(NSView*)subview
658 if ([subview isKindOfClass:[WineContentView class]])
660 WineContentView* view = (WineContentView*)subview;
661 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
662 [self invalidateHasGLDescendant];
664 [super didAddSubview:subview];
667 - (void) willRemoveSubview:(NSView*)subview
669 if ([subview isKindOfClass:[WineContentView class]])
671 WineContentView* view = (WineContentView*)subview;
672 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
673 [self invalidateHasGLDescendant];
675 [super willRemoveSubview:subview];
679 * ---------- NSTextInputClient methods ----------
681 - (NSTextInputContext*) inputContext
684 markedText = [[NSMutableAttributedString alloc] init];
685 return [super inputContext];
688 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
690 if ([string isKindOfClass:[NSAttributedString class]])
691 string = [string string];
693 if ([string isKindOfClass:[NSString class]])
694 [self completeText:string];
697 - (void) doCommandBySelector:(SEL)aSelector
699 [(WineWindow*)[self window] setCommandDone:TRUE];
702 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
704 if ([string isKindOfClass:[NSAttributedString class]])
705 string = [string string];
707 if ([string isKindOfClass:[NSString class]])
710 WineWindow* window = (WineWindow*)[self window];
712 if (replacementRange.location == NSNotFound)
713 replacementRange = NSMakeRange(0, [markedText length]);
715 [markedText replaceCharactersInRange:replacementRange withString:string];
716 markedTextSelection = selectedRange;
717 markedTextSelection.location += replacementRange.location;
719 event = macdrv_create_event(IM_SET_TEXT, window);
720 event->im_set_text.data = [window imeData];
721 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
722 event->im_set_text.complete = FALSE;
723 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
725 [[window queue] postEvent:event];
727 macdrv_release_event(event);
729 [[self inputContext] invalidateCharacterCoordinates];
735 [self completeText:nil];
738 - (NSRange) selectedRange
740 return markedTextSelection;
743 - (NSRange) markedRange
745 NSRange range = NSMakeRange(0, [markedText length]);
747 range.location = NSNotFound;
751 - (BOOL) hasMarkedText
753 return [markedText length] > 0;
756 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
758 if (aRange.location >= [markedText length])
761 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
763 *actualRange = aRange;
764 return [markedText attributedSubstringFromRange:aRange];
767 - (NSArray*) validAttributesForMarkedText
769 return [NSArray array];
772 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
775 WineWindow* window = (WineWindow*)[self window];
778 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
780 query = macdrv_create_query();
781 query->type = QUERY_IME_CHAR_RECT;
782 query->window = (macdrv_window)[window retain];
783 query->ime_char_rect.data = [window imeData];
784 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
786 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
788 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
789 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
790 [[WineApplicationController sharedController] flipRect:&ret];
793 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
795 macdrv_release_query(query);
798 *actualRange = aRange;
802 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
807 - (NSInteger) windowLevel
809 return [[self window] level];
815 @implementation WineWindow
817 static WineWindow* causing_becomeKeyWindow;
819 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
820 @synthesize drawnSinceShown;
821 @synthesize surface, surface_mutex;
822 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
823 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
824 @synthesize usePerPixelAlpha;
825 @synthesize imeData, commandDone;
827 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
828 windowFrame:(NSRect)window_frame
830 queue:(WineEventQueue*)queue
833 WineContentView* contentView;
834 NSTrackingArea* trackingArea;
835 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
837 [[WineApplicationController sharedController] flipRect:&window_frame];
839 window = [[[self alloc] initWithContentRect:window_frame
840 styleMask:style_mask_for_features(wf)
841 backing:NSBackingStoreBuffered
842 defer:YES] autorelease];
844 if (!window) return nil;
846 /* Standardize windows to eliminate differences between titled and
847 borderless windows and between NSWindow and NSPanel. */
848 [window setHidesOnDeactivate:NO];
849 [window setReleasedWhenClosed:NO];
851 [window setOneShot:YES];
852 [window disableCursorRects];
853 [window setShowsResizeIndicator:NO];
854 [window setHasShadow:wf->shadow];
855 [window setAcceptsMouseMovedEvents:YES];
856 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
857 [window setDelegate:window];
858 [window setBackgroundColor:[NSColor clearColor]];
859 [window setOpaque:NO];
861 window.queue = queue;
862 window->savedContentMinSize = NSZeroSize;
863 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
864 window->resizable = wf->resizable;
865 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
867 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
868 (NSString*)kUTTypeContent,
871 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
874 [contentView setAutoresizesSubviews:NO];
876 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
877 because they give us mouse moves in the background. */
878 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
879 options:(NSTrackingMouseMoved |
880 NSTrackingActiveAlways |
881 NSTrackingInVisibleRect)
883 userInfo:nil] autorelease];
886 [contentView addTrackingArea:trackingArea];
888 [window setContentView:contentView];
889 [window setInitialFirstResponder:contentView];
891 [nc addObserver:window
892 selector:@selector(updateFullscreen)
893 name:NSApplicationDidChangeScreenParametersNotification
895 [window updateFullscreen];
897 [nc addObserver:window
898 selector:@selector(applicationWillHide)
899 name:NSApplicationWillHideNotification
901 [nc addObserver:window
902 selector:@selector(applicationDidUnhide)
903 name:NSApplicationDidUnhideNotification
906 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
907 selector:@selector(checkWineDisplayLink)
908 name:NSWorkspaceActiveSpaceDidChangeNotification
909 object:[NSWorkspace sharedWorkspace]];
911 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
918 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
919 [[NSNotificationCenter defaultCenter] removeObserver:self];
921 [latentChildWindows release];
922 [latentParentWindow release];
928 - (BOOL) preventResizing
930 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
931 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
934 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
936 if (allow_immovable_windows && (disabled || inMaximized))
938 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
944 - (void) adjustFeaturesForState
946 NSUInteger style = [self styleMask];
948 if (style & NSClosableWindowMask)
949 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
950 if (style & NSMiniaturizableWindowMask)
951 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
952 if (style & NSResizableWindowMask)
953 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
954 if ([self respondsToSelector:@selector(toggleFullScreen:)])
956 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
957 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
960 if ([self preventResizing])
962 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
963 [self setContentMinSize:size];
964 [self setContentMaxSize:size];
968 [self setContentMaxSize:savedContentMaxSize];
969 [self setContentMinSize:savedContentMinSize];
972 if (allow_immovable_windows || cursor_clipping_locks_windows)
973 [self setMovable:[self allowsMovingWithMaximized:maximized]];
976 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
978 if ([self respondsToSelector:@selector(toggleFullScreen:)])
980 NSUInteger style = [self styleMask];
982 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
983 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
984 !(self.parentWindow || self.latentParentWindow))
986 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
987 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
991 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
992 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
993 if (style & NSFullScreenWindowMask)
994 [super toggleFullScreen:nil];
998 if (behavior != [self collectionBehavior])
1000 [self setCollectionBehavior:behavior];
1001 [self adjustFeaturesForState];
1005 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1007 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1008 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1009 NSUInteger currentStyle = [self styleMask];
1010 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1012 if (newStyle != currentStyle)
1014 NSString* title = [[[self title] copy] autorelease];
1015 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1016 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1017 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1019 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1020 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1021 // just NSTitledWindowMask, the window buttons should disappear rather
1022 // than just being disabled. But they don't. Similarly in reverse.
1023 // The workaround is to also toggle NSClosableWindowMask at the same time.
1024 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1026 [self setStyleMask:newStyle];
1028 // -setStyleMask: resets the firstResponder to the window. Set it
1029 // back to the content view.
1030 if ([[self contentView] acceptsFirstResponder])
1031 [self makeFirstResponder:[self contentView]];
1033 [self adjustFullScreenBehavior:[self collectionBehavior]];
1035 if ([[self title] length] == 0 && [title length] > 0)
1036 [self setTitle:title];
1039 resizable = wf->resizable;
1040 [self adjustFeaturesForState];
1041 [self setHasShadow:wf->shadow];
1044 // Indicates if the window would be visible if the app were not hidden.
1045 - (BOOL) wouldBeVisible
1047 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1050 - (BOOL) isOrderedIn
1052 return [self wouldBeVisible] || [self isMiniaturized];
1055 - (NSInteger) minimumLevelForActive:(BOOL)active
1059 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1060 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1061 level = NSFloatingWindowLevel;
1063 level = NSNormalWindowLevel;
1069 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1071 if (captured || fullscreen)
1074 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1076 level = NSStatusWindowLevel + 1;
1086 - (void) postDidUnminimizeEvent
1088 macdrv_event* event;
1090 /* Coalesce events by discarding any previous ones still in the queue. */
1091 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1094 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1095 [queue postEvent:event];
1096 macdrv_release_event(event);
1099 - (void) sendResizeStartQuery
1101 macdrv_query* query = macdrv_create_query();
1102 query->type = QUERY_RESIZE_START;
1103 query->window = (macdrv_window)[self retain];
1105 [self.queue query:query timeout:0.3];
1106 macdrv_release_query(query);
1109 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1111 NSWindowCollectionBehavior behavior;
1113 self.disabled = state->disabled;
1114 self.noActivate = state->no_activate;
1116 if (self.floating != state->floating)
1118 self.floating = state->floating;
1119 if (state->floating)
1121 // Became floating. If child of non-floating window, make that
1122 // relationship latent.
1123 WineWindow* parent = (WineWindow*)[self parentWindow];
1124 if (parent && !parent.floating)
1125 [self becameIneligibleChild];
1129 // Became non-floating. If parent of floating children, make that
1130 // relationship latent.
1132 for (child in [self childWineWindows])
1135 [child becameIneligibleChild];
1139 // Check our latent relationships. If floating status was the only
1140 // reason they were latent, then make them active.
1141 if ([self isVisible])
1142 [self becameEligibleParentOrChild];
1144 [[WineApplicationController sharedController] adjustWindowLevels];
1147 if (state->minimized_valid)
1149 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1151 pendingMinimize = FALSE;
1152 if (state->minimized && ![self isMiniaturized])
1154 if ([self wouldBeVisible])
1156 if ([self styleMask] & NSFullScreenWindowMask)
1158 [self postDidUnminimizeEvent];
1159 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1163 [super miniaturize:nil];
1164 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1165 event_mask_for_type(WINDOW_GOT_FOCUS) |
1166 event_mask_for_type(WINDOW_LOST_FOCUS);
1170 pendingMinimize = TRUE;
1172 else if (!state->minimized && [self isMiniaturized])
1174 ignore_windowDeminiaturize = TRUE;
1175 [self deminiaturize:nil];
1176 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1180 [queue discardEventsMatchingMask:discard forWindow:self];
1183 if (state->maximized != maximized)
1185 maximized = state->maximized;
1186 [self adjustFeaturesForState];
1188 if (!maximized && [self inLiveResize])
1189 [self sendResizeStartQuery];
1192 behavior = NSWindowCollectionBehaviorDefault;
1193 if (state->excluded_by_expose)
1194 behavior |= NSWindowCollectionBehaviorTransient;
1196 behavior |= NSWindowCollectionBehaviorManaged;
1197 if (state->excluded_by_cycle)
1199 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1200 if ([self isOrderedIn])
1201 [NSApp removeWindowsItem:self];
1205 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1206 if ([self isOrderedIn])
1207 [NSApp addWindowsItem:self title:[self title] filename:NO];
1209 [self adjustFullScreenBehavior:behavior];
1212 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1214 BOOL reordered = FALSE;
1216 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1218 if ([self level] > [child level])
1219 [child setLevel:[self level]];
1220 if (![child isVisible])
1221 [child setAutodisplay:YES];
1222 [self addChildWindow:child ordered:NSWindowAbove];
1223 [child checkWineDisplayLink];
1224 [latentChildWindows removeObjectIdenticalTo:child];
1225 child.latentParentWindow = nil;
1230 if (!latentChildWindows)
1231 latentChildWindows = [[NSMutableArray alloc] init];
1232 if (![latentChildWindows containsObject:child])
1233 [latentChildWindows addObject:child];
1234 child.latentParentWindow = self;
1240 - (BOOL) addChildWineWindow:(WineWindow*)child
1242 return [self addChildWineWindow:child assumeVisible:FALSE];
1245 - (void) removeChildWineWindow:(WineWindow*)child
1247 [self removeChildWindow:child];
1248 if (child.latentParentWindow == self)
1249 child.latentParentWindow = nil;
1250 [latentChildWindows removeObjectIdenticalTo:child];
1253 - (void) setChildWineWindows:(NSArray*)childWindows
1255 NSArray* origChildren;
1256 NSUInteger count, start, limit, i;
1258 origChildren = self.childWineWindows;
1260 // If the current and desired children arrays match up to a point, leave
1261 // those matching children alone.
1262 count = childWindows.count;
1263 limit = MIN(origChildren.count, count);
1264 for (start = 0; start < limit; start++)
1266 if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1270 // Remove all of the child windows and re-add them back-to-front so they
1271 // are in the desired order.
1272 for (i = start; i < count; i++)
1274 WineWindow* child = [childWindows objectAtIndex:i];
1275 [self removeChildWindow:child];
1277 for (i = start; i < count; i++)
1279 WineWindow* child = [childWindows objectAtIndex:i];
1280 [self addChildWindow:child ordered:NSWindowAbove];
1284 static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1286 NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1287 NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1288 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1289 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1290 if (index1 == NSNotFound)
1292 if (index2 == NSNotFound)
1293 return NSOrderedSame;
1295 return NSOrderedAscending;
1297 else if (index2 == NSNotFound)
1298 return NSOrderedDescending;
1299 else if (index1 < index2)
1300 return NSOrderedDescending;
1301 else if (index2 < index1)
1302 return NSOrderedAscending;
1304 return NSOrderedSame;
1307 - (BOOL) becameEligibleParentOrChild
1309 BOOL reordered = FALSE;
1312 if (latentParentWindow.floating || !self.floating)
1314 // If we aren't visible currently, we assume that we should be and soon
1315 // will be. So, if the latent parent is visible that's enough to assume
1316 // we can establish the parent-child relationship in Cocoa. That will
1317 // actually make us visible, which is fine.
1318 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1322 // Here, though, we may not actually be visible yet and adding a child
1323 // won't make us visible. The caller will have to call this method
1324 // again after actually making us visible.
1325 if ([self isVisible] && (count = [latentChildWindows count]))
1327 NSMutableArray* windowNumbers;
1328 NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1329 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1332 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1334 for (i = 0; i < count; i++)
1336 WineWindow* child = [latentChildWindows objectAtIndex:i];
1337 if ([child isVisible] && (self.floating || !child.floating))
1339 if (child.latentParentWindow == self)
1341 if ([self level] > [child level])
1342 [child setLevel:[self level]];
1343 [childWindows addObject:child];
1344 child.latentParentWindow = nil;
1348 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1349 [indexesToRemove addIndex:i];
1353 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1355 [childWindows sortWithOptions:NSSortStable
1356 usingComparator:^NSComparisonResult(id obj1, id obj2){
1357 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1359 [self setChildWineWindows:childWindows];
1365 - (void) becameIneligibleChild
1367 WineWindow* parent = (WineWindow*)[self parentWindow];
1370 if (!parent->latentChildWindows)
1371 parent->latentChildWindows = [[NSMutableArray alloc] init];
1372 [parent->latentChildWindows insertObject:self atIndex:0];
1373 self.latentParentWindow = parent;
1374 [parent removeChildWindow:self];
1378 - (void) becameIneligibleParentOrChild
1380 NSArray* childWindows = [self childWineWindows];
1382 [self becameIneligibleChild];
1384 if ([childWindows count])
1388 for (child in childWindows)
1390 child.latentParentWindow = self;
1391 [self removeChildWindow:child];
1394 if (latentChildWindows)
1395 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1397 latentChildWindows = [childWindows mutableCopy];
1401 // Determine if, among Wine windows, this window is directly above or below
1402 // a given other Wine window with no other Wine window intervening.
1403 // Intervening non-Wine windows are ignored.
1404 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1406 NSNumber* windowNumber;
1407 NSNumber* otherWindowNumber;
1408 NSArray* windowNumbers;
1409 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1411 if (![self isVisible] || ![otherWindow isVisible])
1414 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1415 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1416 windowNumbers = [[self class] windowNumbersWithOptions:0];
1417 windowIndex = [windowNumbers indexOfObject:windowNumber];
1418 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1420 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1423 if (orderingMode == NSWindowAbove)
1425 lowIndex = windowIndex;
1426 highIndex = otherWindowIndex;
1428 else if (orderingMode == NSWindowBelow)
1430 lowIndex = otherWindowIndex;
1431 highIndex = windowIndex;
1436 if (highIndex <= lowIndex)
1439 for (i = lowIndex + 1; i < highIndex; i++)
1441 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1442 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1443 if ([interveningWindow isKindOfClass:[WineWindow class]])
1450 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1452 NSMutableArray* windowNumbers;
1453 NSNumber* childWindowNumber;
1454 NSUInteger otherIndex;
1455 NSArray* origChildren;
1456 NSMutableArray* children;
1458 // Get the z-order from the window server and modify it to reflect the
1459 // requested window ordering.
1460 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1461 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1462 [windowNumbers removeObject:childWindowNumber];
1465 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1466 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1468 else if (mode == NSWindowAbove)
1469 [windowNumbers insertObject:childWindowNumber atIndex:0];
1471 [windowNumbers addObject:childWindowNumber];
1473 // Get our child windows and sort them in the reverse of the desired
1474 // z-order (back-to-front).
1475 origChildren = [self childWineWindows];
1476 children = [[origChildren mutableCopy] autorelease];
1477 [children sortWithOptions:NSSortStable
1478 usingComparator:^NSComparisonResult(id obj1, id obj2){
1479 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1482 [self setChildWineWindows:children];
1485 // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1486 // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1487 // (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
1488 // if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1489 // otherwise, the two ancestors are each roots of disjoint window trees
1490 // (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
1491 // (non-nil, nil) self is a descendent of other and *ancestor is the direct child
1492 - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1494 NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1498 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1504 *ancestorOfOther = child;
1508 [otherAncestors addObject:parent];
1512 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1515 NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1516 if (index != NSNotFound)
1520 *ancestorOfOther = nil;
1522 *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1528 *ancestorOfOther = otherAncestors.lastObject;;
1531 /* Returns whether or not the window was ordered in, which depends on if
1532 its frame intersects any screen. */
1533 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1535 WineApplicationController* controller = [WineApplicationController sharedController];
1536 if (![self isMiniaturized])
1538 BOOL needAdjustWindowLevels = FALSE;
1543 [controller transformProcessToForeground];
1544 if ([NSApp isHidden])
1546 wasVisible = [self isVisible];
1549 [NSApp activateIgnoringOtherApps:YES];
1551 NSDisableScreenUpdates();
1553 if ([self becameEligibleParentOrChild])
1554 needAdjustWindowLevels = TRUE;
1558 WineWindow* other = [prev isVisible] ? prev : next;
1559 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1561 if (![self isOrdered:orderingMode relativeTo:other])
1563 WineWindow* ancestor;
1564 WineWindow* ancestorOfOther;
1566 [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1569 [self setAutodisplay:YES];
1570 if (ancestorOfOther)
1572 // This window level may not be right for this window based
1573 // on floating-ness, fullscreen-ness, etc. But we set it
1574 // temporarily to allow us to order the windows properly.
1575 // Then the levels get fixed by -adjustWindowLevels.
1576 if ([ancestor level] != [ancestorOfOther level])
1577 [ancestor setLevel:[ancestorOfOther level]];
1579 parent = (WineWindow*)ancestor.parentWindow;
1580 if ([parent isKindOfClass:[WineWindow class]])
1581 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1583 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1587 (parent = (WineWindow*)child.parentWindow);
1590 if ([parent isKindOfClass:[WineWindow class]])
1591 [parent order:-orderingMode childWindow:child relativeTo:nil];
1592 if (parent == ancestor)
1596 [self checkWineDisplayLink];
1597 needAdjustWindowLevels = TRUE;
1604 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1607 [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1610 // Again, temporarily set level to make sure we can order to
1612 next = [controller frontWineWindow];
1613 if (next && [self level] < [next level])
1614 [self setLevel:[next level]];
1615 [self setAutodisplay:YES];
1616 [self orderFront:nil];
1617 [self checkWineDisplayLink];
1618 needAdjustWindowLevels = TRUE;
1620 pendingOrderOut = FALSE;
1622 if ([self becameEligibleParentOrChild])
1623 needAdjustWindowLevels = TRUE;
1625 if (needAdjustWindowLevels)
1627 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1628 [controller updateFullscreenWindows];
1629 [controller adjustWindowLevels];
1632 if (pendingMinimize)
1634 [super miniaturize:nil];
1635 pendingMinimize = FALSE;
1638 NSEnableScreenUpdates();
1640 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1641 Generate a frame-changed event just in case. The back end will ignore
1642 it if nothing actually changed. */
1643 [self windowDidResize:nil];
1645 if (![self isExcludedFromWindowsMenu])
1646 [NSApp addWindowsItem:self title:[self title] filename:NO];
1652 WineApplicationController* controller = [WineApplicationController sharedController];
1653 BOOL wasVisible = [self isVisible];
1654 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1656 [self endWindowDragging];
1657 [controller windowWillOrderOut:self];
1659 if (enteringFullScreen || exitingFullScreen)
1661 pendingOrderOut = TRUE;
1662 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1663 event_mask_for_type(WINDOW_GOT_FOCUS) |
1664 event_mask_for_type(WINDOW_LOST_FOCUS) |
1665 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1666 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1667 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1672 pendingOrderOut = FALSE;
1674 if ([self isMiniaturized])
1675 pendingMinimize = TRUE;
1677 WineWindow* parent = (WineWindow*)self.parentWindow;
1678 if ([parent isKindOfClass:[WineWindow class]])
1679 [parent grabDockIconSnapshotFromWindow:self force:NO];
1681 [self becameIneligibleParentOrChild];
1682 if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1686 fakingClose = FALSE;
1689 [self orderOut:nil];
1690 [self checkWineDisplayLink];
1691 [self setBackgroundColor:[NSColor clearColor]];
1692 [self setOpaque:NO];
1693 drawnSinceShown = NO;
1694 savedVisibleState = FALSE;
1695 if (wasVisible && wasOnActiveSpace && fullscreen)
1696 [controller updateFullscreenWindows];
1697 [controller adjustWindowLevels];
1698 [NSApp removeWindowsItem:self];
1700 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1701 event_mask_for_type(WINDOW_GOT_FOCUS) |
1702 event_mask_for_type(WINDOW_LOST_FOCUS) |
1703 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1704 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1705 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1709 - (void) updateFullscreen
1711 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1712 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1714 if (nowFullscreen != fullscreen)
1716 WineApplicationController* controller = [WineApplicationController sharedController];
1718 fullscreen = nowFullscreen;
1719 if ([self isVisible] && [self isOnActiveSpace])
1720 [controller updateFullscreenWindows];
1722 [controller adjustWindowLevels];
1726 - (void) setFrameAndWineFrame:(NSRect)frame
1728 [self setFrame:frame display:YES];
1731 roundedWineFrame = self.frame;
1733 #if CGFLOAT_IS_DOUBLE
1734 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1735 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1736 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1737 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1738 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1739 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1740 roundedWineFrame = wineFrame;
1742 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1743 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1744 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1745 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1746 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1747 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1748 roundedWineFrame = wineFrame;
1752 - (void) setFrameFromWine:(NSRect)contentRect
1754 /* Origin is (left, top) in a top-down space. Need to convert it to
1755 (left, bottom) in a bottom-up space. */
1756 [[WineApplicationController sharedController] flipRect:&contentRect];
1758 /* The back end is establishing a new window size and position. It's
1759 not interested in any stale events regarding those that may be sitting
1761 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1764 if (!NSIsEmptyRect(contentRect))
1766 NSRect frame, oldFrame;
1768 oldFrame = self.wine_fractionalFrame;
1769 frame = [self frameRectForContentRect:contentRect];
1770 if (!NSEqualRects(frame, oldFrame))
1772 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1773 BOOL needEnableScreenUpdates = FALSE;
1775 if ([self preventResizing])
1777 // Allow the following calls to -setFrame:display: to work even
1778 // if they would violate the content size constraints. This
1779 // shouldn't be necessary since the content size constraints are
1780 // documented to not constrain that method, but it seems to be.
1781 [self setContentMinSize:NSZeroSize];
1782 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1785 if (equalSizes && [[self childWineWindows] count])
1787 // If we change the window frame such that the origin moves
1788 // but the size doesn't change, then Cocoa moves child
1789 // windows with the parent. We don't want that so we fake
1790 // a change of the size and then change it back.
1791 NSRect bogusFrame = frame;
1792 bogusFrame.size.width++;
1794 NSDisableScreenUpdates();
1795 needEnableScreenUpdates = TRUE;
1797 ignore_windowResize = TRUE;
1798 [self setFrame:bogusFrame display:NO];
1799 ignore_windowResize = FALSE;
1802 [self setFrameAndWineFrame:frame];
1803 if ([self preventResizing])
1805 [self setContentMinSize:contentRect.size];
1806 [self setContentMaxSize:contentRect.size];
1809 if (needEnableScreenUpdates)
1810 NSEnableScreenUpdates();
1813 [self updateColorSpace];
1815 if (!enteringFullScreen &&
1816 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1817 nonFullscreenFrame = frame;
1819 [self updateFullscreen];
1821 if ([self isOrderedIn])
1823 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1824 event. The back end will ignore it if nothing actually changed. */
1825 [self windowDidResize:nil];
1831 - (NSRect) wine_fractionalFrame
1833 NSRect frame = self.frame;
1834 if (NSEqualRects(frame, roundedWineFrame))
1839 - (void) setMacDrvParentWindow:(WineWindow*)parent
1841 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1842 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1844 [oldParent removeChildWineWindow:self];
1845 [latentParentWindow removeChildWineWindow:self];
1846 if ([parent addChildWineWindow:self])
1847 [[WineApplicationController sharedController] adjustWindowLevels];
1848 [self adjustFullScreenBehavior:[self collectionBehavior]];
1852 - (void) setDisabled:(BOOL)newValue
1854 if (disabled != newValue)
1856 disabled = newValue;
1857 [self adjustFeaturesForState];
1861 - (BOOL) needsTransparency
1863 return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1864 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1867 - (void) checkTransparency
1869 if (![self isOpaque] && !self.needsTransparency)
1871 self.shapeChangedSinceLastDraw = TRUE;
1872 [[self contentView] setNeedsDisplay:YES];
1873 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1874 [self setOpaque:YES];
1876 else if ([self isOpaque] && self.needsTransparency)
1878 self.shapeChangedSinceLastDraw = TRUE;
1879 [[self contentView] setNeedsDisplay:YES];
1880 [self setBackgroundColor:[NSColor clearColor]];
1881 [self setOpaque:NO];
1885 - (void) setShape:(NSBezierPath*)newShape
1887 if (shape == newShape) return;
1891 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1895 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1897 shape = [newShape copy];
1898 self.shapeChangedSinceLastDraw = TRUE;
1900 [self checkTransparency];
1903 - (void) makeFocused:(BOOL)activate
1907 [[WineApplicationController sharedController] transformProcessToForeground];
1908 [NSApp activateIgnoringOtherApps:YES];
1911 causing_becomeKeyWindow = self;
1912 [self makeKeyWindow];
1913 causing_becomeKeyWindow = nil;
1915 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1916 event_mask_for_type(WINDOW_LOST_FOCUS)
1920 - (void) postKey:(uint16_t)keyCode
1921 pressed:(BOOL)pressed
1922 modifiers:(NSUInteger)modifiers
1923 event:(NSEvent*)theEvent
1925 macdrv_event* event;
1927 WineApplicationController* controller = [WineApplicationController sharedController];
1929 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1930 event->key.keycode = keyCode;
1931 event->key.modifiers = modifiers;
1932 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1934 if ((cgevent = [theEvent CGEvent]))
1935 controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
1937 [queue postEvent:event];
1939 macdrv_release_event(event);
1941 [controller noteKey:keyCode pressed:pressed];
1944 - (void) postKeyEvent:(NSEvent *)theEvent
1946 [self flagsChanged:theEvent];
1947 [self postKey:[theEvent keyCode]
1948 pressed:[theEvent type] == NSKeyDown
1949 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1953 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1955 savedContentMinSize = minSize;
1956 savedContentMaxSize = maxSize;
1957 if (![self preventResizing])
1959 [self setContentMinSize:minSize];
1960 [self setContentMaxSize:maxSize];
1964 - (WineWindow*) ancestorWineWindow
1966 WineWindow* ancestor = self;
1969 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1970 if ([parent isKindOfClass:[WineWindow class]])
1978 - (void) postBroughtForwardEvent
1980 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1981 [queue postEvent:event];
1982 macdrv_release_event(event);
1985 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
1987 macdrv_event* event;
1988 NSUInteger style = self.styleMask;
1991 style |= NSFullScreenWindowMask;
1993 style &= ~NSFullScreenWindowMask;
1994 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
1995 [[WineApplicationController sharedController] flipRect:&frame];
1997 /* Coalesce events by discarding any previous ones still in the queue. */
1998 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2001 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2002 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2003 event->window_frame_changed.fullscreen = isFullscreen;
2004 event->window_frame_changed.in_resize = resizing;
2005 [queue postEvent:event];
2006 macdrv_release_event(event);
2009 - (void) updateForCursorClipping
2011 [self adjustFeaturesForState];
2014 - (void) endWindowDragging
2018 if (draggingPhase == 3)
2020 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2021 [queue postEvent:event];
2022 macdrv_release_event(event);
2026 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2030 - (NSMutableDictionary*) displayIDToDisplayLinkMap
2032 static NSMutableDictionary* displayIDToDisplayLinkMap;
2033 if (!displayIDToDisplayLinkMap)
2035 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2037 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2040 usingBlock:^(NSNotification *note){
2041 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2042 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2043 [badDisplayIDs minusSet:validDisplayIDs];
2044 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2047 return displayIDToDisplayLinkMap;
2050 - (WineDisplayLink*) wineDisplayLink
2052 if (!_lastDisplayID)
2055 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2056 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2059 - (void) checkWineDisplayLink
2061 NSScreen* screen = self.screen;
2062 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2064 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2065 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2069 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2070 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2071 if (displayID == _lastDisplayID)
2074 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2078 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2079 [link removeWindow:self];
2083 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2086 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2087 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2089 [link addWindow:self];
2090 [self displayIfNeeded];
2092 _lastDisplayID = displayID;
2095 - (BOOL) isEmptyShaped
2097 return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2100 - (BOOL) canProvideSnapshot
2102 return (self.windowNumber > 0 && ![self isEmptyShaped]);
2105 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2107 if (![self isEmptyShaped])
2110 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2111 if (!force && now < lastDockIconSnapshot + 1)
2116 if (![window canProvideSnapshot])
2122 for (WineWindow* childWindow in self.childWindows)
2124 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2127 NSSize size = childWindow.frame.size;
2128 CGFloat area = size.width * size.height;
2129 if (!window || area > bestArea)
2131 window = childWindow;
2140 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2141 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2142 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2143 CFRelease(windowIDs);
2147 NSImage* appImage = [NSApp applicationIconImage];
2149 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2151 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2152 [dockIcon lockFocus];
2154 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2156 CGRect rect = CGRectMake(8, 8, 240, 240);
2157 size_t width = CGImageGetWidth(windowImage);
2158 size_t height = CGImageGetHeight(windowImage);
2161 rect.size.height *= height / (double)width;
2162 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2164 else if (width != height)
2166 rect.size.width *= width / (double)height;
2167 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2170 CGContextDrawImage(cgcontext, rect, windowImage);
2171 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2173 operation:NSCompositeSourceOver
2178 [dockIcon unlockFocus];
2180 CGImageRelease(windowImage);
2182 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2183 if (![imageView isKindOfClass:[NSImageView class]])
2185 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2186 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2187 self.dockTile.contentView = imageView;
2189 imageView.image = dockIcon;
2190 [self.dockTile display];
2191 lastDockIconSnapshot = now;
2194 - (void) checkEmptyShaped
2196 if (self.dockTile.contentView && ![self isEmptyShaped])
2198 self.dockTile.contentView = nil;
2199 lastDockIconSnapshot = 0;
2201 [self checkWineDisplayLink];
2206 * ---------- NSWindow method overrides ----------
2208 - (BOOL) canBecomeKeyWindow
2210 if (causing_becomeKeyWindow == self) return YES;
2211 if (self.disabled || self.noActivate) return NO;
2212 return [self isKeyWindow];
2215 - (BOOL) canBecomeMainWindow
2217 return [self canBecomeKeyWindow];
2220 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2222 // If a window is sized to completely cover a screen, then it's in
2223 // full-screen mode. In that case, we don't allow NSWindow to constrain
2225 NSArray* screens = [NSScreen screens];
2226 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2227 if (!screen_covered_by_rect(contentRect, screens) &&
2228 frame_intersects_screens(frameRect, screens))
2229 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2233 // This private method of NSWindow is called as Cocoa reacts to the display
2234 // configuration changing. Among other things, it adjusts the window's
2235 // frame based on how the screen(s) changed size. That tells Wine that the
2236 // window has been moved. We don't want that. Rather, we want to make
2237 // sure that the WinAPI notion of the window position is maintained/
2238 // restored, possibly undoing or overriding Cocoa's adjustment.
2240 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2241 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2242 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2243 // reassert its notion of the position. That call won't get processed
2244 // until after this method returns, so it will override whatever this
2245 // method does to the window position. It will also discard any pending
2246 // WINDOW_FRAME_CHANGED events.
2248 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2249 // adjust the window's position due to a display change is to hook into
2250 // this private method. This private method has remained stable from 10.6
2251 // through 10.11. If it does change, the most likely thing is that it
2252 // will be removed and no longer called and this fix will simply stop
2253 // working. The only real danger would be if Apple changed the return type
2254 // to a struct or floating-point type, which would change the calling
2256 - (id) _displayChanged
2258 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2259 [queue postEvent:event];
2260 macdrv_release_event(event);
2262 return [super _displayChanged];
2265 - (BOOL) isExcludedFromWindowsMenu
2267 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2270 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2272 BOOL ret = [super validateMenuItem:menuItem];
2274 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2275 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2276 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2282 /* We don't call this. It's the action method of the items in the Window menu. */
2283 - (void) makeKeyAndOrderFront:(id)sender
2285 if ([self isMiniaturized])
2286 [self deminiaturize:nil];
2287 [self orderBelow:nil orAbove:nil activate:NO];
2288 [[self ancestorWineWindow] postBroughtForwardEvent];
2290 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2291 [[WineApplicationController sharedController] windowGotFocus:self];
2294 - (void) sendEvent:(NSEvent*)event
2296 NSEventType type = event.type;
2298 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2299 interface control. For example, Control-Tab switches focus among
2300 views. We want to bypass that feature, so directly route key-down
2301 events to -keyDown:. */
2302 if (type == NSKeyDown)
2303 [[self firstResponder] keyDown:event];
2306 if (!draggingPhase && maximized && ![self isMovable] &&
2307 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2308 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2310 NSRect titleBar = self.frame;
2311 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2312 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2313 titleBar.origin.y = NSMaxY(contentRect);
2315 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2317 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2319 static const NSWindowButton buttons[] = {
2320 NSWindowCloseButton,
2321 NSWindowMiniaturizeButton,
2323 NSWindowFullScreenButton,
2325 BOOL hitButton = NO;
2328 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2332 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2335 button = [self standardWindowButton:buttons[i]];
2336 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2346 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2347 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2351 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2353 if ([self isMovable])
2355 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2356 NSPoint newTopLeft = dragWindowStartPosition;
2358 newTopLeft.x += point.x - dragStartPosition.x;
2359 newTopLeft.y += point.y - dragStartPosition.y;
2361 if (draggingPhase == 2)
2363 macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2364 mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2365 [queue postEvent:mevent];
2366 macdrv_release_event(mevent);
2371 [self setFrameTopLeftPoint:newTopLeft];
2373 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2375 macdrv_event* event;
2376 NSRect frame = [self contentRectForFrameRect:self.frame];
2378 [[WineApplicationController sharedController] flipRect:&frame];
2380 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2381 event->window_restore_requested.keep_frame = TRUE;
2382 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2383 [queue postEvent:event];
2384 macdrv_release_event(event);
2389 if (type == NSLeftMouseUp)
2390 [self endWindowDragging];
2393 [super sendEvent:event];
2397 - (void) miniaturize:(id)sender
2399 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2400 [queue postEvent:event];
2401 macdrv_release_event(event);
2403 WineWindow* parent = (WineWindow*)self.parentWindow;
2404 if ([parent isKindOfClass:[WineWindow class]])
2405 [parent grabDockIconSnapshotFromWindow:self force:YES];
2408 - (void) toggleFullScreen:(id)sender
2410 if (!self.disabled && !maximized)
2411 [super toggleFullScreen:sender];
2414 - (void) setViewsNeedDisplay:(BOOL)value
2416 if (value && ![self viewsNeedDisplay])
2418 WineDisplayLink* link = [self wineDisplayLink];
2421 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2422 if (_lastDisplayTime + [link refreshPeriod] < now)
2423 [self setAutodisplay:YES];
2427 _lastDisplayTime = now;
2431 [super setViewsNeedDisplay:value];
2436 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2438 [self setAutodisplay:NO];
2441 - (void) displayIfNeeded
2443 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2444 [super displayIfNeeded];
2445 [self setAutodisplay:NO];
2448 - (void) windowDidDrawContent
2450 if (!drawnSinceShown)
2452 drawnSinceShown = YES;
2453 dispatch_async(dispatch_get_main_queue(), ^{
2454 [self checkTransparency];
2459 - (NSArray*) childWineWindows
2461 NSArray* childWindows = self.childWindows;
2462 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2463 return [child isKindOfClass:[WineWindow class]];
2465 return [childWindows objectsAtIndexes:indexes];
2468 // We normally use the generic/calibrated RGB color space for the window,
2469 // rather than the device color space, to avoid expensive color conversion
2470 // which slows down drawing. However, for windows displaying OpenGL, having
2471 // a different color space than the screen greatly reduces frame rates, often
2472 // limiting it to the display refresh rate.
2474 // To avoid this, we switch back to the screen color space whenever the
2475 // window is covered by a view with an attached OpenGL context.
2476 - (void) updateColorSpace
2478 NSRect contentRect = [[self contentView] frame];
2479 BOOL coveredByGLView = FALSE;
2480 WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2481 if ([view isKindOfClass:[WineContentView class]] && view.everHadGLContext)
2483 NSRect frame = [view convertRect:[view bounds] toView:nil];
2484 if (NSContainsRect(frame, contentRect))
2485 coveredByGLView = TRUE;
2488 if (coveredByGLView)
2489 [self setColorSpace:nil];
2491 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2494 - (void) updateForGLSubviews
2496 [self updateColorSpace];
2497 if (gl_surface_mode == GL_SURFACE_BEHIND)
2498 [self checkTransparency];
2501 - (void) setRetinaMode:(int)mode
2504 double scale = mode ? 0.5 : 2.0;
2505 NSAffineTransform* transform = [NSAffineTransform transform];
2507 [transform scaleBy:scale];
2510 [shape transformUsingAffineTransform:transform];
2512 for (WineContentView* subview in [self.contentView subviews])
2514 if ([subview isKindOfClass:[WineContentView class]])
2515 [subview setRetinaMode:mode];
2518 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2519 frame.origin.x *= scale;
2520 frame.origin.y *= scale;
2521 frame.size.width *= scale;
2522 frame.size.height *= scale;
2523 frame = [self frameRectForContentRect:frame];
2525 savedContentMinSize = [transform transformSize:savedContentMinSize];
2526 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2527 savedContentMaxSize.width *= scale;
2528 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2529 savedContentMaxSize.height *= scale;
2531 self.contentMinSize = [transform transformSize:self.contentMinSize];
2532 NSSize temp = self.contentMaxSize;
2533 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2534 temp.width *= scale;
2535 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2536 temp.height *= scale;
2537 self.contentMaxSize = temp;
2539 ignore_windowResize = TRUE;
2540 [self setFrameAndWineFrame:frame];
2541 ignore_windowResize = FALSE;
2546 * ---------- NSResponder method overrides ----------
2548 - (void) keyDown:(NSEvent *)theEvent
2550 if ([theEvent isARepeat])
2552 if (!allowKeyRepeats)
2556 allowKeyRepeats = YES;
2558 [self postKeyEvent:theEvent];
2561 - (void) flagsChanged:(NSEvent *)theEvent
2563 static const struct {
2567 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2568 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2569 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2570 { NX_DEVICELCTLKEYMASK, kVK_Control },
2571 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2572 { NX_DEVICELALTKEYMASK, kVK_Option },
2573 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2574 { NX_DEVICELCMDKEYMASK, kVK_Command },
2575 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2578 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2580 int i, last_changed;
2582 fix_device_modifiers_by_generic(&modifierFlags);
2583 changed = modifierFlags ^ lastModifierFlags;
2586 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2587 if (changed & modifiers[i].mask)
2590 for (i = 0; i <= last_changed; i++)
2592 if (changed & modifiers[i].mask)
2594 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2597 allowKeyRepeats = NO;
2599 if (i == last_changed)
2600 lastModifierFlags = modifierFlags;
2603 lastModifierFlags ^= modifiers[i].mask;
2604 fix_generic_modifiers_by_device(&lastModifierFlags);
2607 // Caps lock generates one event for each press-release action.
2608 // We need to simulate a pair of events for each actual event.
2609 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2611 [self postKey:modifiers[i].keycode
2613 modifiers:lastModifierFlags
2614 event:(NSEvent*)theEvent];
2618 [self postKey:modifiers[i].keycode
2620 modifiers:lastModifierFlags
2621 event:(NSEvent*)theEvent];
2626 - (void) applicationWillHide
2628 savedVisibleState = [self isVisible];
2631 - (void) applicationDidUnhide
2633 if ([self isVisible])
2634 [self becameEligibleParentOrChild];
2639 * ---------- NSWindowDelegate methods ----------
2641 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2643 macdrv_query* query;
2646 query = macdrv_create_query();
2647 query->type = QUERY_MIN_MAX_INFO;
2648 query->window = (macdrv_window)[self retain];
2649 [self.queue query:query timeout:0.5];
2650 macdrv_release_query(query);
2652 size = [self contentMaxSize];
2653 if (proposedSize.width < size.width)
2654 size.width = proposedSize.width;
2655 if (proposedSize.height < size.height)
2656 size.height = proposedSize.height;
2660 - (void)windowDidBecomeKey:(NSNotification *)notification
2662 WineApplicationController* controller = [WineApplicationController sharedController];
2663 NSEvent* event = [controller lastFlagsChanged];
2665 [self flagsChanged:event];
2667 if (causing_becomeKeyWindow == self) return;
2669 [controller windowGotFocus:self];
2672 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2674 [self checkWineDisplayLink];
2677 - (void) windowDidChangeScreen:(NSNotification*)notification
2679 [self checkWineDisplayLink];
2682 - (void)windowDidDeminiaturize:(NSNotification *)notification
2684 WineApplicationController* controller = [WineApplicationController sharedController];
2686 if (!ignore_windowDeminiaturize)
2687 [self postDidUnminimizeEvent];
2688 ignore_windowDeminiaturize = FALSE;
2690 [self becameEligibleParentOrChild];
2692 if (fullscreen && [self isOnActiveSpace])
2693 [controller updateFullscreenWindows];
2694 [controller adjustWindowLevels];
2696 if (![self parentWindow])
2697 [self postBroughtForwardEvent];
2699 if (!self.disabled && !self.noActivate)
2701 causing_becomeKeyWindow = self;
2702 [self makeKeyWindow];
2703 causing_becomeKeyWindow = nil;
2704 [controller windowGotFocus:self];
2707 [self windowDidResize:notification];
2708 [self checkWineDisplayLink];
2711 - (void) windowDidEndLiveResize:(NSNotification *)notification
2715 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2716 [queue postEvent:event];
2717 macdrv_release_event(event);
2721 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2723 enteringFullScreen = FALSE;
2724 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2725 if (pendingOrderOut)
2729 - (void) windowDidExitFullScreen:(NSNotification*)notification
2731 exitingFullScreen = FALSE;
2732 [self setFrameAndWineFrame:nonFullscreenFrame];
2733 [self windowDidResize:nil];
2734 if (pendingOrderOut)
2738 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2740 enteringFullScreen = FALSE;
2741 enteredFullScreenTime = 0;
2742 if (pendingOrderOut)
2746 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2748 exitingFullScreen = FALSE;
2749 [self windowDidResize:nil];
2750 if (pendingOrderOut)
2754 - (void)windowDidMiniaturize:(NSNotification *)notification
2756 if (fullscreen && [self isOnActiveSpace])
2757 [[WineApplicationController sharedController] updateFullscreenWindows];
2758 [self checkWineDisplayLink];
2761 - (void)windowDidMove:(NSNotification *)notification
2763 [self windowDidResize:notification];
2766 - (void)windowDidResignKey:(NSNotification *)notification
2768 macdrv_event* event;
2770 if (causing_becomeKeyWindow) return;
2772 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2773 [queue postEvent:event];
2774 macdrv_release_event(event);
2777 - (void)windowDidResize:(NSNotification *)notification
2779 NSRect frame = self.wine_fractionalFrame;
2781 if ([self inLiveResize])
2783 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2784 resizingFromLeft = TRUE;
2785 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2786 resizingFromTop = TRUE;
2789 if (ignore_windowResize || exitingFullScreen) return;
2791 if ([self preventResizing])
2793 NSRect contentRect = [self contentRectForFrameRect:frame];
2794 [self setContentMinSize:contentRect.size];
2795 [self setContentMaxSize:contentRect.size];
2798 [self postWindowFrameChanged:frame
2799 fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2800 resizing:[self inLiveResize]];
2802 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2803 [self updateFullscreen];
2806 - (BOOL)windowShouldClose:(id)sender
2808 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2809 [queue postEvent:event];
2810 macdrv_release_event(event);
2814 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2818 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2819 [queue postEvent:event];
2820 macdrv_release_event(event);
2823 else if (!resizable)
2825 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2826 [queue postEvent:event];
2827 macdrv_release_event(event);
2834 - (void) windowWillClose:(NSNotification*)notification
2838 if (fakingClose) return;
2839 if (latentParentWindow)
2841 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2842 self.latentParentWindow = nil;
2845 for (child in latentChildWindows)
2847 if (child.latentParentWindow == self)
2848 child.latentParentWindow = nil;
2850 [latentChildWindows removeAllObjects];
2853 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2855 enteringFullScreen = TRUE;
2856 nonFullscreenFrame = self.wine_fractionalFrame;
2859 - (void) windowWillExitFullScreen:(NSNotification*)notification
2861 exitingFullScreen = TRUE;
2862 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2865 - (void)windowWillMiniaturize:(NSNotification *)notification
2867 [self becameIneligibleParentOrChild];
2868 [self grabDockIconSnapshotFromWindow:nil force:NO];
2871 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2873 if ([self inLiveResize])
2876 return self.wine_fractionalFrame.size;
2879 macdrv_query* query;
2881 rect = [self frame];
2882 if (resizingFromLeft)
2883 rect.origin.x = NSMaxX(rect) - frameSize.width;
2884 if (!resizingFromTop)
2885 rect.origin.y = NSMaxY(rect) - frameSize.height;
2886 rect.size = frameSize;
2887 rect = [self contentRectForFrameRect:rect];
2888 [[WineApplicationController sharedController] flipRect:&rect];
2890 query = macdrv_create_query();
2891 query->type = QUERY_RESIZE_SIZE;
2892 query->window = (macdrv_window)[self retain];
2893 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2894 query->resize_size.from_left = resizingFromLeft;
2895 query->resize_size.from_top = resizingFromTop;
2897 if ([self.queue query:query timeout:0.1])
2899 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2900 rect = [self frameRectForContentRect:rect];
2901 frameSize = rect.size;
2904 macdrv_release_query(query);
2910 - (void) windowWillStartLiveResize:(NSNotification *)notification
2912 [self endWindowDragging];
2916 macdrv_event* event;
2917 NSRect frame = [self contentRectForFrameRect:self.frame];
2919 [[WineApplicationController sharedController] flipRect:&frame];
2921 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2922 event->window_restore_requested.keep_frame = TRUE;
2923 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2924 [queue postEvent:event];
2925 macdrv_release_event(event);
2928 [self sendResizeStartQuery];
2930 frameAtResizeStart = [self frame];
2931 resizingFromLeft = resizingFromTop = FALSE;
2934 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2936 macdrv_query* query;
2937 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2940 query = macdrv_create_query();
2941 query->type = QUERY_MIN_MAX_INFO;
2942 query->window = (macdrv_window)[self retain];
2943 [self.queue query:query timeout:0.5];
2944 macdrv_release_query(query);
2946 currentContentRect = [self contentRectForFrameRect:[self frame]];
2947 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2949 maxSize = [self contentMaxSize];
2950 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2951 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2953 // Try to keep the top-left corner where it is.
2954 newContentRect.origin.x = NSMinX(currentContentRect);
2955 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2957 // If that pushes the bottom or right off the screen, pull it up and to the left.
2958 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2959 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2960 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2961 if (NSMinY(newContentRect) < NSMinY(screenRect))
2962 newContentRect.origin.y = NSMinY(screenRect);
2964 // If that pushes the top or left off the screen, push it down and the right
2965 // again. Do this last because the top-left corner is more important than the
2967 if (NSMinX(newContentRect) < NSMinX(screenRect))
2968 newContentRect.origin.x = NSMinX(screenRect);
2969 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2970 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2972 return [self frameRectForContentRect:newContentRect];
2977 * ---------- NSPasteboardOwner methods ----------
2979 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2981 macdrv_query* query = macdrv_create_query();
2982 query->type = QUERY_PASTEBOARD_DATA;
2983 query->window = (macdrv_window)[self retain];
2984 query->pasteboard_data.type = (CFStringRef)[type copy];
2986 [self.queue query:query timeout:3];
2987 macdrv_release_query(query);
2990 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
2992 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
2993 [queue postEvent:event];
2994 macdrv_release_event(event);
2999 * ---------- NSDraggingDestination methods ----------
3001 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3003 return [self draggingUpdated:sender];
3006 - (void) draggingExited:(id <NSDraggingInfo>)sender
3008 // This isn't really a query. We don't need any response. However, it
3009 // has to be processed in a similar manner as the other drag-and-drop
3010 // queries in order to maintain the proper order of operations.
3011 macdrv_query* query = macdrv_create_query();
3012 query->type = QUERY_DRAG_EXITED;
3013 query->window = (macdrv_window)[self retain];
3015 [self.queue query:query timeout:0.1];
3016 macdrv_release_query(query);
3019 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3021 NSDragOperation ret;
3022 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3023 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3024 NSPasteboard* pb = [sender draggingPasteboard];
3026 macdrv_query* query = macdrv_create_query();
3027 query->type = QUERY_DRAG_OPERATION;
3028 query->window = (macdrv_window)[self retain];
3029 query->drag_operation.x = floor(cgpt.x);
3030 query->drag_operation.y = floor(cgpt.y);
3031 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3032 query->drag_operation.accepted_op = NSDragOperationNone;
3033 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3035 [self.queue query:query timeout:3];
3036 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3037 macdrv_release_query(query);
3042 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3045 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3046 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3047 NSPasteboard* pb = [sender draggingPasteboard];
3049 macdrv_query* query = macdrv_create_query();
3050 query->type = QUERY_DRAG_DROP;
3051 query->window = (macdrv_window)[self retain];
3052 query->drag_drop.x = floor(cgpt.x);
3053 query->drag_drop.y = floor(cgpt.y);
3054 query->drag_drop.op = [sender draggingSourceOperationMask];
3055 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3057 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3058 ret = query->status;
3059 macdrv_release_query(query);
3064 - (BOOL) wantsPeriodicDraggingUpdates
3072 /***********************************************************************
3073 * macdrv_create_cocoa_window
3075 * Create a Cocoa window with the given content frame and features (e.g.
3076 * title bar, close box, etc.).
3078 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3079 CGRect frame, void* hwnd, macdrv_event_queue queue)
3081 __block WineWindow* window;
3084 window = [[WineWindow createWindowWithFeatures:wf
3085 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3087 queue:(WineEventQueue*)queue] retain];
3090 return (macdrv_window)window;
3093 /***********************************************************************
3094 * macdrv_destroy_cocoa_window
3096 * Destroy a Cocoa window.
3098 void macdrv_destroy_cocoa_window(macdrv_window w)
3100 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3101 WineWindow* window = (WineWindow*)w;
3104 [window doOrderOut];
3107 [window.queue discardEventsMatchingMask:-1 forWindow:window];
3113 /***********************************************************************
3114 * macdrv_get_window_hwnd
3116 * Get the hwnd that was set for the window at creation.
3118 void* macdrv_get_window_hwnd(macdrv_window w)
3120 WineWindow* window = (WineWindow*)w;
3124 /***********************************************************************
3125 * macdrv_set_cocoa_window_features
3127 * Update a Cocoa window's features.
3129 void macdrv_set_cocoa_window_features(macdrv_window w,
3130 const struct macdrv_window_features* wf)
3132 WineWindow* window = (WineWindow*)w;
3135 [window setWindowFeatures:wf];
3139 /***********************************************************************
3140 * macdrv_set_cocoa_window_state
3142 * Update a Cocoa window's state.
3144 void macdrv_set_cocoa_window_state(macdrv_window w,
3145 const struct macdrv_window_state* state)
3147 WineWindow* window = (WineWindow*)w;
3150 [window setMacDrvState:state];
3154 /***********************************************************************
3155 * macdrv_set_cocoa_window_title
3157 * Set a Cocoa window's title.
3159 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3162 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3163 WineWindow* window = (WineWindow*)w;
3164 NSString* titleString;
3167 titleString = [NSString stringWithCharacters:title length:length];
3170 OnMainThreadAsync(^{
3171 [window setTitle:titleString];
3172 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3173 [NSApp changeWindowsItem:window title:titleString filename:NO];
3179 /***********************************************************************
3180 * macdrv_order_cocoa_window
3182 * Reorder a Cocoa window relative to other windows. If prev is
3183 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3184 * it is ordered above that window. Otherwise, it is ordered to the
3187 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3188 macdrv_window n, int activate)
3190 WineWindow* window = (WineWindow*)w;
3191 WineWindow* prev = (WineWindow*)p;
3192 WineWindow* next = (WineWindow*)n;
3194 OnMainThreadAsync(^{
3195 [window orderBelow:prev
3199 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3201 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3205 /***********************************************************************
3206 * macdrv_hide_cocoa_window
3208 * Hides a Cocoa window.
3210 void macdrv_hide_cocoa_window(macdrv_window w)
3212 WineWindow* window = (WineWindow*)w;
3215 [window doOrderOut];
3219 /***********************************************************************
3220 * macdrv_set_cocoa_window_frame
3222 * Move a Cocoa window.
3224 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3226 WineWindow* window = (WineWindow*)w;
3229 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3233 /***********************************************************************
3234 * macdrv_get_cocoa_window_frame
3236 * Gets the frame of a Cocoa window.
3238 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3240 WineWindow* window = (WineWindow*)w;
3245 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3246 [[WineApplicationController sharedController] flipRect:&frame];
3247 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3251 /***********************************************************************
3252 * macdrv_set_cocoa_parent_window
3254 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3255 * the parent window.
3257 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3259 WineWindow* window = (WineWindow*)w;
3262 [window setMacDrvParentWindow:(WineWindow*)parent];
3266 /***********************************************************************
3267 * macdrv_set_window_surface
3269 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3271 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3272 WineWindow* window = (WineWindow*)w;
3275 window.surface = surface;
3276 window.surface_mutex = mutex;
3282 /***********************************************************************
3283 * macdrv_window_needs_display
3285 * Mark a window as needing display in a specified rect (in non-client
3286 * area coordinates).
3288 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3290 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3291 WineWindow* window = (WineWindow*)w;
3293 OnMainThreadAsync(^{
3294 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3300 /***********************************************************************
3301 * macdrv_set_window_shape
3303 * Sets the shape of a Cocoa window from an array of rectangles. If
3304 * rects is NULL, resets the window's shape to its frame.
3306 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3308 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3309 WineWindow* window = (WineWindow*)w;
3312 if (!rects || !count)
3315 window.shapeData = nil;
3316 [window checkEmptyShaped];
3320 size_t length = sizeof(*rects) * count;
3321 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3326 path = [NSBezierPath bezierPath];
3327 for (i = 0; i < count; i++)
3328 [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3329 window.shape = path;
3330 window.shapeData = [NSData dataWithBytes:rects length:length];
3331 [window checkEmptyShaped];
3339 /***********************************************************************
3340 * macdrv_set_window_alpha
3342 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3344 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3345 WineWindow* window = (WineWindow*)w;
3347 [window setAlphaValue:alpha];
3352 /***********************************************************************
3353 * macdrv_set_window_color_key
3355 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3358 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3359 WineWindow* window = (WineWindow*)w;
3362 window.colorKeyed = TRUE;
3363 window.colorKeyRed = keyRed;
3364 window.colorKeyGreen = keyGreen;
3365 window.colorKeyBlue = keyBlue;
3366 [window checkTransparency];
3372 /***********************************************************************
3373 * macdrv_clear_window_color_key
3375 void macdrv_clear_window_color_key(macdrv_window w)
3377 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3378 WineWindow* window = (WineWindow*)w;
3381 window.colorKeyed = FALSE;
3382 [window checkTransparency];
3388 /***********************************************************************
3389 * macdrv_window_use_per_pixel_alpha
3391 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3393 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3394 WineWindow* window = (WineWindow*)w;
3397 window.usePerPixelAlpha = use_per_pixel_alpha;
3398 [window checkTransparency];
3404 /***********************************************************************
3405 * macdrv_give_cocoa_window_focus
3407 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3408 * orders it front and, if its frame was not within the desktop bounds,
3409 * Cocoa will typically move it on-screen.
3411 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3413 WineWindow* window = (WineWindow*)w;
3416 [window makeFocused:activate];
3420 /***********************************************************************
3421 * macdrv_set_window_min_max_sizes
3423 * Sets the window's minimum and maximum content sizes.
3425 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3427 WineWindow* window = (WineWindow*)w;
3430 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3434 /***********************************************************************
3435 * macdrv_create_view
3437 * Creates and returns a view with the specified frame rect. The
3438 * caller is responsible for calling macdrv_dispose_view() on the view
3439 * when it is done with it.
3441 macdrv_view macdrv_create_view(CGRect rect)
3443 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3444 __block WineContentView* view;
3446 if (CGRectIsNull(rect)) rect = CGRectZero;
3449 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3451 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3452 [view setAutoresizesSubviews:NO];
3453 [view setHidden:YES];
3454 [nc addObserver:view
3455 selector:@selector(updateGLContexts)
3456 name:NSViewGlobalFrameDidChangeNotification
3458 [nc addObserver:view
3459 selector:@selector(updateGLContexts)
3460 name:NSApplicationDidChangeScreenParametersNotification
3465 return (macdrv_view)view;
3468 /***********************************************************************
3469 * macdrv_dispose_view
3471 * Destroys a view previously returned by macdrv_create_view.
3473 void macdrv_dispose_view(macdrv_view v)
3475 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3476 WineContentView* view = (WineContentView*)v;
3479 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3480 WineWindow* window = (WineWindow*)[view window];
3482 [nc removeObserver:view
3483 name:NSViewGlobalFrameDidChangeNotification
3485 [nc removeObserver:view
3486 name:NSApplicationDidChangeScreenParametersNotification
3488 [view removeFromSuperview];
3490 [window updateForGLSubviews];
3496 /***********************************************************************
3497 * macdrv_set_view_frame
3499 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3501 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3502 WineContentView* view = (WineContentView*)v;
3504 if (CGRectIsNull(rect)) rect = CGRectZero;
3506 OnMainThreadAsync(^{
3507 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3508 NSRect oldFrame = [view frame];
3510 if (!NSEqualRects(oldFrame, newFrame))
3512 [[view superview] setNeedsDisplayInRect:oldFrame];
3513 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3514 [view setFrameSize:newFrame.size];
3515 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3516 [view setFrameOrigin:newFrame.origin];
3518 [view setFrame:newFrame];
3519 [view setNeedsDisplay:YES];
3523 int backing_size[2] = { 0 };
3524 [view wine_setBackingSize:backing_size];
3526 [(WineWindow*)[view window] updateForGLSubviews];
3533 /***********************************************************************
3534 * macdrv_set_view_superview
3536 * Move a view to a new superview and position it relative to its
3537 * siblings. If p is non-NULL, the view is ordered behind it.
3538 * Otherwise, the view is ordered above n. If s is NULL, use the
3539 * content view of w as the new superview.
3541 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3543 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3544 WineContentView* view = (WineContentView*)v;
3545 WineContentView* superview = (WineContentView*)s;
3546 WineWindow* window = (WineWindow*)w;
3547 WineContentView* prev = (WineContentView*)p;
3548 WineContentView* next = (WineContentView*)n;
3551 superview = [window contentView];
3553 OnMainThreadAsync(^{
3554 if (superview == [view superview])
3556 NSArray* subviews = [superview subviews];
3557 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3558 if (!prev && !next && index == [subviews count] - 1)
3560 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3562 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3566 WineWindow* oldWindow = (WineWindow*)[view window];
3567 WineWindow* newWindow = (WineWindow*)[superview window];
3569 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3570 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3571 [view removeFromSuperview];
3574 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3576 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3578 if (oldWindow != newWindow)
3580 [oldWindow updateForGLSubviews];
3581 [newWindow updateForGLSubviews];
3588 /***********************************************************************
3589 * macdrv_set_view_hidden
3591 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3593 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3594 WineContentView* view = (WineContentView*)v;
3596 OnMainThreadAsync(^{
3597 [view setHidden:hidden];
3598 [(WineWindow*)view.window updateForGLSubviews];
3604 /***********************************************************************
3605 * macdrv_add_view_opengl_context
3607 * Add an OpenGL context to the list being tracked for each view.
3609 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3611 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3612 WineContentView* view = (WineContentView*)v;
3613 WineOpenGLContext *context = (WineOpenGLContext*)c;
3616 [view addGLContext:context];
3622 /***********************************************************************
3623 * macdrv_remove_view_opengl_context
3625 * Add an OpenGL context to the list being tracked for each view.
3627 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3629 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3630 WineContentView* view = (WineContentView*)v;
3631 WineOpenGLContext *context = (WineOpenGLContext*)c;
3633 OnMainThreadAsync(^{
3634 [view removeGLContext:context];
3640 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3642 WineContentView* view = (WineContentView*)v;
3644 if (![view isKindOfClass:[WineContentView class]])
3647 [view wine_getBackingSize:backing_size];
3651 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3653 WineContentView* view = (WineContentView*)v;
3655 if ([view isKindOfClass:[WineContentView class]])
3656 [view wine_setBackingSize:backing_size];
3659 /***********************************************************************
3660 * macdrv_window_background_color
3662 * Returns the standard Mac window background color as a 32-bit value of
3663 * the form 0x00rrggbb.
3665 uint32_t macdrv_window_background_color(void)
3667 static uint32_t result;
3668 static dispatch_once_t once;
3670 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3671 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3672 // of it is to draw with it.
3673 dispatch_once(&once, ^{
3675 unsigned char rgbx[4];
3676 unsigned char *planes = rgbx;
3677 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3684 colorSpaceName:NSCalibratedRGBColorSpace
3688 [NSGraphicsContext saveGraphicsState];
3689 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3690 [[NSColor windowBackgroundColor] set];
3691 NSRectFill(NSMakeRect(0, 0, 1, 1));
3692 [NSGraphicsContext restoreGraphicsState];
3694 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3701 /***********************************************************************
3702 * macdrv_send_text_input_event
3704 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3706 OnMainThreadAsync(^{
3708 macdrv_event* event;
3709 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3710 if (![window isKindOfClass:[WineWindow class]])
3712 window = (WineWindow*)[NSApp mainWindow];
3713 if (![window isKindOfClass:[WineWindow class]])
3714 window = [[WineApplicationController sharedController] frontWineWindow];
3719 NSUInteger localFlags = flags;
3723 window.imeData = data;
3724 fix_device_modifiers_by_generic(&localFlags);
3726 // An NSEvent created with +keyEventWithType:... is internally marked
3727 // as synthetic and doesn't get sent through input methods. But one
3728 // created from a CGEvent doesn't have that problem.
3729 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3730 CGEventSetFlags(c, localFlags);
3731 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3732 event = [NSEvent eventWithCGEvent:c];
3735 window.commandDone = FALSE;
3736 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3741 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3742 event->sent_text_input.handled = ret;
3743 event->sent_text_input.done = done;
3744 [[window queue] postEvent:event];
3745 macdrv_release_event(event);