winewayland.drv: Implement vkGetPhysicalDeviceSurfaceSupportKHR.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blob1655ea98ef733cb6cb02943a314c4543811b2395
1 /*
2  * MACDRV Cocoa window code
3  *
4  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
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.
10  *
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.
15  *
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
19  */
21 #include "config.h"
23 #define GL_SILENCE_DEPRECATION
24 #import <Carbon/Carbon.h>
25 #import <CoreVideo/CoreVideo.h>
26 #import <Metal/Metal.h>
27 #import <QuartzCore/QuartzCore.h>
29 #import "cocoa_window.h"
31 #include "macdrv_cocoa.h"
32 #import "cocoa_app.h"
33 #import "cocoa_event.h"
34 #import "cocoa_opengl.h"
36 #pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
38 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
39 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
40 enum {
41     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
43 #endif
46 @interface NSWindow (PrivatePreventsActivation)
48 /* Needed to ensure proper behavior after adding or removing
49  * NSWindowStyleMaskNonactivatingPanel.
50  * Available since at least macOS 10.6. */
51 - (void)_setPreventsActivation:(BOOL)flag;
53 @end
56 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
58     NSUInteger style_mask;
60     if (wf->title_bar)
61     {
62         style_mask = NSWindowStyleMaskTitled;
63         if (wf->close_button) style_mask |= NSWindowStyleMaskClosable;
64         if (wf->minimize_button) style_mask |= NSWindowStyleMaskMiniaturizable;
65         if (wf->resizable || wf->maximize_button) style_mask |= NSWindowStyleMaskResizable;
66         if (wf->utility) style_mask |= NSWindowStyleMaskUtilityWindow;
67     }
68     else style_mask = NSWindowStyleMaskBorderless;
70     if (wf->prevents_app_activation) style_mask |= NSWindowStyleMaskNonactivatingPanel;
72     return style_mask;
76 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
78     NSScreen* screen;
79     for (screen in screens)
80     {
81         if (NSIntersectsRect(frame, [screen frame]))
82             return TRUE;
83     }
84     return FALSE;
88 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
90     for (NSScreen* screen in screens)
91     {
92         if (NSContainsRect(rect, [screen frame]))
93             return screen;
94     }
95     return nil;
99 /* We rely on the supposedly device-dependent modifier flags to distinguish the
100    keys on the left side of the keyboard from those on the right.  Some event
101    sources don't set those device-depdendent flags.  If we see a device-independent
102    flag for a modifier without either corresponding device-dependent flag, assume
103    the left one. */
104 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
106     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
107         *modifiers |= NX_DEVICELCMDKEYMASK;
108     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
109         *modifiers |= NX_DEVICELSHIFTKEYMASK;
110     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
111         *modifiers |= NX_DEVICELCTLKEYMASK;
112     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
113         *modifiers |= NX_DEVICELALTKEYMASK;
116 /* As we manipulate individual bits of a modifier mask, we can end up with
117    inconsistent sets of flags.  In particular, we might set or clear one of the
118    left/right-specific bits, but not the corresponding non-side-specific bit.
119    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
120    otherwise clear it. */
121 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
123     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
124         *modifiers |= NX_COMMANDMASK;
125     else
126         *modifiers &= ~NX_COMMANDMASK;
127     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
128         *modifiers |= NX_SHIFTMASK;
129     else
130         *modifiers &= ~NX_SHIFTMASK;
131     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
132         *modifiers |= NX_CONTROLMASK;
133     else
134         *modifiers &= ~NX_CONTROLMASK;
135     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
136         *modifiers |= NX_ALTERNATEMASK;
137     else
138         *modifiers &= ~NX_ALTERNATEMASK;
141 static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
143     fix_device_modifiers_by_generic(&modifiers);
144     NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
145                                              NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
147     // The MACDRV keyboard driver translates Command keys to Alt. If the
148     // Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
149     // Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
150     if (modifiers & NX_DEVICELALTKEYMASK)
151         new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
152     if (modifiers & NX_DEVICERALTKEYMASK)
153         new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
155     if (modifiers & NX_DEVICELCMDKEYMASK)
156         new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
157     if (modifiers & NX_DEVICERCMDKEYMASK)
158         new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
160     fix_generic_modifiers_by_device(&new_modifiers);
161     return new_modifiers;
164 static inline BOOL stage_manager_enabled(void)
166     /* There is no documented way to determine if Stage Manager is enabled,
167      * but this seems like the best option.
168      */
169     if (floor(NSAppKitVersionNumber) >= 2299 /* NSAppKitVersionNumber13_0 */)
170     {
171         NSUserDefaults *defs = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.WindowManager.plist"];
172         BOOL enabled = [defs boolForKey:@"GloballyEnabled"];
173         [defs release];
174         return enabled;
175     }
176     return FALSE;
180 @interface NSWindow (WineAccessPrivateMethods)
181     - (id) _displayChanged;
182 @end
185 @interface WineDisplayLink : NSObject
187     CGDirectDisplayID _displayID;
188     CVDisplayLinkRef _link;
189     NSMutableSet* _windows;
191     NSTimeInterval _actualRefreshPeriod;
192     NSTimeInterval _nominalRefreshPeriod;
194     NSTimeInterval _lastDisplayTime;
197     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
199     - (void) addWindow:(WineWindow*)window;
200     - (void) removeWindow:(WineWindow*)window;
202     - (NSTimeInterval) refreshPeriod;
204     - (void) start;
206 @end
208 @implementation WineDisplayLink
210 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
212     - (id) initWithDisplayID:(CGDirectDisplayID)displayID
213     {
214         self = [super init];
215         if (self)
216         {
217             CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
218             if (status == kCVReturnSuccess && !_link)
219                 status = kCVReturnError;
220             if (status == kCVReturnSuccess)
221                 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
222             if (status != kCVReturnSuccess)
223             {
224                 [self release];
225                 return nil;
226             }
228             _displayID = displayID;
229             _windows = [[NSMutableSet alloc] init];
230         }
231         return self;
232     }
234     - (void) dealloc
235     {
236         if (_link)
237         {
238             CVDisplayLinkStop(_link);
239             CVDisplayLinkRelease(_link);
240         }
241         [_windows release];
242         [super dealloc];
243     }
245     - (void) addWindow:(WineWindow*)window
246     {
247         BOOL firstWindow;
248         @synchronized(self) {
249             firstWindow = !_windows.count;
250             [_windows addObject:window];
251         }
252         if (firstWindow || !CVDisplayLinkIsRunning(_link))
253             [self start];
254     }
256     - (void) removeWindow:(WineWindow*)window
257     {
258         BOOL lastWindow = FALSE;
259         @synchronized(self) {
260             BOOL hadWindows = _windows.count > 0;
261             [_windows removeObject:window];
262             if (hadWindows && !_windows.count)
263                 lastWindow = TRUE;
264         }
265         if (lastWindow && CVDisplayLinkIsRunning(_link))
266             CVDisplayLinkStop(_link);
267     }
269     - (void) fire
270     {
271         NSSet* windows;
272         @synchronized(self) {
273             windows = [_windows copy];
274         }
275         dispatch_async(dispatch_get_main_queue(), ^{
276             BOOL anyDisplayed = FALSE;
277             for (WineWindow* window in windows)
278             {
279                 if ([window viewsNeedDisplay])
280                 {
281                     [window displayIfNeeded];
282                     anyDisplayed = YES;
283                 }
284             }
286             NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
287             if (anyDisplayed)
288                 _lastDisplayTime = now;
289             else if (_lastDisplayTime + 2.0 < now)
290                 CVDisplayLinkStop(_link);
291         });
292         [windows release];
293     }
295     - (NSTimeInterval) refreshPeriod
296     {
297         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
298             return _actualRefreshPeriod;
300         if (_nominalRefreshPeriod)
301             return _nominalRefreshPeriod;
303         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
304         if (time.flags & kCVTimeIsIndefinite)
305             return 1.0 / 60.0;
306         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
307         return _nominalRefreshPeriod;
308     }
310     - (void) start
311     {
312         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
313         CVDisplayLinkStart(_link);
314     }
316 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
318     WineDisplayLink* link = displayLinkContext;
319     [link fire];
320     return kCVReturnSuccess;
323 @end
326 #ifndef MAC_OS_X_VERSION_10_14
327 @protocol NSViewLayerContentScaleDelegate <NSObject>
328 @optional
330     - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window;
332 @end
333 #endif
336 @interface CAShapeLayer (WineShapeMaskExtensions)
338 @property(readonly, nonatomic, getter=isEmptyShaped) BOOL emptyShaped;
340 @end
342 @implementation CAShapeLayer (WineShapeMaskExtensions)
344     - (BOOL) isEmptyShaped
345     {
346         return CGRectEqualToRect(CGPathGetBoundingBox(self.path), CGRectZero);
347     }
349 @end
352 @interface WineBaseView : NSView
353 @end
356 @interface WineMetalView : WineBaseView
358     id<MTLDevice> _device;
361     - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
363 @end
366 @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContentScaleDelegate>
368     NSMutableArray* glContexts;
369     NSMutableArray* pendingGlContexts;
370     BOOL _everHadGLContext;
371     BOOL _cachedHasGLDescendant;
372     BOOL _cachedHasGLDescendantValid;
373     BOOL clearedGlSurface;
375     NSMutableAttributedString* markedText;
376     NSRange markedTextSelection;
378     int backingSize[2];
380     WineMetalView *_metalView;
383 @property (readonly, nonatomic) BOOL everHadGLContext;
385     - (void) addGLContext:(WineOpenGLContext*)context;
386     - (void) removeGLContext:(WineOpenGLContext*)context;
387     - (void) updateGLContexts;
389     - (void) wine_getBackingSize:(int*)outBackingSize;
390     - (void) wine_setBackingSize:(const int*)newBackingSize;
392     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
394 @end
397 @interface WineWindow ()
399 @property (readwrite, nonatomic) BOOL disabled;
400 @property (readwrite, nonatomic) BOOL noForeground;
401 @property (readwrite, nonatomic) BOOL preventsAppActivation;
402 @property (readwrite, nonatomic) BOOL floating;
403 @property (readwrite, nonatomic) BOOL drawnSinceShown;
404 @property (readwrite, nonatomic) BOOL closing;
405 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
406 @property (retain, nonatomic) NSWindow* latentParentWindow;
408 @property (nonatomic) void* hwnd;
409 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
411 @property (nonatomic) void* surface;
412 @property (nonatomic) pthread_mutex_t* surface_mutex;
414 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
415 @property (readonly, nonatomic) BOOL needsTransparency;
417 @property (nonatomic) BOOL colorKeyed;
418 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
419 @property (nonatomic) BOOL usePerPixelAlpha;
421 @property (assign, nonatomic) void* himc;
422 @property (nonatomic) BOOL commandDone;
424 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
426     - (void) setShape:(CGPathRef)newShape;
428     - (void) updateForGLSubviews;
430     - (BOOL) becameEligibleParentOrChild;
431     - (void) becameIneligibleChild;
433     - (void) windowDidDrawContent;
435 @end
438 @implementation WineBaseView
440     - (void) setRetinaMode:(int)mode
441     {
442         for (WineBaseView* subview in [self subviews])
443         {
444             if ([subview isKindOfClass:[WineBaseView class]])
445                 [subview setRetinaMode:mode];
446         }
447     }
449     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
450     {
451         return YES;
452     }
454     - (BOOL) preservesContentDuringLiveResize
455     {
456         // Returning YES from this tells Cocoa to keep our view's content during
457         // a Cocoa-driven resize.  In theory, we're also supposed to override
458         // -setFrameSize: to mark exposed sections as needing redisplay, but
459         // user32 will take care of that in a roundabout way.  This way, we don't
460         // redraw until the window surface is flushed.
461         //
462         // This doesn't do anything when we resize the window ourselves.
463         return YES;
464     }
466     - (BOOL)acceptsFirstResponder
467     {
468         return [[self window] contentView] == self;
469     }
471     - (BOOL) mouseDownCanMoveWindow
472     {
473         return NO;
474     }
476     - (NSFocusRingType) focusRingType
477     {
478         return NSFocusRingTypeNone;
479     }
481 @end
484 @implementation WineContentView
486 @synthesize everHadGLContext = _everHadGLContext;
488     - (instancetype) initWithFrame:(NSRect)frame
489     {
490         self = [super initWithFrame:frame];
491         if (self)
492         {
493             [self setWantsLayer:YES];
494             [self setLayerRetinaProperties:retina_on];
495             [self setAutoresizesSubviews:NO];
496         }
497         return self;
498     }
500     - (void) dealloc
501     {
502         [markedText release];
503         [glContexts release];
504         [pendingGlContexts release];
505         [super dealloc];
506     }
508     - (BOOL) isFlipped
509     {
510         return YES;
511     }
513     - (BOOL) wantsUpdateLayer
514     {
515         return YES /*!_everHadGLContext*/;
516     }
518     - (void) updateLayer
519     {
520         WineWindow* window = (WineWindow*)[self window];
521         CGImageRef image = NULL;
522         CGRect imageRect;
523         CALayer* layer = [self layer];
525         if ([window contentView] != self)
526             return;
528         if (window.closing || !window.surface || !window.surface_mutex)
529             return;
531         pthread_mutex_lock(window.surface_mutex);
532         if (get_surface_blit_rects(window.surface, NULL, NULL))
533         {
534             imageRect = layer.bounds;
535             imageRect.origin.x *= layer.contentsScale;
536             imageRect.origin.y *= layer.contentsScale;
537             imageRect.size.width *= layer.contentsScale;
538             imageRect.size.height *= layer.contentsScale;
539             image = create_surface_image(window.surface, &imageRect, FALSE, window.colorKeyed,
540                                          window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue);
541         }
542         pthread_mutex_unlock(window.surface_mutex);
544         if (image)
545         {
546             layer.contents = (id)image;
547             CFRelease(image);
548             [window windowDidDrawContent];
550             // If the window may be transparent, then we have to invalidate the
551             // shadow every time we draw.  Also, if this is the first time we've
552             // drawn since changing from transparent to opaque.
553             if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
554             {
555                 window.shapeChangedSinceLastDraw = FALSE;
556                 [window invalidateShadow];
557             }
558         }
559     }
561     - (void) viewWillDraw
562     {
563         [super viewWillDraw];
565         for (WineOpenGLContext* context in pendingGlContexts)
566         {
567             if (!clearedGlSurface)
568             {
569                 context.shouldClearToBlack = TRUE;
570                 clearedGlSurface = TRUE;
571             }
572             context.needsUpdate = TRUE;
573         }
574         [glContexts addObjectsFromArray:pendingGlContexts];
575         [pendingGlContexts removeAllObjects];
576     }
578     - (void) addGLContext:(WineOpenGLContext*)context
579     {
580         BOOL hadContext = _everHadGLContext;
581         if (!glContexts)
582             glContexts = [[NSMutableArray alloc] init];
583         if (!pendingGlContexts)
584             pendingGlContexts = [[NSMutableArray alloc] init];
586         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
587         {
588             [glContexts addObject:context];
589             if (!clearedGlSurface)
590             {
591                 context.shouldClearToBlack = TRUE;
592                 clearedGlSurface = TRUE;
593             }
594             context.needsUpdate = TRUE;
595         }
596         else
597         {
598             [pendingGlContexts addObject:context];
599             [self setNeedsDisplay:YES];
600         }
602         _everHadGLContext = YES;
603         if (!hadContext)
604             [self invalidateHasGLDescendant];
605         [(WineWindow*)[self window] updateForGLSubviews];
606     }
608     - (void) removeGLContext:(WineOpenGLContext*)context
609     {
610         [glContexts removeObjectIdenticalTo:context];
611         [pendingGlContexts removeObjectIdenticalTo:context];
612         [(WineWindow*)[self window] updateForGLSubviews];
613     }
615     - (void) updateGLContexts:(BOOL)reattach
616     {
617         for (WineOpenGLContext* context in glContexts)
618         {
619             context.needsUpdate = TRUE;
620             if (reattach)
621                 context.needsReattach = TRUE;
622         }
623     }
625     - (void) updateGLContexts
626     {
627         [self updateGLContexts:NO];
628     }
630     - (BOOL) _hasGLDescendant
631     {
632         if ([self isHidden])
633             return NO;
634         if (_everHadGLContext)
635             return YES;
636         for (WineContentView* view in [self subviews])
637         {
638             if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
639                 return YES;
640         }
641         return NO;
642     }
644     - (BOOL) hasGLDescendant
645     {
646         if (!_cachedHasGLDescendantValid)
647         {
648             _cachedHasGLDescendant = [self _hasGLDescendant];
649             _cachedHasGLDescendantValid = YES;
650         }
651         return _cachedHasGLDescendant;
652     }
654     - (void) invalidateHasGLDescendant
655     {
656         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
657         _cachedHasGLDescendantValid = NO;
658         if (invalidateAncestors && self != [[self window] contentView])
659         {
660             WineContentView* superview = (WineContentView*)[self superview];
661             if ([superview isKindOfClass:[WineContentView class]])
662                 [superview invalidateHasGLDescendant];
663         }
664     }
666     - (void) wine_getBackingSize:(int*)outBackingSize
667     {
668         @synchronized(self) {
669             memcpy(outBackingSize, backingSize, sizeof(backingSize));
670         }
671     }
672     - (void) wine_setBackingSize:(const int*)newBackingSize
673     {
674         @synchronized(self) {
675             memcpy(backingSize, newBackingSize, sizeof(backingSize));
676         }
677     }
679     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
680     {
681         if (_metalView) return _metalView;
683         WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
684         [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
685         [self setAutoresizesSubviews:YES];
686         [self addSubview:view positioned:NSWindowBelow relativeTo:nil];
687         _metalView = view;
689         [(WineWindow*)self.window windowDidDrawContent];
691         return _metalView;
692     }
694     - (void) setLayerRetinaProperties:(int)mode
695     {
696         [self layer].contentsScale = mode ? 2.0 : 1.0;
697         [self layer].minificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
698         [self layer].magnificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
700         /* On macOS 10.13 and earlier, the desired minificationFilter seems to be
701          * ignored and "nearest" filtering is used, which looks terrible.
702          * Enabling rasterization seems to work around this, only enable
703          * it when there may be down-scaling (retina mode enabled).
704          */
705         if (floor(NSAppKitVersionNumber) < 1671 /*NSAppKitVersionNumber10_14*/)
706         {
707             if (mode)
708             {
709                 [self layer].shouldRasterize = YES;
710                 [self layer].rasterizationScale = 2.0;
711             }
712             else
713                 [self layer].shouldRasterize = NO;
714         }
715     }
717     - (void) setRetinaMode:(int)mode
718     {
719         double scale = mode ? 0.5 : 2.0;
720         NSRect frame = self.frame;
721         frame.origin.x *= scale;
722         frame.origin.y *= scale;
723         frame.size.width *= scale;
724         frame.size.height *= scale;
725         [self setFrame:frame];
726         [self setWantsBestResolutionOpenGLSurface:mode];
727         [self updateGLContexts];
728         [self setLayerRetinaProperties:mode];
730         [super setRetinaMode:mode];
731     }
733     - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window
734     {
735         /* This method is invoked when the contentsScale of the layer is not
736          * equal to the contentsScale of the window.
737          * (Initially when the layer is first created, and later if the window
738          * contentsScale changes, i.e. moved between retina/non-retina monitors).
739          *
740          * We usually want to return YES, so the "moving windows between
741          * retina/non-retina monitors" case works right.
742          * But return NO when we need an intentional mismatch between the
743          * window and layer contentsScale
744          * (non-retina mode with a retina monitor, and vice-versa).
745          */
746         if (layer.contentsScale != window.backingScaleFactor)
747             return NO;
748         return YES;
749     }
751     - (void) viewDidHide
752     {
753         [super viewDidHide];
754         [self invalidateHasGLDescendant];
755     }
757     - (void) viewDidUnhide
758     {
759         [super viewDidUnhide];
760         [self updateGLContexts:YES];
761         [self invalidateHasGLDescendant];
762     }
764     - (void) clearMarkedText
765     {
766         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
767         markedTextSelection = NSMakeRange(0, 0);
768         [[self inputContext] discardMarkedText];
769     }
771     - (void) completeText:(NSString*)text
772     {
773         macdrv_event* event;
774         WineWindow* window = (WineWindow*)[self window];
776         event = macdrv_create_event(IM_SET_TEXT, window);
777         event->im_set_text.himc = [window himc];
778         event->im_set_text.text = (CFStringRef)[text copy];
779         event->im_set_text.complete = TRUE;
781         [[window queue] postEvent:event];
783         macdrv_release_event(event);
785         [self clearMarkedText];
786     }
788     - (void) didAddSubview:(NSView*)subview
789     {
790         if ([subview isKindOfClass:[WineContentView class]])
791         {
792             WineContentView* view = (WineContentView*)subview;
793             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
794                 [self invalidateHasGLDescendant];
795         }
796         [super didAddSubview:subview];
797     }
799     - (void) willRemoveSubview:(NSView*)subview
800     {
801         if ([subview isKindOfClass:[WineContentView class]])
802         {
803             WineContentView* view = (WineContentView*)subview;
804             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
805                 [self invalidateHasGLDescendant];
806         }
807         if (subview == _metalView)
808             _metalView = nil;
809         [super willRemoveSubview:subview];
810     }
812     - (void) setLayer:(CALayer*)newLayer
813     {
814         [super setLayer:newLayer];
815         [self updateGLContexts];
816     }
818     /*
819      * ---------- NSTextInputClient methods ----------
820      */
821     - (NSTextInputContext*) inputContext
822     {
823         if (!markedText)
824             markedText = [[NSMutableAttributedString alloc] init];
825         return [super inputContext];
826     }
828     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
829     {
830         if ([string isKindOfClass:[NSAttributedString class]])
831             string = [string string];
833         if ([string isKindOfClass:[NSString class]])
834             [self completeText:string];
835     }
837     - (void) doCommandBySelector:(SEL)aSelector
838     {
839         [(WineWindow*)[self window] setCommandDone:TRUE];
840     }
842     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
843     {
844         if ([string isKindOfClass:[NSAttributedString class]])
845             string = [string string];
847         if ([string isKindOfClass:[NSString class]])
848         {
849             macdrv_event* event;
850             WineWindow* window = (WineWindow*)[self window];
852             if (replacementRange.location == NSNotFound)
853                 replacementRange = NSMakeRange(0, [markedText length]);
855             [markedText replaceCharactersInRange:replacementRange withString:string];
856             markedTextSelection = selectedRange;
857             markedTextSelection.location += replacementRange.location;
859             event = macdrv_create_event(IM_SET_TEXT, window);
860             event->im_set_text.himc = [window himc];
861             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
862             event->im_set_text.complete = FALSE;
863             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
865             [[window queue] postEvent:event];
867             macdrv_release_event(event);
869             [[self inputContext] invalidateCharacterCoordinates];
870         }
871     }
873     - (void) unmarkText
874     {
875         [self completeText:nil];
876     }
878     - (NSRange) selectedRange
879     {
880         return markedTextSelection;
881     }
883     - (NSRange) markedRange
884     {
885         NSRange range = NSMakeRange(0, [markedText length]);
886         if (!range.length)
887             range.location = NSNotFound;
888         return range;
889     }
891     - (BOOL) hasMarkedText
892     {
893         return [markedText length] > 0;
894     }
896     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
897     {
898         if (aRange.location >= [markedText length])
899             return nil;
901         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
902         if (actualRange)
903             *actualRange = aRange;
904         return [markedText attributedSubstringFromRange:aRange];
905     }
907     - (NSArray*) validAttributesForMarkedText
908     {
909         return [NSArray array];
910     }
912     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
913     {
914         macdrv_query* query;
915         WineWindow* window = (WineWindow*)[self window];
916         NSRect ret;
918         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
920         query = macdrv_create_query();
921         query->type = QUERY_IME_CHAR_RECT;
922         query->window = (macdrv_window)[window retain];
923         query->ime_char_rect.himc = [window himc];
924         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
926         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
927         {
928             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
929             ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
930             [[WineApplicationController sharedController] flipRect:&ret];
931         }
932         else
933             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
935         macdrv_release_query(query);
937         if (actualRange)
938             *actualRange = aRange;
939         return ret;
940     }
942     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
943     {
944         return NSNotFound;
945     }
947     - (NSInteger) windowLevel
948     {
949         return [[self window] level];
950     }
952 @end
955 @implementation WineMetalView
957     - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
958     {
959         self = [super initWithFrame:frame];
960         if (self)
961         {
962             _device = [device retain];
963             self.wantsLayer = YES;
964             self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
965         }
966         return self;
967     }
969     - (void) dealloc
970     {
971         [_device release];
972         [super dealloc];
973     }
975     - (void) setRetinaMode:(int)mode
976     {
977         self.layer.contentsScale = mode ? 2.0 : 1.0;
978         [super setRetinaMode:mode];
979     }
981     - (CALayer*) makeBackingLayer
982     {
983         CAMetalLayer *layer = [CAMetalLayer layer];
984         layer.device = _device;
985         layer.framebufferOnly = YES;
986         layer.magnificationFilter = kCAFilterNearest;
987         layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
988         layer.contentsScale = retina_on ? 2.0 : 1.0;
989         return layer;
990     }
992     - (BOOL) isOpaque
993     {
994         return YES;
995     }
997 @end
1000 @implementation WineWindow
1002     static WineWindow* causing_becomeKeyWindow;
1004     @synthesize disabled, noForeground, preventsAppActivation, floating, fullscreen, fakingClose, closing, latentParentWindow, hwnd, queue;
1005     @synthesize drawnSinceShown;
1006     @synthesize surface, surface_mutex;
1007     @synthesize shapeChangedSinceLastDraw;
1008     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
1009     @synthesize usePerPixelAlpha;
1010     @synthesize himc, commandDone;
1012     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
1013                                  windowFrame:(NSRect)window_frame
1014                                         hwnd:(void*)hwnd
1015                                        queue:(WineEventQueue*)queue
1016     {
1017         WineWindow* window;
1018         WineContentView* contentView;
1019         NSTrackingArea* trackingArea;
1020         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1022         [[WineApplicationController sharedController] flipRect:&window_frame];
1024         window = [[[self alloc] initWithContentRect:window_frame
1025                                           styleMask:style_mask_for_features(wf)
1026                                             backing:NSBackingStoreBuffered
1027                                               defer:YES] autorelease];
1029         if (!window) return nil;
1031         /* Standardize windows to eliminate differences between titled and
1032            borderless windows and between NSWindow and NSPanel. */
1033         [window setHidesOnDeactivate:NO];
1034         [window setReleasedWhenClosed:NO];
1036         [window setOneShot:YES];
1037         [window disableCursorRects];
1038         [window setShowsResizeIndicator:NO];
1039         [window setHasShadow:wf->shadow];
1040         [window setAcceptsMouseMovedEvents:YES];
1041         [window setDelegate:window];
1042         [window setBackgroundColor:[NSColor clearColor]];
1043         [window setOpaque:NO];
1044         window.hwnd = hwnd;
1045         window.queue = queue;
1046         window->savedContentMinSize = NSZeroSize;
1047         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
1048         window->resizable = wf->resizable;
1049         window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
1051         [window registerForDraggedTypes:@[(NSString*)kUTTypeData, (NSString*)kUTTypeContent]];
1053         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
1054         if (!contentView)
1055             return nil;
1057         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
1058            because they give us mouse moves in the background. */
1059         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
1060                                                      options:(NSTrackingMouseMoved |
1061                                                               NSTrackingActiveAlways |
1062                                                               NSTrackingInVisibleRect)
1063                                                        owner:window
1064                                                     userInfo:nil] autorelease];
1065         if (!trackingArea)
1066             return nil;
1067         [contentView addTrackingArea:trackingArea];
1069         [window setContentView:contentView];
1070         [window setInitialFirstResponder:contentView];
1072         [nc addObserver:window
1073                selector:@selector(updateFullscreen)
1074                    name:NSApplicationDidChangeScreenParametersNotification
1075                  object:NSApp];
1076         [window updateFullscreen];
1078         [nc addObserver:window
1079                selector:@selector(applicationWillHide)
1080                    name:NSApplicationWillHideNotification
1081                  object:NSApp];
1082         [nc addObserver:window
1083                selector:@selector(applicationDidUnhide)
1084                    name:NSApplicationDidUnhideNotification
1085                  object:NSApp];
1087         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1088                                                               selector:@selector(checkWineDisplayLink)
1089                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
1090                                                                 object:[NSWorkspace sharedWorkspace]];
1092         [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1094         return window;
1095     }
1097     - (void) dealloc
1098     {
1099         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1100         [[NSNotificationCenter defaultCenter] removeObserver:self];
1101         [queue release];
1102         [latentChildWindows release];
1103         [latentParentWindow release];
1104         [super dealloc];
1105     }
1107     - (BOOL) preventResizing
1108     {
1109         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1110         return ([self styleMask] & NSWindowStyleMaskResizable) && (disabled || !resizable || preventForClipping);
1111     }
1113     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1114     {
1115         if (allow_immovable_windows && (disabled || inMaximized))
1116             return NO;
1117         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1118             return NO;
1119         else
1120             return YES;
1121     }
1123     - (void) adjustFeaturesForState
1124     {
1125         NSUInteger style = [self styleMask];
1127         if (style & NSWindowStyleMaskClosable)
1128             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1129         if (style & NSWindowStyleMaskMiniaturizable)
1130             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1131         if (style & NSWindowStyleMaskResizable)
1132             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1133         if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1134             [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1136         if ([self preventResizing])
1137         {
1138             NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1139             [self setContentMinSize:size];
1140             [self setContentMaxSize:size];
1141         }
1142         else
1143         {
1144             [self setContentMaxSize:savedContentMaxSize];
1145             [self setContentMinSize:savedContentMinSize];
1146         }
1148         if (allow_immovable_windows || cursor_clipping_locks_windows)
1149             [self setMovable:[self allowsMovingWithMaximized:maximized]];
1150     }
1152     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1153     {
1154         NSUInteger style = [self styleMask];
1156         if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1157             style & NSWindowStyleMaskResizable && !(style & NSWindowStyleMaskUtilityWindow) && !maximized &&
1158             !(self.parentWindow || self.latentParentWindow))
1159         {
1160             behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1161             behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1162         }
1163         else
1164         {
1165             behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1166             behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1167             if (style & NSWindowStyleMaskFullScreen)
1168                 [super toggleFullScreen:nil];
1169         }
1171         if (behavior != [self collectionBehavior])
1172         {
1173             [self setCollectionBehavior:behavior];
1174             [self adjustFeaturesForState];
1175         }
1176     }
1178     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1179     {
1180         static const NSUInteger usedStyles = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable |
1181                                              NSWindowStyleMaskResizable | NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskBorderless |
1182                                              NSWindowStyleMaskNonactivatingPanel;
1183         NSUInteger currentStyle = [self styleMask];
1184         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1186         self.preventsAppActivation = wf->prevents_app_activation;
1188         if (newStyle != currentStyle)
1189         {
1190             NSString* title = [[[self title] copy] autorelease];
1191             BOOL showingButtons = (currentStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1192             BOOL shouldShowButtons = (newStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1193             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSWindowStyleMaskClosable))
1194             {
1195                 // -setStyleMask: is buggy on 10.7+ with respect to NSWindowStyleMaskResizable.
1196                 // If transitioning from NSWindowStyleMaskTitled | NSWindowStyleMaskResizable to
1197                 // just NSWindowStyleMaskTitled, the window buttons should disappear rather
1198                 // than just being disabled.  But they don't.  Similarly in reverse.
1199                 // The workaround is to also toggle NSWindowStyleMaskClosable at the same time.
1200                 [self setStyleMask:newStyle ^ NSWindowStyleMaskClosable];
1201             }
1202             [self setStyleMask:newStyle];
1204             BOOL isNonActivating = (currentStyle & NSWindowStyleMaskNonactivatingPanel) != 0;
1205             BOOL shouldBeNonActivating = (newStyle & NSWindowStyleMaskNonactivatingPanel) != 0;
1206             if (isNonActivating != shouldBeNonActivating) {
1207                 // Changing NSWindowStyleMaskNonactivatingPanel with -setStyleMask is also
1208                 // buggy. If it's added, clicking the title bar will still activate the
1209                 // app. If it's removed, nothing changes at all.
1210                 // This private method ensures the correct behavior.
1211                 if ([self respondsToSelector:@selector(_setPreventsActivation:)])
1212                     [self _setPreventsActivation:shouldBeNonActivating];
1213             }
1215             // -setStyleMask: resets the firstResponder to the window.  Set it
1216             // back to the content view.
1217             if ([[self contentView] acceptsFirstResponder])
1218                 [self makeFirstResponder:[self contentView]];
1220             [self adjustFullScreenBehavior:[self collectionBehavior]];
1222             if ([[self title] length] == 0 && [title length] > 0)
1223                 [self setTitle:title];
1224         }
1226         resizable = wf->resizable;
1227         [self adjustFeaturesForState];
1228         [self setHasShadow:wf->shadow];
1229     }
1231     // Indicates if the window would be visible if the app were not hidden.
1232     - (BOOL) wouldBeVisible
1233     {
1234         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1235     }
1237     - (BOOL) isOrderedIn
1238     {
1239         return [self wouldBeVisible] || [self isMiniaturized];
1240     }
1242     - (NSInteger) minimumLevelForActive:(BOOL)active
1243     {
1244         NSInteger level;
1246         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1247                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1248             level = NSFloatingWindowLevel;
1249         else
1250             level = NSNormalWindowLevel;
1252         if (active)
1253         {
1254             BOOL captured;
1256             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1258             if (captured || fullscreen)
1259             {
1260                 if (captured)
1261                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1262                 else
1263                     level = NSStatusWindowLevel + 1;
1265                 if (self.floating)
1266                     level++;
1267             }
1268         }
1270         return level;
1271     }
1273     - (void) postDidUnminimizeEvent
1274     {
1275         macdrv_event* event;
1277         /* Coalesce events by discarding any previous ones still in the queue. */
1278         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1279                                forWindow:self];
1281         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1282         [queue postEvent:event];
1283         macdrv_release_event(event);
1284     }
1286     - (void) sendResizeStartQuery
1287     {
1288         macdrv_query* query = macdrv_create_query();
1289         query->type = QUERY_RESIZE_START;
1290         query->window = (macdrv_window)[self retain];
1292         [self.queue query:query timeout:0.3];
1293         macdrv_release_query(query);
1294     }
1296     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1297     {
1298         NSWindowCollectionBehavior behavior;
1300         self.disabled = state->disabled;
1301         self.noForeground = state->no_foreground;
1303         if (self.floating != state->floating)
1304         {
1305             self.floating = state->floating;
1306             if (state->floating)
1307             {
1308                 // Became floating.  If child of non-floating window, make that
1309                 // relationship latent.
1310                 WineWindow* parent = (WineWindow*)[self parentWindow];
1311                 if (parent && !parent.floating)
1312                     [self becameIneligibleChild];
1313             }
1314             else
1315             {
1316                 // Became non-floating.  If parent of floating children, make that
1317                 // relationship latent.
1318                 WineWindow* child;
1319                 for (child in [self childWineWindows])
1320                 {
1321                     if (child.floating)
1322                         [child becameIneligibleChild];
1323                 }
1324             }
1326             // Check our latent relationships.  If floating status was the only
1327             // reason they were latent, then make them active.
1328             if ([self isVisible])
1329                 [self becameEligibleParentOrChild];
1331             [[WineApplicationController sharedController] adjustWindowLevels];
1332         }
1334         if (state->minimized_valid)
1335         {
1336             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1338             pendingMinimize = FALSE;
1339             if (state->minimized && ![self isMiniaturized])
1340             {
1341                 if ([self wouldBeVisible])
1342                 {
1343                     if (([self styleMask] & NSWindowStyleMaskFullScreen) || stage_manager_enabled())
1344                     {
1345                         [self postDidUnminimizeEvent];
1346                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1348                         /* When Stage Manager is enabled, it's not possible to minimize the window
1349                          * (miniaturize: just moves the window to the background).
1350                          * Post an unminimize event, then miniaturize:.
1351                          */
1352                         if (stage_manager_enabled())
1353                         {
1354                             [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1355                             [super miniaturize:nil];
1356                         }
1357                     }
1358                     else
1359                     {
1360                         [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1361                         [super miniaturize:nil];
1362                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1363                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1364                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1365                     }
1366                 }
1367                 else
1368                     pendingMinimize = TRUE;
1369             }
1370             else if (!state->minimized && [self isMiniaturized])
1371             {
1372                 ignore_windowDeminiaturize = TRUE;
1373                 [self deminiaturize:nil];
1374                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1375             }
1377             if (discard)
1378                 [queue discardEventsMatchingMask:discard forWindow:self];
1379         }
1381         if (state->maximized != maximized)
1382         {
1383             maximized = state->maximized;
1384             [self adjustFeaturesForState];
1386             if (!maximized && [self inLiveResize])
1387                 [self sendResizeStartQuery];
1388         }
1390         behavior = NSWindowCollectionBehaviorDefault;
1391         if (state->excluded_by_expose)
1392             behavior |= NSWindowCollectionBehaviorTransient;
1393         else
1394             behavior |= NSWindowCollectionBehaviorManaged;
1395         if (state->excluded_by_cycle)
1396         {
1397             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1398             if ([self isOrderedIn])
1399                 [NSApp removeWindowsItem:self];
1400         }
1401         else
1402         {
1403             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1404             if ([self isOrderedIn])
1405                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1406         }
1407         [self adjustFullScreenBehavior:behavior];
1408     }
1410     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1411     {
1412         BOOL reordered = FALSE;
1414         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1415         {
1416             if ([self level] > [child level])
1417                 [child setLevel:[self level]];
1418             if (![child isVisible])
1419                 [child setAutodisplay:YES];
1420             [self addChildWindow:child ordered:NSWindowAbove];
1421             [child checkWineDisplayLink];
1422             [latentChildWindows removeObjectIdenticalTo:child];
1423             child.latentParentWindow = nil;
1424             reordered = TRUE;
1425         }
1426         else
1427         {
1428             if (!latentChildWindows)
1429                 latentChildWindows = [[NSMutableArray alloc] init];
1430             if (![latentChildWindows containsObject:child])
1431                 [latentChildWindows addObject:child];
1432             child.latentParentWindow = self;
1433         }
1435         return reordered;
1436     }
1438     - (BOOL) addChildWineWindow:(WineWindow*)child
1439     {
1440         return [self addChildWineWindow:child assumeVisible:FALSE];
1441     }
1443     - (void) removeChildWineWindow:(WineWindow*)child
1444     {
1445         [self removeChildWindow:child];
1446         if (child.latentParentWindow == self)
1447             child.latentParentWindow = nil;
1448         [latentChildWindows removeObjectIdenticalTo:child];
1449     }
1451     - (void) setChildWineWindows:(NSArray*)childWindows
1452     {
1453         NSArray* origChildren;
1454         NSUInteger count, start, limit, i;
1456         origChildren = self.childWineWindows;
1458         // If the current and desired children arrays match up to a point, leave
1459         // those matching children alone.
1460         count = childWindows.count;
1461         limit = MIN(origChildren.count, count);
1462         for (start = 0; start < limit; start++)
1463         {
1464             if (origChildren[start] != childWindows[start])
1465                 break;
1466         }
1468         // Remove all of the child windows and re-add them back-to-front so they
1469         // are in the desired order.
1470         for (i = start; i < count; i++)
1471         {
1472             WineWindow* child = childWindows[i];
1473             [self removeChildWindow:child];
1474         }
1475         for (i = start; i < count; i++)
1476         {
1477             WineWindow* child = childWindows[i];
1478             [self addChildWindow:child ordered:NSWindowAbove];
1479         }
1480     }
1482     static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1483     {
1484         NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1485         NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1486         NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1487         NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1488         if (index1 == NSNotFound)
1489         {
1490             if (index2 == NSNotFound)
1491                 return NSOrderedSame;
1492             else
1493                 return NSOrderedAscending;
1494         }
1495         else if (index2 == NSNotFound)
1496             return NSOrderedDescending;
1497         else if (index1 < index2)
1498             return NSOrderedDescending;
1499         else if (index2 < index1)
1500             return NSOrderedAscending;
1502         return NSOrderedSame;
1503     }
1505     - (BOOL) becameEligibleParentOrChild
1506     {
1507         BOOL reordered = FALSE;
1508         NSUInteger count;
1510         if (latentParentWindow.floating || !self.floating)
1511         {
1512             // If we aren't visible currently, we assume that we should be and soon
1513             // will be.  So, if the latent parent is visible that's enough to assume
1514             // we can establish the parent-child relationship in Cocoa.  That will
1515             // actually make us visible, which is fine.
1516             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1517                 reordered = TRUE;
1518         }
1520         // Here, though, we may not actually be visible yet and adding a child
1521         // won't make us visible.  The caller will have to call this method
1522         // again after actually making us visible.
1523         if ([self isVisible] && (count = [latentChildWindows count]))
1524         {
1525             NSMutableArray* windowNumbers;
1526             NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1527             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1528             NSUInteger i;
1530             windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1532             for (i = 0; i < count; i++)
1533             {
1534                 WineWindow* child = latentChildWindows[i];
1535                 if ([child isVisible] && (self.floating || !child.floating))
1536                 {
1537                     if (child.latentParentWindow == self)
1538                     {
1539                         if ([self level] > [child level])
1540                             [child setLevel:[self level]];
1541                         [childWindows addObject:child];
1542                         child.latentParentWindow = nil;
1543                         reordered = TRUE;
1544                     }
1545                     else
1546                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1547                     [indexesToRemove addIndex:i];
1548                 }
1549             }
1551             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1553             [childWindows sortWithOptions:NSSortStable
1554                           usingComparator:^NSComparisonResult(id obj1, id obj2){
1555                 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1556             }];
1557             [self setChildWineWindows:childWindows];
1558         }
1560         return reordered;
1561     }
1563     - (void) becameIneligibleChild
1564     {
1565         WineWindow* parent = (WineWindow*)[self parentWindow];
1566         if (parent)
1567         {
1568             if (!parent->latentChildWindows)
1569                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1570             [parent->latentChildWindows insertObject:self atIndex:0];
1571             self.latentParentWindow = parent;
1572             [parent removeChildWindow:self];
1573         }
1574     }
1576     - (void) becameIneligibleParentOrChild
1577     {
1578         NSArray* childWindows = [self childWineWindows];
1580         [self becameIneligibleChild];
1582         if ([childWindows count])
1583         {
1584             WineWindow* child;
1586             for (child in childWindows)
1587             {
1588                 child.latentParentWindow = self;
1589                 [self removeChildWindow:child];
1590             }
1592             if (latentChildWindows)
1593                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1594             else
1595                 latentChildWindows = [childWindows mutableCopy];
1596         }
1597     }
1599     // Determine if, among Wine windows, this window is directly above or below
1600     // a given other Wine window with no other Wine window intervening.
1601     // Intervening non-Wine windows are ignored.
1602     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1603     {
1604         NSNumber* windowNumber;
1605         NSNumber* otherWindowNumber;
1606         NSArray* windowNumbers;
1607         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1609         if (![self isVisible] || ![otherWindow isVisible])
1610             return FALSE;
1612         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1613         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1614         windowNumbers = [[self class] windowNumbersWithOptions:0];
1615         windowIndex = [windowNumbers indexOfObject:windowNumber];
1616         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1618         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1619             return FALSE;
1621         if (orderingMode == NSWindowAbove)
1622         {
1623             lowIndex = windowIndex;
1624             highIndex = otherWindowIndex;
1625         }
1626         else if (orderingMode == NSWindowBelow)
1627         {
1628             lowIndex = otherWindowIndex;
1629             highIndex = windowIndex;
1630         }
1631         else
1632             return FALSE;
1634         if (highIndex <= lowIndex)
1635             return FALSE;
1637         for (i = lowIndex + 1; i < highIndex; i++)
1638         {
1639             NSInteger interveningWindowNumber = [windowNumbers[i] integerValue];
1640             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1641             if ([interveningWindow isKindOfClass:[WineWindow class]])
1642                 return FALSE;
1643         }
1645         return TRUE;
1646     }
1648     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1649     {
1650         NSMutableArray* windowNumbers;
1651         NSNumber* childWindowNumber;
1652         NSUInteger otherIndex;
1653         NSArray* origChildren;
1654         NSMutableArray* children;
1656         // Get the z-order from the window server and modify it to reflect the
1657         // requested window ordering.
1658         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1659         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1660         [windowNumbers removeObject:childWindowNumber];
1661         if (other)
1662         {
1663             otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1664             [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1665         }
1666         else if (mode == NSWindowAbove)
1667             [windowNumbers insertObject:childWindowNumber atIndex:0];
1668         else
1669             [windowNumbers addObject:childWindowNumber];
1671         // Get our child windows and sort them in the reverse of the desired
1672         // z-order (back-to-front).
1673         origChildren = [self childWineWindows];
1674         children = [[origChildren mutableCopy] autorelease];
1675         [children sortWithOptions:NSSortStable
1676                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1677             return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1678         }];
1680         [self setChildWineWindows:children];
1681     }
1683     // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1684     // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1685     //      (non-nil, non-nil)  there is a level in the window tree where the two windows have sibling ancestors
1686     //                          if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1687     //                          otherwise, the two ancestors are each roots of disjoint window trees
1688     //      (nil, non-nil)      the other window is a descendent of self and *ancestorOfOther is the direct child
1689     //      (non-nil, nil)      self is a descendent of other and *ancestor is the direct child
1690     - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1691     {
1692         NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1693         WineWindow* child;
1694         WineWindow* parent;
1695         for (child = other;
1696              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1697              child = parent)
1698         {
1699             if (parent == self)
1700             {
1701                 *ancestor = nil;
1702                 *ancestorOfOther = child;
1703                 return;
1704             }
1706             [otherAncestors addObject:parent];
1707         }
1709         for (child = self;
1710              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1711              child = parent)
1712         {
1713             NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1714             if (index != NSNotFound)
1715             {
1716                 *ancestor = child;
1717                 if (index == 0)
1718                     *ancestorOfOther = nil;
1719                 else
1720                     *ancestorOfOther = otherAncestors[index - 1];
1721                 return;
1722             }
1723         }
1725         *ancestor = child;
1726         *ancestorOfOther = otherAncestors.lastObject;;
1727     }
1729     /* Returns whether or not the window was ordered in, which depends on if
1730        its frame intersects any screen. */
1731     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1732     {
1733         WineApplicationController* controller = [WineApplicationController sharedController];
1734         if (![self isMiniaturized])
1735         {
1736             BOOL needAdjustWindowLevels = FALSE;
1737             BOOL wasVisible;
1738             WineWindow* parent;
1739             WineWindow* child;
1741             [controller transformProcessToForeground:!self.preventsAppActivation];
1742             if ([NSApp isHidden])
1743                 [NSApp unhide:nil];
1744             wasVisible = [self isVisible];
1746             if (activate)
1747                 [controller tryToActivateIgnoringOtherApps:YES];
1749             NSDisableScreenUpdates();
1751             if ([self becameEligibleParentOrChild])
1752                 needAdjustWindowLevels = TRUE;
1754             if (prev || next)
1755             {
1756                 WineWindow* other = [prev isVisible] ? prev : next;
1757                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1759                 if (![self isOrdered:orderingMode relativeTo:other])
1760                 {
1761                     WineWindow* ancestor;
1762                     WineWindow* ancestorOfOther;
1764                     [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1765                     if (ancestor)
1766                     {
1767                         [self setAutodisplay:YES];
1768                         if (ancestorOfOther)
1769                         {
1770                             // This window level may not be right for this window based
1771                             // on floating-ness, fullscreen-ness, etc.  But we set it
1772                             // temporarily to allow us to order the windows properly.
1773                             // Then the levels get fixed by -adjustWindowLevels.
1774                             if ([ancestor level] != [ancestorOfOther level])
1775                                 [ancestor setLevel:[ancestorOfOther level]];
1777                             parent = (WineWindow*)ancestor.parentWindow;
1778                             if ([parent isKindOfClass:[WineWindow class]])
1779                                 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1780                             else
1781                                 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1782                         }
1784                         if (!ancestorOfOther || ancestor != self)
1785                         {
1786                             for (child = self;
1787                                  (parent = (WineWindow*)child.parentWindow);
1788                                  child = parent)
1789                             {
1790                                 if ([parent isKindOfClass:[WineWindow class]])
1791                                     [parent order:-orderingMode childWindow:child relativeTo:nil];
1792                                 if (parent == ancestor)
1793                                     break;
1794                             }
1795                         }
1797                         [self checkWineDisplayLink];
1798                         needAdjustWindowLevels = TRUE;
1799                     }
1800                 }
1801             }
1802             else
1803             {
1804                 for (child = self;
1805                      (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1806                      child = parent)
1807                 {
1808                     [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1809                 }
1811                 // Again, temporarily set level to make sure we can order to
1812                 // the right place.
1813                 next = [controller frontWineWindow];
1814                 if (next && [self level] < [next level])
1815                     [self setLevel:[next level]];
1816                 [self setAutodisplay:YES];
1817                 [self orderFront:nil];
1818                 [self checkWineDisplayLink];
1819                 needAdjustWindowLevels = TRUE;
1820             }
1821             pendingOrderOut = FALSE;
1823             if ([self becameEligibleParentOrChild])
1824                 needAdjustWindowLevels = TRUE;
1826             if (needAdjustWindowLevels)
1827             {
1828                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1829                     [controller updateFullscreenWindows];
1830                 [controller adjustWindowLevels];
1831             }
1833             if (pendingMinimize)
1834             {
1835                 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1836                 [super miniaturize:nil];
1837                 pendingMinimize = FALSE;
1838             }
1840             NSEnableScreenUpdates();
1842             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1843                Generate a frame-changed event just in case.  The back end will ignore
1844                it if nothing actually changed. */
1845             [self windowDidResize:nil skipSizeMove:TRUE];
1847             if (![self isExcludedFromWindowsMenu])
1848                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1849         }
1850     }
1852     - (void) doOrderOut
1853     {
1854         WineApplicationController* controller = [WineApplicationController sharedController];
1855         BOOL wasVisible = [self isVisible];
1856         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1858         [self endWindowDragging];
1859         [controller windowWillOrderOut:self];
1861         if (enteringFullScreen || exitingFullScreen)
1862         {
1863             pendingOrderOut = TRUE;
1864             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1865                                              event_mask_for_type(WINDOW_GOT_FOCUS) |
1866                                              event_mask_for_type(WINDOW_LOST_FOCUS) |
1867                                              event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1868                                              event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1869                                              event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1870                                    forWindow:self];
1871             return;
1872         }
1874         pendingOrderOut = FALSE;
1876         if ([self isMiniaturized])
1877             pendingMinimize = TRUE;
1879         WineWindow* parent = (WineWindow*)self.parentWindow;
1880         if ([parent isKindOfClass:[WineWindow class]])
1881             [parent grabDockIconSnapshotFromWindow:self force:NO];
1883         [self becameIneligibleParentOrChild];
1884         if ([self isMiniaturized] || [self styleMask] & NSWindowStyleMaskFullScreen)
1885         {
1886             fakingClose = TRUE;
1887             [self close];
1888             fakingClose = FALSE;
1889         }
1890         else
1891             [self orderOut:nil];
1892         [self checkWineDisplayLink];
1893         [self setBackgroundColor:[NSColor clearColor]];
1894         [self setOpaque:NO];
1895         drawnSinceShown = NO;
1896         savedVisibleState = FALSE;
1897         if (wasVisible && wasOnActiveSpace && fullscreen)
1898             [controller updateFullscreenWindows];
1899         [controller adjustWindowLevels];
1900         [NSApp removeWindowsItem:self];
1902         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1903                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1904                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1905                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1906                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1907                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1908                                forWindow:self];
1909     }
1911     - (void) updateFullscreen
1912     {
1913         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1914         BOOL nowFullscreen = !([self styleMask] & NSWindowStyleMaskFullScreen) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1916         if (nowFullscreen != fullscreen)
1917         {
1918             WineApplicationController* controller = [WineApplicationController sharedController];
1920             fullscreen = nowFullscreen;
1921             if ([self isVisible] && [self isOnActiveSpace])
1922                 [controller updateFullscreenWindows];
1924             [controller adjustWindowLevels];
1925         }
1926     }
1928     - (void) setFrameAndWineFrame:(NSRect)frame
1929     {
1930         [self setFrame:frame display:YES];
1932         wineFrame = frame;
1933         roundedWineFrame = self.frame;
1934         CGFloat junk;
1935 #if CGFLOAT_IS_DOUBLE
1936         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1937              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1938             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1939             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1940             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1941             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1942             roundedWineFrame = wineFrame;
1943 #else
1944         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1945              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1946             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1947             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1948             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1949             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1950             roundedWineFrame = wineFrame;
1951 #endif
1952     }
1954     - (void) setFrameFromWine:(NSRect)contentRect
1955     {
1956         /* Origin is (left, top) in a top-down space.  Need to convert it to
1957            (left, bottom) in a bottom-up space. */
1958         [[WineApplicationController sharedController] flipRect:&contentRect];
1960         /* The back end is establishing a new window size and position.  It's
1961            not interested in any stale events regarding those that may be sitting
1962            in the queue. */
1963         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1964                                forWindow:self];
1966         if (!NSIsEmptyRect(contentRect))
1967         {
1968             NSRect frame, oldFrame;
1970             oldFrame = self.wine_fractionalFrame;
1971             frame = [self frameRectForContentRect:contentRect];
1972             if (!NSEqualRects(frame, oldFrame))
1973             {
1974                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1975                 BOOL needEnableScreenUpdates = FALSE;
1977                 if ([self preventResizing])
1978                 {
1979                     // Allow the following calls to -setFrame:display: to work even
1980                     // if they would violate the content size constraints. This
1981                     // shouldn't be necessary since the content size constraints are
1982                     // documented to not constrain that method, but it seems to be.
1983                     [self setContentMinSize:NSZeroSize];
1984                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1985                 }
1987                 if (equalSizes && [[self childWineWindows] count])
1988                 {
1989                     // If we change the window frame such that the origin moves
1990                     // but the size doesn't change, then Cocoa moves child
1991                     // windows with the parent.  We don't want that so we fake
1992                     // a change of the size and then change it back.
1993                     NSRect bogusFrame = frame;
1994                     bogusFrame.size.width++;
1996                     NSDisableScreenUpdates();
1997                     needEnableScreenUpdates = TRUE;
1999                     ignore_windowResize = TRUE;
2000                     [self setFrame:bogusFrame display:NO];
2001                     ignore_windowResize = FALSE;
2002                 }
2004                 [self setFrameAndWineFrame:frame];
2005                 if ([self preventResizing])
2006                 {
2007                     [self setContentMinSize:contentRect.size];
2008                     [self setContentMaxSize:contentRect.size];
2009                 }
2011                 if (needEnableScreenUpdates)
2012                     NSEnableScreenUpdates();
2014                 if (!enteringFullScreen &&
2015                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
2016                     nonFullscreenFrame = frame;
2018                 [self updateFullscreen];
2020                 if ([self isOrderedIn])
2021                 {
2022                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
2023                        event.  The back end will ignore it if nothing actually changed. */
2024                     [self windowDidResize:nil skipSizeMove:TRUE];
2025                 }
2026             }
2027         }
2028     }
2030     - (NSRect) wine_fractionalFrame
2031     {
2032         NSRect frame = self.frame;
2033         if (NSEqualRects(frame, roundedWineFrame))
2034             frame = wineFrame;
2035         return frame;
2036     }
2038     - (void) setMacDrvParentWindow:(WineWindow*)parent
2039     {
2040         WineWindow* oldParent = (WineWindow*)[self parentWindow];
2041         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
2042         {
2043             [oldParent removeChildWineWindow:self];
2044             [latentParentWindow removeChildWineWindow:self];
2045             if ([parent addChildWineWindow:self])
2046                 [[WineApplicationController sharedController] adjustWindowLevels];
2047             [self adjustFullScreenBehavior:[self collectionBehavior]];
2048         }
2049     }
2051     - (void) setDisabled:(BOOL)newValue
2052     {
2053         if (disabled != newValue)
2054         {
2055             disabled = newValue;
2056             [self adjustFeaturesForState];
2057         }
2058     }
2060     - (BOOL) needsTransparency
2061     {
2062         return self.contentView.layer.mask || self.colorKeyed || self.usePerPixelAlpha ||
2063                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
2064     }
2066     - (void) checkTransparency
2067     {
2068         if (![self isOpaque] && !self.needsTransparency)
2069         {
2070             self.shapeChangedSinceLastDraw = TRUE;
2071             [[self contentView] setNeedsDisplay:YES];
2072             [self setBackgroundColor:[NSColor windowBackgroundColor]];
2073             [self setOpaque:YES];
2074         }
2075         else if ([self isOpaque] && self.needsTransparency)
2076         {
2077             self.shapeChangedSinceLastDraw = TRUE;
2078             [[self contentView] setNeedsDisplay:YES];
2079             [self setBackgroundColor:[NSColor clearColor]];
2080             [self setOpaque:NO];
2081         }
2082     }
2084     - (void) setShape:(CGPathRef)newShape
2085     {
2086         CALayer* layer = [[self contentView] layer];
2087         CAShapeLayer* mask = (CAShapeLayer*)layer.mask;
2088         if (CGPathEqualToPath(newShape, mask.path)) return;
2090         if (newShape && !layer.mask)
2091             layer.mask = mask = [CAShapeLayer layer];
2092         else if (!newShape)
2093             layer.mask = mask = nil;
2095         if (mask.path)
2096             [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(mask.path))];
2097         if (newShape)
2098             [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(newShape))];
2100         mask.path = newShape;
2101         self.shapeChangedSinceLastDraw = TRUE;
2103         [self checkTransparency];
2104         [self checkEmptyShaped];
2105     }
2107     - (void) makeFocused:(BOOL)activate
2108     {
2109         if (activate)
2110         {
2111             WineApplicationController *controller = [WineApplicationController sharedController];
2112             [controller transformProcessToForeground:YES];
2113             [controller tryToActivateIgnoringOtherApps:YES];
2114         }
2116         causing_becomeKeyWindow = self;
2117         [self makeKeyWindow];
2118         causing_becomeKeyWindow = nil;
2120         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2121                                          event_mask_for_type(WINDOW_LOST_FOCUS)
2122                                forWindow:self];
2123     }
2125     - (void) postKey:(uint16_t)keyCode
2126              pressed:(BOOL)pressed
2127            modifiers:(NSUInteger)modifiers
2128                event:(NSEvent*)theEvent
2129     {
2130         macdrv_event* event;
2131         CGEventRef cgevent;
2132         WineApplicationController* controller = [WineApplicationController sharedController];
2134         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2135         event->key.keycode   = keyCode;
2136         event->key.modifiers = modifiers;
2137         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
2139         if ((cgevent = [theEvent CGEvent]))
2140             controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2142         [queue postEvent:event];
2144         macdrv_release_event(event);
2146         [controller noteKey:keyCode pressed:pressed];
2147     }
2149     - (void) postKeyEvent:(NSEvent *)theEvent
2150     {
2151         [self flagsChanged:theEvent];
2152         [self postKey:[theEvent keyCode]
2153               pressed:[theEvent type] == NSEventTypeKeyDown
2154             modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2155                 event:theEvent];
2156     }
2158     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2159     {
2160         savedContentMinSize = minSize;
2161         savedContentMaxSize = maxSize;
2162         if (![self preventResizing])
2163         {
2164             [self setContentMinSize:minSize];
2165             [self setContentMaxSize:maxSize];
2166         }
2167     }
2169     - (WineWindow*) ancestorWineWindow
2170     {
2171         WineWindow* ancestor = self;
2172         for (;;)
2173         {
2174             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2175             if ([parent isKindOfClass:[WineWindow class]])
2176                 ancestor = parent;
2177             else
2178                 break;
2179         }
2180         return ancestor;
2181     }
2183     - (void) postBroughtForwardEvent
2184     {
2185         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2186         [queue postEvent:event];
2187         macdrv_release_event(event);
2188     }
2190     - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing skipSizeMove:(BOOL)skipSizeMove
2191     {
2192         macdrv_event* event;
2193         NSUInteger style = self.styleMask;
2195         if (isFullscreen)
2196             style |= NSWindowStyleMaskFullScreen;
2197         else
2198             style &= ~NSWindowStyleMaskFullScreen;
2199         frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2200         [[WineApplicationController sharedController] flipRect:&frame];
2202         /* Coalesce events by discarding any previous ones still in the queue. */
2203         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2204                                forWindow:self];
2206         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2207         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2208         event->window_frame_changed.fullscreen = isFullscreen;
2209         event->window_frame_changed.in_resize = resizing;
2210         event->window_frame_changed.skip_size_move_loop = skipSizeMove;
2211         [queue postEvent:event];
2212         macdrv_release_event(event);
2213     }
2215     - (void) updateForCursorClipping
2216     {
2217         [self adjustFeaturesForState];
2218     }
2220     - (void) endWindowDragging
2221     {
2222         if (draggingPhase)
2223         {
2224             if (draggingPhase == 3)
2225             {
2226                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2227                 [queue postEvent:event];
2228                 macdrv_release_event(event);
2229             }
2231             draggingPhase = 0;
2232             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2233         }
2234     }
2236     - (NSMutableDictionary*) displayIDToDisplayLinkMap
2237     {
2238         static NSMutableDictionary* displayIDToDisplayLinkMap;
2239         if (!displayIDToDisplayLinkMap)
2240         {
2241             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2243             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2244                                                               object:NSApp
2245                                                                queue:nil
2246                                                           usingBlock:^(NSNotification *note){
2247                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2248                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2249                 [badDisplayIDs minusSet:validDisplayIDs];
2250                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2251             }];
2252         }
2253         return displayIDToDisplayLinkMap;
2254     }
2256     - (WineDisplayLink*) wineDisplayLink
2257     {
2258         if (!_lastDisplayID)
2259             return nil;
2261         return [self displayIDToDisplayLinkMap][@(_lastDisplayID)];
2262     }
2264     - (void) checkWineDisplayLink
2265     {
2266         NSScreen* screen = self.screen;
2267         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2268             screen = nil;
2269 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2270         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2271             screen = nil;
2272 #endif
2274         NSNumber* displayIDNumber = screen.deviceDescription[@"NSScreenNumber"];
2275         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2276         if (displayID == _lastDisplayID)
2277             return;
2279         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2281         if (_lastDisplayID)
2282         {
2283             WineDisplayLink* link = displayIDToDisplayLinkMap[@(_lastDisplayID)];
2284             [link removeWindow:self];
2285         }
2286         if (displayID)
2287         {
2288             WineDisplayLink* link = displayIDToDisplayLinkMap[displayIDNumber];
2289             if (!link)
2290             {
2291                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2292                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2293             }
2294             [link addWindow:self];
2295             [self displayIfNeeded];
2296         }
2297         _lastDisplayID = displayID;
2298     }
2300     - (BOOL) isEmptyShaped
2301     {
2302         CAShapeLayer* mask = (CAShapeLayer*)[[self contentView] layer].mask;
2303         return ([mask isEmptyShaped]);
2304     }
2306     - (BOOL) canProvideSnapshot
2307     {
2308         return (self.windowNumber > 0 && ![self isEmptyShaped]);
2309     }
2311     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2312     {
2313         if (![self isEmptyShaped])
2314             return;
2316         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2317         if (!force && now < lastDockIconSnapshot + 1)
2318             return;
2320         if (window)
2321         {
2322             if (![window canProvideSnapshot])
2323                 return;
2324         }
2325         else
2326         {
2327             CGFloat bestArea;
2328             for (WineWindow* childWindow in self.childWindows)
2329             {
2330                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2331                     continue;
2333                 NSSize size = childWindow.frame.size;
2334                 CGFloat area = size.width * size.height;
2335                 if (!window || area > bestArea)
2336                 {
2337                     window = childWindow;
2338                     bestArea = area;
2339                 }
2340             }
2342             if (!window)
2343                 return;
2344         }
2346         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2347         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2348         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2349         CFRelease(windowIDs);
2350         if (!windowImage)
2351             return;
2353         NSImage* appImage = [NSApp applicationIconImage];
2354         if (!appImage)
2355             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2357         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2358         [dockIcon lockFocus];
2360         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2362         CGRect rect = CGRectMake(8, 8, 240, 240);
2363         size_t width = CGImageGetWidth(windowImage);
2364         size_t height = CGImageGetHeight(windowImage);
2365         if (width > height)
2366         {
2367             rect.size.height *= height / (double)width;
2368             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2369         }
2370         else if (width != height)
2371         {
2372             rect.size.width *= width / (double)height;
2373             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2374         }
2376         CGContextDrawImage(cgcontext, rect, windowImage);
2377         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2378                     fromRect:NSZeroRect
2379                    operation:NSCompositingOperationSourceOver
2380                     fraction:1
2381               respectFlipped:YES
2382                        hints:nil];
2384         [dockIcon unlockFocus];
2386         CGImageRelease(windowImage);
2388         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2389         if (![imageView isKindOfClass:[NSImageView class]])
2390         {
2391             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2392             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2393             self.dockTile.contentView = imageView;
2394         }
2395         imageView.image = dockIcon;
2396         [self.dockTile display];
2397         lastDockIconSnapshot = now;
2398     }
2400     - (void) checkEmptyShaped
2401     {
2402         if (self.dockTile.contentView && ![self isEmptyShaped])
2403         {
2404             self.dockTile.contentView = nil;
2405             lastDockIconSnapshot = 0;
2406         }
2407         [self checkWineDisplayLink];
2408     }
2411     /*
2412      * ---------- NSWindow method overrides ----------
2413      */
2414     - (BOOL) canBecomeKeyWindow
2415     {
2416         if (causing_becomeKeyWindow == self) return YES;
2417         if (self.disabled || self.noForeground) return NO;
2418         if ([self isKeyWindow]) return YES;
2420         // If a window's collectionBehavior says it participates in cycling,
2421         // it must return YES from this method to actually be eligible.
2422         return ![self isExcludedFromWindowsMenu];
2423     }
2425     - (BOOL) canBecomeMainWindow
2426     {
2427         return [self canBecomeKeyWindow];
2428     }
2430     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2431     {
2432         // If a window is sized to completely cover a screen, then it's in
2433         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2434         // it.
2435         NSArray* screens = [NSScreen screens];
2436         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2437         if (!screen_covered_by_rect(contentRect, screens) &&
2438             frame_intersects_screens(frameRect, screens))
2439             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2440         return frameRect;
2441     }
2443     // This private method of NSWindow is called as Cocoa reacts to the display
2444     // configuration changing.  Among other things, it adjusts the window's
2445     // frame based on how the screen(s) changed size.  That tells Wine that the
2446     // window has been moved.  We don't want that.  Rather, we want to make
2447     // sure that the WinAPI notion of the window position is maintained/
2448     // restored, possibly undoing or overriding Cocoa's adjustment.
2449     //
2450     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2451     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2452     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2453     // reassert its notion of the position.  That call won't get processed
2454     // until after this method returns, so it will override whatever this
2455     // method does to the window position.  It will also discard any pending
2456     // WINDOW_FRAME_CHANGED events.
2457     //
2458     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2459     // adjust the window's position due to a display change is to hook into
2460     // this private method.  This private method has remained stable from 10.6
2461     // through 10.11.  If it does change, the most likely thing is that it
2462     // will be removed and no longer called and this fix will simply stop
2463     // working.  The only real danger would be if Apple changed the return type
2464     // to a struct or floating-point type, which would change the calling
2465     // convention.
2466     - (id) _displayChanged
2467     {
2468         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2469         [queue postEvent:event];
2470         macdrv_release_event(event);
2472         return [super _displayChanged];
2473     }
2475     - (BOOL) isExcludedFromWindowsMenu
2476     {
2477         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2478     }
2480     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2481     {
2482         BOOL ret = [super validateMenuItem:menuItem];
2484         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2485             ret = [self isKeyWindow] || (!self.disabled && !self.noForeground);
2486         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2487             ret = NO;
2489         return ret;
2490     }
2492     /* We don't call this.  It's the action method of the items in the Window menu. */
2493     - (void) makeKeyAndOrderFront:(id)sender
2494     {
2495         if ([self isMiniaturized])
2496             [self deminiaturize:nil];
2497         [self orderBelow:nil orAbove:nil activate:NO];
2498         [[self ancestorWineWindow] postBroughtForwardEvent];
2500         if (![self isKeyWindow] && !self.disabled && !self.noForeground)
2501             [[WineApplicationController sharedController] windowGotFocus:self];
2502     }
2504     - (void) sendEvent:(NSEvent*)event
2505     {
2506         NSEventType type = event.type;
2508         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2509            interface control.  For example, Control-Tab switches focus among
2510            views.  We want to bypass that feature, so directly route key-down
2511            events to -keyDown:. */
2512         if (type == NSEventTypeKeyDown)
2513             [[self firstResponder] keyDown:event];
2514         else
2515         {
2516             if (!draggingPhase && maximized && ![self isMovable] &&
2517                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2518                 type == NSEventTypeLeftMouseDown && (self.styleMask & NSWindowStyleMaskTitled))
2519             {
2520                 NSRect titleBar = self.frame;
2521                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2522                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2523                 titleBar.origin.y = NSMaxY(contentRect);
2525                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2527                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2528                 {
2529                     static const NSWindowButton buttons[] = {
2530                         NSWindowCloseButton,
2531                         NSWindowMiniaturizeButton,
2532                         NSWindowZoomButton,
2533                         NSWindowFullScreenButton,
2534                     };
2535                     BOOL hitButton = NO;
2536                     int i;
2538                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2539                     {
2540                         NSButton* button = [self standardWindowButton:buttons[i]];
2541                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2542                         {
2543                             hitButton = YES;
2544                             break;
2545                         }
2546                     }
2548                     if (!hitButton)
2549                     {
2550                         draggingPhase = 1;
2551                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2552                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2553                     }
2554                 }
2555             }
2556             else if (draggingPhase && (type == NSEventTypeLeftMouseDragged || type == NSEventTypeLeftMouseUp))
2557             {
2558                 if ([self isMovable])
2559                 {
2560                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2561                     NSPoint newTopLeft = dragWindowStartPosition;
2563                     newTopLeft.x += point.x - dragStartPosition.x;
2564                     newTopLeft.y += point.y - dragStartPosition.y;
2566                     if (draggingPhase == 2)
2567                     {
2568                         macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2569                         mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2570                         [queue postEvent:mevent];
2571                         macdrv_release_event(mevent);
2573                         draggingPhase = 3;
2574                     }
2576                     [self setFrameTopLeftPoint:newTopLeft];
2577                 }
2578                 else if (draggingPhase == 1 && type == NSEventTypeLeftMouseDragged)
2579                 {
2580                     macdrv_event* event;
2581                     NSRect frame = [self contentRectForFrameRect:self.frame];
2583                     [[WineApplicationController sharedController] flipRect:&frame];
2585                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2586                     event->window_restore_requested.keep_frame = TRUE;
2587                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2588                     [queue postEvent:event];
2589                     macdrv_release_event(event);
2591                     draggingPhase = 2;
2592                 }
2594                 if (type == NSEventTypeLeftMouseUp)
2595                     [self endWindowDragging];
2596             }
2598             [super sendEvent:event];
2599         }
2600     }
2602     - (void) miniaturize:(id)sender
2603     {
2604         /* When Stage Manager is enabled, miniaturize: just moves the app/window to
2605          * the background rather than minimizing the window.
2606          * Don't start minimizing the window on the Win32 side.
2607          */
2608         if (stage_manager_enabled())
2609         {
2610             [super miniaturize:sender];
2611             return;
2612         }
2614         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2615         [queue postEvent:event];
2616         macdrv_release_event(event);
2618         WineWindow* parent = (WineWindow*)self.parentWindow;
2619         if ([parent isKindOfClass:[WineWindow class]])
2620             [parent grabDockIconSnapshotFromWindow:self force:YES];
2621     }
2623     - (void) toggleFullScreen:(id)sender
2624     {
2625         if (!self.disabled && !maximized)
2626             [super toggleFullScreen:sender];
2627     }
2629     - (void) setViewsNeedDisplay:(BOOL)value
2630     {
2631         if (value && ![self viewsNeedDisplay])
2632         {
2633             WineDisplayLink* link = [self wineDisplayLink];
2634             if (link)
2635             {
2636                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2637                 if (_lastDisplayTime + [link refreshPeriod] < now)
2638                     [self setAutodisplay:YES];
2639                 else
2640                 {
2641                     [link start];
2642                     _lastDisplayTime = now;
2643                 }
2644             }
2645             else
2646                 [self setAutodisplay:YES];
2647         }
2648         [super setViewsNeedDisplay:value];
2649     }
2651     - (void) display
2652     {
2653         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2654         [super display];
2655         if (_lastDisplayID)
2656             [self setAutodisplay:NO];
2657     }
2659     - (void) displayIfNeeded
2660     {
2661         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2662         [super displayIfNeeded];
2663         if (_lastDisplayID)
2664             [self setAutodisplay:NO];
2665     }
2667     - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2668     {
2669         if (flag)
2670             [self setAutodisplay:YES];
2671         [super setFrame:frameRect display:flag];
2672     }
2674     - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2675     {
2676         if (displayFlag)
2677             [self setAutodisplay:YES];
2678         [super setFrame:frameRect display:displayFlag animate:animateFlag];
2679     }
2681     - (void) windowDidDrawContent
2682     {
2683         if (!drawnSinceShown)
2684         {
2685             drawnSinceShown = YES;
2686             dispatch_async(dispatch_get_main_queue(), ^{
2687                 [self checkTransparency];
2688             });
2689         }
2690     }
2692     - (NSArray*) childWineWindows
2693     {
2694         NSArray* childWindows = self.childWindows;
2695         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2696             return [child isKindOfClass:[WineWindow class]];
2697         }];
2698         return [childWindows objectsAtIndexes:indexes];
2699     }
2701     - (void) updateForGLSubviews
2702     {
2703         if (gl_surface_mode == GL_SURFACE_BEHIND)
2704             [self checkTransparency];
2705     }
2707     - (void) setRetinaMode:(int)mode
2708     {
2709         NSRect frame;
2710         double scale = mode ? 0.5 : 2.0;
2711         NSAffineTransform* transform = [NSAffineTransform transform];
2713         [transform scaleBy:scale];
2715         [[self contentView] layer].mask.contentsScale = mode ? 2.0 : 1.0;
2717         for (WineBaseView* subview in [self.contentView subviews])
2718         {
2719             if ([subview isKindOfClass:[WineBaseView class]])
2720                 [subview setRetinaMode:mode];
2721         }
2723         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2724         frame.origin.x *= scale;
2725         frame.origin.y *= scale;
2726         frame.size.width *= scale;
2727         frame.size.height *= scale;
2728         frame = [self frameRectForContentRect:frame];
2730         savedContentMinSize = [transform transformSize:savedContentMinSize];
2731         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2732             savedContentMaxSize.width *= scale;
2733         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2734             savedContentMaxSize.height *= scale;
2736         self.contentMinSize = [transform transformSize:self.contentMinSize];
2737         NSSize temp = self.contentMaxSize;
2738         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2739             temp.width *= scale;
2740         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2741             temp.height *= scale;
2742         self.contentMaxSize = temp;
2744         ignore_windowResize = TRUE;
2745         [self setFrameAndWineFrame:frame];
2746         ignore_windowResize = FALSE;
2747     }
2750     /*
2751      * ---------- NSResponder method overrides ----------
2752      */
2753     - (void) keyDown:(NSEvent *)theEvent
2754     {
2755         if ([theEvent isARepeat])
2756         {
2757             if (!allowKeyRepeats)
2758                 return;
2759         }
2760         else
2761             allowKeyRepeats = YES;
2763         [self postKeyEvent:theEvent];
2764     }
2766     - (void) flagsChanged:(NSEvent *)theEvent
2767     {
2768         static const struct {
2769             NSUInteger  mask;
2770             uint16_t    keycode;
2771         } modifiers[] = {
2772             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2773             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2774             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2775             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2776             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2777             { NX_DEVICELALTKEYMASK,     kVK_Option },
2778             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2779             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2780             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2781         };
2783         NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2784         NSUInteger changed;
2785         int i, last_changed;
2787         fix_device_modifiers_by_generic(&modifierFlags);
2788         changed = modifierFlags ^ lastModifierFlags;
2790         last_changed = -1;
2791         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2792             if (changed & modifiers[i].mask)
2793                 last_changed = i;
2795         for (i = 0; i <= last_changed; i++)
2796         {
2797             if (changed & modifiers[i].mask)
2798             {
2799                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2801                 if (pressed)
2802                     allowKeyRepeats = NO;
2804                 if (i == last_changed)
2805                     lastModifierFlags = modifierFlags;
2806                 else
2807                 {
2808                     lastModifierFlags ^= modifiers[i].mask;
2809                     fix_generic_modifiers_by_device(&lastModifierFlags);
2810                 }
2812                 // Caps lock generates one event for each press-release action.
2813                 // We need to simulate a pair of events for each actual event.
2814                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2815                 {
2816                     [self postKey:modifiers[i].keycode
2817                           pressed:TRUE
2818                         modifiers:lastModifierFlags
2819                             event:(NSEvent*)theEvent];
2820                     pressed = FALSE;
2821                 }
2823                 [self postKey:modifiers[i].keycode
2824                       pressed:pressed
2825                     modifiers:lastModifierFlags
2826                         event:(NSEvent*)theEvent];
2827             }
2828         }
2829     }
2831     - (void) applicationWillHide
2832     {
2833         savedVisibleState = [self isVisible];
2834     }
2836     - (void) applicationDidUnhide
2837     {
2838         if ([self isVisible])
2839             [self becameEligibleParentOrChild];
2840     }
2843     /*
2844      * ---------- NSWindowDelegate methods ----------
2845      */
2846     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2847     {
2848         macdrv_query* query;
2849         NSSize size;
2851         query = macdrv_create_query();
2852         query->type = QUERY_MIN_MAX_INFO;
2853         query->window = (macdrv_window)[self retain];
2854         [self.queue query:query timeout:0.5];
2855         macdrv_release_query(query);
2857         size = [self contentMaxSize];
2858         if (proposedSize.width < size.width)
2859             size.width = proposedSize.width;
2860         if (proposedSize.height < size.height)
2861             size.height = proposedSize.height;
2862         return size;
2863     }
2865     - (void)windowDidBecomeKey:(NSNotification *)notification
2866     {
2867         WineApplicationController* controller = [WineApplicationController sharedController];
2868         NSEvent* event = [controller lastFlagsChanged];
2869         if (event)
2870             [self flagsChanged:event];
2872         if (causing_becomeKeyWindow == self) return;
2874         [controller windowGotFocus:self];
2875     }
2877     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2878     {
2879         [self checkWineDisplayLink];
2880     }
2882     - (void) windowDidChangeScreen:(NSNotification*)notification
2883     {
2884         [self checkWineDisplayLink];
2885     }
2887     - (void)windowDidDeminiaturize:(NSNotification *)notification
2888     {
2889         WineApplicationController* controller = [WineApplicationController sharedController];
2891         if (!ignore_windowDeminiaturize)
2892             [self postDidUnminimizeEvent];
2893         ignore_windowDeminiaturize = FALSE;
2895         [self becameEligibleParentOrChild];
2897         if (fullscreen && [self isOnActiveSpace])
2898             [controller updateFullscreenWindows];
2899         [controller adjustWindowLevels];
2901         if (![self parentWindow])
2902             [self postBroughtForwardEvent];
2904         if (!self.disabled && !self.noForeground)
2905         {
2906             causing_becomeKeyWindow = self;
2907             [self makeKeyWindow];
2908             causing_becomeKeyWindow = nil;
2909             [controller windowGotFocus:self];
2910         }
2912         [self windowDidResize:notification];
2913         [self checkWineDisplayLink];
2914     }
2916     - (void) windowDidEndLiveResize:(NSNotification *)notification
2917     {
2918         if (!maximized)
2919         {
2920             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2921             [queue postEvent:event];
2922             macdrv_release_event(event);
2923         }
2924     }
2926     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2927     {
2928         enteringFullScreen = FALSE;
2929         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2930         if (pendingOrderOut)
2931             [self doOrderOut];
2932     }
2934     - (void) windowDidExitFullScreen:(NSNotification*)notification
2935     {
2936         exitingFullScreen = FALSE;
2937         [self setFrameAndWineFrame:nonFullscreenFrame];
2938         [self windowDidResize:nil];
2939         if (pendingOrderOut)
2940             [self doOrderOut];
2941     }
2943     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2944     {
2945         enteringFullScreen = FALSE;
2946         enteredFullScreenTime = 0;
2947         if (pendingOrderOut)
2948             [self doOrderOut];
2949     }
2951     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2952     {
2953         exitingFullScreen = FALSE;
2954         [self windowDidResize:nil];
2955         if (pendingOrderOut)
2956             [self doOrderOut];
2957     }
2959     - (void)windowDidMiniaturize:(NSNotification *)notification
2960     {
2961         macdrv_event* event;
2963         if (fullscreen && [self isOnActiveSpace])
2964             [[WineApplicationController sharedController] updateFullscreenWindows];
2966         [self checkWineDisplayLink];
2968         event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
2969         [queue postEvent:event];
2970         macdrv_release_event(event);
2971     }
2973     - (void)windowDidMove:(NSNotification *)notification
2974     {
2975         [self windowDidResize:notification];
2976     }
2978     - (void)windowDidResignKey:(NSNotification *)notification
2979     {
2980         macdrv_event* event;
2982         if (causing_becomeKeyWindow) return;
2984         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2985         [queue postEvent:event];
2986         macdrv_release_event(event);
2987     }
2989     - (void)windowDidResize:(NSNotification *)notification skipSizeMove:(BOOL)skipSizeMove
2990     {
2991         NSRect frame = self.wine_fractionalFrame;
2993         if ([self inLiveResize])
2994         {
2995             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2996                 resizingFromLeft = TRUE;
2997             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2998                 resizingFromTop = TRUE;
2999         }
3001         if (ignore_windowResize || exitingFullScreen) return;
3003         if ([self preventResizing])
3004         {
3005             NSRect contentRect = [self contentRectForFrameRect:frame];
3006             [self setContentMinSize:contentRect.size];
3007             [self setContentMaxSize:contentRect.size];
3008         }
3010         [self postWindowFrameChanged:frame
3011                           fullscreen:([self styleMask] & NSWindowStyleMaskFullScreen) != 0
3012                             resizing:[self inLiveResize]
3013                         skipSizeMove:skipSizeMove];
3015         [[[self contentView] inputContext] invalidateCharacterCoordinates];
3016         [self updateFullscreen];
3017     }
3019     - (void)windowDidResize:(NSNotification *)notification
3020     {
3021         [self windowDidResize:notification skipSizeMove:FALSE];
3022     }
3024     - (BOOL)windowShouldClose:(id)sender
3025     {
3026         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
3027         [queue postEvent:event];
3028         macdrv_release_event(event);
3029         return NO;
3030     }
3032     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
3033     {
3034         if (maximized)
3035         {
3036             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3037             [queue postEvent:event];
3038             macdrv_release_event(event);
3039             return NO;
3040         }
3041         else if (!resizable)
3042         {
3043             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
3044             [queue postEvent:event];
3045             macdrv_release_event(event);
3046             return NO;
3047         }
3049         return YES;
3050     }
3052     - (void) windowWillClose:(NSNotification*)notification
3053     {
3054         WineWindow* child;
3056         if (fakingClose) return;
3057         if (latentParentWindow)
3058         {
3059             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
3060             self.latentParentWindow = nil;
3061         }
3063         for (child in latentChildWindows)
3064         {
3065             if (child.latentParentWindow == self)
3066                 child.latentParentWindow = nil;
3067         }
3068         [latentChildWindows removeAllObjects];
3069     }
3071     - (void) windowWillEnterFullScreen:(NSNotification*)notification
3072     {
3073         enteringFullScreen = TRUE;
3074         nonFullscreenFrame = self.wine_fractionalFrame;
3075     }
3077     - (void) windowWillExitFullScreen:(NSNotification*)notification
3078     {
3079         exitingFullScreen = TRUE;
3080         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE skipSizeMove:FALSE];
3081     }
3083     - (void)windowWillMiniaturize:(NSNotification *)notification
3084     {
3085         [self becameIneligibleParentOrChild];
3086         [self grabDockIconSnapshotFromWindow:nil force:NO];
3087     }
3089     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
3090     {
3091         if ([self inLiveResize])
3092         {
3093             if (maximized)
3094                 return self.wine_fractionalFrame.size;
3096             NSRect rect;
3097             macdrv_query* query;
3099             rect = [self frame];
3100             if (resizingFromLeft)
3101                 rect.origin.x = NSMaxX(rect) - frameSize.width;
3102             if (!resizingFromTop)
3103                 rect.origin.y = NSMaxY(rect) - frameSize.height;
3104             rect.size = frameSize;
3105             rect = [self contentRectForFrameRect:rect];
3106             [[WineApplicationController sharedController] flipRect:&rect];
3108             query = macdrv_create_query();
3109             query->type = QUERY_RESIZE_SIZE;
3110             query->window = (macdrv_window)[self retain];
3111             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3112             query->resize_size.from_left = resizingFromLeft;
3113             query->resize_size.from_top = resizingFromTop;
3115             if ([self.queue query:query timeout:0.1])
3116             {
3117                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3118                 rect = [self frameRectForContentRect:rect];
3119                 frameSize = rect.size;
3120             }
3122             macdrv_release_query(query);
3123         }
3125         return frameSize;
3126     }
3128     - (void) windowWillStartLiveResize:(NSNotification *)notification
3129     {
3130         [self endWindowDragging];
3132         if (maximized)
3133         {
3134             macdrv_event* event;
3135             NSRect frame = [self contentRectForFrameRect:self.frame];
3137             [[WineApplicationController sharedController] flipRect:&frame];
3139             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3140             event->window_restore_requested.keep_frame = TRUE;
3141             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3142             [queue postEvent:event];
3143             macdrv_release_event(event);
3144         }
3145         else
3146             [self sendResizeStartQuery];
3148         frameAtResizeStart = [self frame];
3149         resizingFromLeft = resizingFromTop = FALSE;
3150     }
3152     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3153     {
3154         macdrv_query* query;
3155         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3156         NSSize maxSize;
3158         query = macdrv_create_query();
3159         query->type = QUERY_MIN_MAX_INFO;
3160         query->window = (macdrv_window)[self retain];
3161         [self.queue query:query timeout:0.5];
3162         macdrv_release_query(query);
3164         currentContentRect = [self contentRectForFrameRect:[self frame]];
3165         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3167         maxSize = [self contentMaxSize];
3168         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3169         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3171         // Try to keep the top-left corner where it is.
3172         newContentRect.origin.x = NSMinX(currentContentRect);
3173         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3175         // If that pushes the bottom or right off the screen, pull it up and to the left.
3176         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3177         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3178             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3179         if (NSMinY(newContentRect) < NSMinY(screenRect))
3180             newContentRect.origin.y = NSMinY(screenRect);
3182         // If that pushes the top or left off the screen, push it down and the right
3183         // again.  Do this last because the top-left corner is more important than the
3184         // bottom-right.
3185         if (NSMinX(newContentRect) < NSMinX(screenRect))
3186             newContentRect.origin.x = NSMinX(screenRect);
3187         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3188             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3190         return [self frameRectForContentRect:newContentRect];
3191     }
3194     /*
3195      * ---------- NSPasteboardOwner methods ----------
3196      */
3197     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3198     {
3199         macdrv_query* query = macdrv_create_query();
3200         query->type = QUERY_PASTEBOARD_DATA;
3201         query->window = (macdrv_window)[self retain];
3202         query->pasteboard_data.type = (CFStringRef)[type copy];
3204         [self.queue query:query timeout:3];
3205         macdrv_release_query(query);
3206     }
3208     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3209     {
3210         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3211         [queue postEvent:event];
3212         macdrv_release_event(event);
3213     }
3216     /*
3217      * ---------- NSDraggingDestination methods ----------
3218      */
3219     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3220     {
3221         return [self draggingUpdated:sender];
3222     }
3224     - (void) draggingExited:(id <NSDraggingInfo>)sender
3225     {
3226         // This isn't really a query.  We don't need any response.  However, it
3227         // has to be processed in a similar manner as the other drag-and-drop
3228         // queries in order to maintain the proper order of operations.
3229         macdrv_query* query = macdrv_create_query();
3230         query->type = QUERY_DRAG_EXITED;
3231         query->window = (macdrv_window)[self retain];
3233         [self.queue query:query timeout:0.1];
3234         macdrv_release_query(query);
3235     }
3237     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3238     {
3239         NSDragOperation ret;
3240         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3241         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3242         NSPasteboard* pb = [sender draggingPasteboard];
3244         macdrv_query* query = macdrv_create_query();
3245         query->type = QUERY_DRAG_OPERATION;
3246         query->window = (macdrv_window)[self retain];
3247         query->drag_operation.x = floor(cgpt.x);
3248         query->drag_operation.y = floor(cgpt.y);
3249         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3250         query->drag_operation.accepted_op = NSDragOperationNone;
3251         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3253         [self.queue query:query timeout:3];
3254         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3255         macdrv_release_query(query);
3257         return ret;
3258     }
3260     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3261     {
3262         BOOL ret;
3263         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3264         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3265         NSPasteboard* pb = [sender draggingPasteboard];
3267         macdrv_query* query = macdrv_create_query();
3268         query->type = QUERY_DRAG_DROP;
3269         query->window = (macdrv_window)[self retain];
3270         query->drag_drop.x = floor(cgpt.x);
3271         query->drag_drop.y = floor(cgpt.y);
3272         query->drag_drop.op = [sender draggingSourceOperationMask];
3273         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3275         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3276         ret = query->status;
3277         macdrv_release_query(query);
3279         return ret;
3280     }
3282     - (BOOL) wantsPeriodicDraggingUpdates
3283     {
3284         return NO;
3285     }
3287 @end
3290 /***********************************************************************
3291  *              macdrv_create_cocoa_window
3293  * Create a Cocoa window with the given content frame and features (e.g.
3294  * title bar, close box, etc.).
3295  */
3296 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3297         CGRect frame, void* hwnd, macdrv_event_queue queue)
3299     __block WineWindow* window;
3301     OnMainThread(^{
3302         window = [[WineWindow createWindowWithFeatures:wf
3303                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3304                                                   hwnd:hwnd
3305                                                  queue:(WineEventQueue*)queue] retain];
3306     });
3308     return (macdrv_window)window;
3311 /***********************************************************************
3312  *              macdrv_destroy_cocoa_window
3314  * Destroy a Cocoa window.
3315  */
3316 void macdrv_destroy_cocoa_window(macdrv_window w)
3318 @autoreleasepool
3320     WineWindow* window = (WineWindow*)w;
3322     OnMainThread(^{
3323         window.closing = TRUE;
3324         [window doOrderOut];
3325         [window close];
3326     });
3327     [window.queue discardEventsMatchingMask:-1 forWindow:window];
3328     [window release];
3332 /***********************************************************************
3333  *              macdrv_get_window_hwnd
3335  * Get the hwnd that was set for the window at creation.
3336  */
3337 void* macdrv_get_window_hwnd(macdrv_window w)
3339     WineWindow* window = (WineWindow*)w;
3340     return window.hwnd;
3343 /***********************************************************************
3344  *              macdrv_set_cocoa_window_features
3346  * Update a Cocoa window's features.
3347  */
3348 void macdrv_set_cocoa_window_features(macdrv_window w,
3349         const struct macdrv_window_features* wf)
3351     WineWindow* window = (WineWindow*)w;
3353     OnMainThread(^{
3354         [window setWindowFeatures:wf];
3355     });
3358 /***********************************************************************
3359  *              macdrv_set_cocoa_window_state
3361  * Update a Cocoa window's state.
3362  */
3363 void macdrv_set_cocoa_window_state(macdrv_window w,
3364         const struct macdrv_window_state* state)
3366     WineWindow* window = (WineWindow*)w;
3368     OnMainThread(^{
3369         [window setMacDrvState:state];
3370     });
3373 /***********************************************************************
3374  *              macdrv_set_cocoa_window_title
3376  * Set a Cocoa window's title.
3377  */
3378 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3379         size_t length)
3381 @autoreleasepool
3383     WineWindow* window = (WineWindow*)w;
3384     NSString* titleString;
3386     if (title)
3387         titleString = [NSString stringWithCharacters:title length:length];
3388     else
3389         titleString = @"";
3390     OnMainThreadAsync(^{
3391         [window setTitle:titleString];
3392         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3393             [NSApp changeWindowsItem:window title:titleString filename:NO];
3394     });
3398 /***********************************************************************
3399  *              macdrv_order_cocoa_window
3401  * Reorder a Cocoa window relative to other windows.  If prev is
3402  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3403  * it is ordered above that window.  Otherwise, it is ordered to the
3404  * front.
3405  */
3406 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3407         macdrv_window n, int activate)
3409     WineWindow* window = (WineWindow*)w;
3410     WineWindow* prev = (WineWindow*)p;
3411     WineWindow* next = (WineWindow*)n;
3413     OnMainThreadAsync(^{
3414         [window orderBelow:prev
3415                    orAbove:next
3416                   activate:activate];
3417     });
3418     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3419                                   forWindow:window];
3420     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3421                                 forWindow:next];
3424 /***********************************************************************
3425  *              macdrv_hide_cocoa_window
3427  * Hides a Cocoa window.
3428  */
3429 void macdrv_hide_cocoa_window(macdrv_window w)
3431     WineWindow* window = (WineWindow*)w;
3433     OnMainThread(^{
3434         [window doOrderOut];
3435     });
3438 /***********************************************************************
3439  *              macdrv_set_cocoa_window_frame
3441  * Move a Cocoa window.
3442  */
3443 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3445     WineWindow* window = (WineWindow*)w;
3447     OnMainThread(^{
3448         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3449     });
3452 /***********************************************************************
3453  *              macdrv_get_cocoa_window_frame
3455  * Gets the frame of a Cocoa window.
3456  */
3457 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3459     WineWindow* window = (WineWindow*)w;
3461     OnMainThread(^{
3462         NSRect frame;
3464         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3465         [[WineApplicationController sharedController] flipRect:&frame];
3466         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3467     });
3470 /***********************************************************************
3471  *              macdrv_set_cocoa_parent_window
3473  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3474  * the parent window.
3475  */
3476 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3478     WineWindow* window = (WineWindow*)w;
3480     OnMainThread(^{
3481         [window setMacDrvParentWindow:(WineWindow*)parent];
3482     });
3485 /***********************************************************************
3486  *              macdrv_set_window_surface
3487  */
3488 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3490 @autoreleasepool
3492     WineWindow* window = (WineWindow*)w;
3494     OnMainThread(^{
3495         window.surface = surface;
3496         window.surface_mutex = mutex;
3497     });
3501 /***********************************************************************
3502  *              macdrv_window_needs_display
3504  * Mark a window as needing display in a specified rect (in non-client
3505  * area coordinates).
3506  */
3507 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3509 @autoreleasepool
3511     WineWindow* window = (WineWindow*)w;
3513     OnMainThreadAsync(^{
3514         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3515     });
3519 /***********************************************************************
3520  *              macdrv_set_window_shape
3522  * Sets the shape of a Cocoa window from an array of rectangles.  If
3523  * rects is NULL, resets the window's shape to its frame.
3524  */
3525 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3527 @autoreleasepool
3529     WineWindow* window = (WineWindow*)w;
3531     OnMainThread(^{
3532         if (!rects || !count)
3533         {
3534             [window setShape:NULL];
3535             [window checkEmptyShaped];
3536         }
3537         else
3538         {
3539             CGMutablePathRef path;
3540             unsigned int i;
3542             path = CGPathCreateMutable();
3543             for (i = 0; i < count; i++)
3544                 CGPathAddRect(path, NULL, cgrect_mac_from_win(rects[i]));
3545             [window setShape:path];
3546             CGPathRelease(path);
3547         }
3548     });
3552 /***********************************************************************
3553  *              macdrv_set_window_alpha
3554  */
3555 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3557 @autoreleasepool
3559     WineWindow* window = (WineWindow*)w;
3561     [window setAlphaValue:alpha];
3565 /***********************************************************************
3566  *              macdrv_set_window_color_key
3567  */
3568 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3569                                  CGFloat keyBlue)
3571 @autoreleasepool
3573     WineWindow* window = (WineWindow*)w;
3575     OnMainThread(^{
3576         window.colorKeyed       = TRUE;
3577         window.colorKeyRed      = keyRed;
3578         window.colorKeyGreen    = keyGreen;
3579         window.colorKeyBlue     = keyBlue;
3580         [window checkTransparency];
3581     });
3585 /***********************************************************************
3586  *              macdrv_clear_window_color_key
3587  */
3588 void macdrv_clear_window_color_key(macdrv_window w)
3590 @autoreleasepool
3592     WineWindow* window = (WineWindow*)w;
3594     OnMainThread(^{
3595         window.colorKeyed = FALSE;
3596         [window checkTransparency];
3597     });
3601 /***********************************************************************
3602  *              macdrv_window_use_per_pixel_alpha
3603  */
3604 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3606 @autoreleasepool
3608     WineWindow* window = (WineWindow*)w;
3610     OnMainThread(^{
3611         window.usePerPixelAlpha = use_per_pixel_alpha;
3612         [window checkTransparency];
3613     });
3617 /***********************************************************************
3618  *              macdrv_give_cocoa_window_focus
3620  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3621  * orders it front and, if its frame was not within the desktop bounds,
3622  * Cocoa will typically move it on-screen.
3623  */
3624 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3626     WineWindow* window = (WineWindow*)w;
3628     OnMainThread(^{
3629         [window makeFocused:activate];
3630     });
3633 /***********************************************************************
3634  *              macdrv_set_window_min_max_sizes
3636  * Sets the window's minimum and maximum content sizes.
3637  */
3638 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3640     WineWindow* window = (WineWindow*)w;
3642     OnMainThread(^{
3643         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3644     });
3647 /***********************************************************************
3648  *              macdrv_create_view
3650  * Creates and returns a view with the specified frame rect.  The
3651  * caller is responsible for calling macdrv_dispose_view() on the view
3652  * when it is done with it.
3653  */
3654 macdrv_view macdrv_create_view(CGRect rect)
3656 @autoreleasepool
3658     __block WineContentView* view;
3660     if (CGRectIsNull(rect)) rect = CGRectZero;
3662     OnMainThread(^{
3663         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3665         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3666         [view setAutoresizingMask:NSViewNotSizable];
3667         [view setHidden:YES];
3668         [view setWantsBestResolutionOpenGLSurface:retina_on];
3669         [nc addObserver:view
3670                selector:@selector(updateGLContexts)
3671                    name:NSViewGlobalFrameDidChangeNotification
3672                  object:view];
3673         [nc addObserver:view
3674                selector:@selector(updateGLContexts)
3675                    name:NSApplicationDidChangeScreenParametersNotification
3676                  object:NSApp];
3677     });
3679     return (macdrv_view)view;
3683 /***********************************************************************
3684  *              macdrv_dispose_view
3686  * Destroys a view previously returned by macdrv_create_view.
3687  */
3688 void macdrv_dispose_view(macdrv_view v)
3690 @autoreleasepool
3692     WineContentView* view = (WineContentView*)v;
3694     OnMainThread(^{
3695         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3696         WineWindow* window = (WineWindow*)[view window];
3698         [nc removeObserver:view
3699                       name:NSViewGlobalFrameDidChangeNotification
3700                     object:view];
3701         [nc removeObserver:view
3702                       name:NSApplicationDidChangeScreenParametersNotification
3703                     object:NSApp];
3704         [view removeFromSuperview];
3705         [view release];
3706         [window updateForGLSubviews];
3707     });
3711 /***********************************************************************
3712  *              macdrv_set_view_frame
3713  */
3714 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3716 @autoreleasepool
3718     WineContentView* view = (WineContentView*)v;
3720     if (CGRectIsNull(rect)) rect = CGRectZero;
3722     OnMainThreadAsync(^{
3723         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3724         NSRect oldFrame = [view frame];
3726         if (!NSEqualRects(oldFrame, newFrame))
3727         {
3728             [[view superview] setNeedsDisplayInRect:oldFrame];
3729             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3730                 [view setFrameSize:newFrame.size];
3731             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3732                 [view setFrameOrigin:newFrame.origin];
3733             else
3734                 [view setFrame:newFrame];
3735             [view setNeedsDisplay:YES];
3737             if (retina_enabled)
3738             {
3739                 int backing_size[2] = { 0 };
3740                 [view wine_setBackingSize:backing_size];
3741             }
3742             [(WineWindow*)[view window] updateForGLSubviews];
3743         }
3744     });
3748 /***********************************************************************
3749  *              macdrv_set_view_superview
3751  * Move a view to a new superview and position it relative to its
3752  * siblings.  If p is non-NULL, the view is ordered behind it.
3753  * Otherwise, the view is ordered above n.  If s is NULL, use the
3754  * content view of w as the new superview.
3755  */
3756 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3758 @autoreleasepool
3760     WineContentView* view = (WineContentView*)v;
3761     WineContentView* superview = (WineContentView*)s;
3762     WineWindow* window = (WineWindow*)w;
3763     WineContentView* prev = (WineContentView*)p;
3764     WineContentView* next = (WineContentView*)n;
3766     if (!superview)
3767         superview = [window contentView];
3769     OnMainThreadAsync(^{
3770         if (superview == [view superview])
3771         {
3772             NSArray* subviews = [superview subviews];
3773             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3774             if (!prev && !next && index == [subviews count] - 1)
3775                 return;
3776             if (prev && index + 1 < [subviews count] && subviews[index + 1] == prev)
3777                 return;
3778             if (!prev && next && index > 0 && subviews[index - 1] == next)
3779                 return;
3780         }
3782         WineWindow* oldWindow = (WineWindow*)[view window];
3783         WineWindow* newWindow = (WineWindow*)[superview window];
3785 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3786         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3787             [view removeFromSuperview];
3788 #endif
3789         if (prev)
3790             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3791         else
3792             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3794         if (oldWindow != newWindow)
3795         {
3796             [oldWindow updateForGLSubviews];
3797             [newWindow updateForGLSubviews];
3798         }
3799     });
3803 /***********************************************************************
3804  *              macdrv_set_view_hidden
3805  */
3806 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3808 @autoreleasepool
3810     WineContentView* view = (WineContentView*)v;
3812     OnMainThreadAsync(^{
3813         [view setHidden:hidden];
3814         [(WineWindow*)view.window updateForGLSubviews];
3815     });
3819 /***********************************************************************
3820  *              macdrv_add_view_opengl_context
3822  * Add an OpenGL context to the list being tracked for each view.
3823  */
3824 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3826 @autoreleasepool
3828     WineContentView* view = (WineContentView*)v;
3829     WineOpenGLContext *context = (WineOpenGLContext*)c;
3831     OnMainThread(^{
3832         [view addGLContext:context];
3833     });
3837 /***********************************************************************
3838  *              macdrv_remove_view_opengl_context
3840  * Add an OpenGL context to the list being tracked for each view.
3841  */
3842 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3844 @autoreleasepool
3846     WineContentView* view = (WineContentView*)v;
3847     WineOpenGLContext *context = (WineOpenGLContext*)c;
3849     OnMainThreadAsync(^{
3850         [view removeGLContext:context];
3851     });
3855 macdrv_metal_device macdrv_create_metal_device(void)
3857 @autoreleasepool
3859     macdrv_metal_device ret;
3861 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3862     if (MTLCreateSystemDefaultDevice == NULL)
3863         return NULL;
3864 #endif
3866     ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3867     return ret;
3871 void macdrv_release_metal_device(macdrv_metal_device d)
3873 @autoreleasepool
3875     [(id<MTLDevice>)d release];
3879 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3881     id<MTLDevice> device = (id<MTLDevice>)d;
3882     WineContentView* view = (WineContentView*)v;
3883     __block WineMetalView *metalView;
3885     OnMainThread(^{
3886         metalView = [view newMetalViewWithDevice:device];
3887     });
3889     return (macdrv_metal_view)metalView;
3892 macdrv_metal_layer macdrv_view_get_metal_layer(macdrv_metal_view v)
3894     WineMetalView* view = (WineMetalView*)v;
3895     __block CAMetalLayer* layer;
3897     OnMainThread(^{
3898         layer = (CAMetalLayer*)view.layer;
3899     });
3901     return (macdrv_metal_layer)layer;
3904 void macdrv_view_release_metal_view(macdrv_metal_view v)
3906     WineMetalView* view = (WineMetalView*)v;
3907     OnMainThread(^{
3908         [view removeFromSuperview];
3909         [view release];
3910     });
3913 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3915     WineContentView* view = (WineContentView*)v;
3917     if (![view isKindOfClass:[WineContentView class]])
3918         return FALSE;
3920     [view wine_getBackingSize:backing_size];
3921     return TRUE;
3924 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3926     WineContentView* view = (WineContentView*)v;
3928     if ([view isKindOfClass:[WineContentView class]])
3929         [view wine_setBackingSize:backing_size];
3932 /***********************************************************************
3933  *              macdrv_window_background_color
3935  * Returns the standard Mac window background color as a 32-bit value of
3936  * the form 0x00rrggbb.
3937  */
3938 uint32_t macdrv_window_background_color(void)
3940     static uint32_t result;
3941     static dispatch_once_t once;
3943     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3944     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3945     // of it is to draw with it.
3946     dispatch_once(&once, ^{
3947         OnMainThread(^{
3948             unsigned char rgbx[4];
3949             unsigned char *planes = rgbx;
3950             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3951                                                                                pixelsWide:1
3952                                                                                pixelsHigh:1
3953                                                                             bitsPerSample:8
3954                                                                           samplesPerPixel:3
3955                                                                                  hasAlpha:NO
3956                                                                                  isPlanar:NO
3957                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3958                                                                              bitmapFormat:0
3959                                                                               bytesPerRow:4
3960                                                                              bitsPerPixel:32];
3961             [NSGraphicsContext saveGraphicsState];
3962             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3963             [[NSColor windowBackgroundColor] set];
3964             NSRectFill(NSMakeRect(0, 0, 1, 1));
3965             [NSGraphicsContext restoreGraphicsState];
3966             [bitmap release];
3967             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3968         });
3969     });
3971     return result;
3974 /***********************************************************************
3975  *              macdrv_send_text_input_event
3976  */
3977 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* himc, int* done)
3979     OnMainThreadAsync(^{
3980         BOOL ret;
3981         macdrv_event* event;
3982         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3983         if (![window isKindOfClass:[WineWindow class]])
3984         {
3985             window = (WineWindow*)[NSApp mainWindow];
3986             if (![window isKindOfClass:[WineWindow class]])
3987                 window = [[WineApplicationController sharedController] frontWineWindow];
3988         }
3990         if (window)
3991         {
3992             NSUInteger localFlags = flags;
3993             CGEventRef c;
3994             NSEvent* event;
3996             window.himc = himc;
3997             fix_device_modifiers_by_generic(&localFlags);
3999             // An NSEvent created with +keyEventWithType:... is internally marked
4000             // as synthetic and doesn't get sent through input methods.  But one
4001             // created from a CGEvent doesn't have that problem.
4002             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
4003             CGEventSetFlags(c, localFlags);
4004             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
4005             event = [NSEvent eventWithCGEvent:c];
4006             CFRelease(c);
4008             window.commandDone = FALSE;
4009             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
4010         }
4011         else
4012             ret = FALSE;
4014         event = macdrv_create_event(SENT_TEXT_INPUT, window);
4015         event->sent_text_input.handled = ret;
4016         event->sent_text_input.done = done;
4017         [[window queue] postEvent:event];
4018         macdrv_release_event(event);
4019     });
4022 void macdrv_clear_ime_text(void)
4024     OnMainThreadAsync(^{
4025         WineWindow* window = (WineWindow*)[NSApp keyWindow];
4026         if (![window isKindOfClass:[WineWindow class]])
4027         {
4028             window = (WineWindow*)[NSApp mainWindow];
4029             if (![window isKindOfClass:[WineWindow class]])
4030                 window = [[WineApplicationController sharedController] frontWineWindow];
4031         }
4032         if (window)
4033             [[window contentView] clearMarkedText];
4034     });