dmusic: Avoid swallowing collection Load failures.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blobcc9a9e13c2b8073c15c9fdfbd4e2acbcaa8de308
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"
37 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
38 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
39 enum {
40     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
42 #endif
45 @interface NSWindow (PrivatePreventsActivation)
47 /* Needed to ensure proper behavior after adding or removing
48  * NSWindowStyleMaskNonactivatingPanel.
49  * Available since at least macOS 10.6. */
50 - (void)_setPreventsActivation:(BOOL)flag;
52 @end
55 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
57     NSUInteger style_mask;
59     if (wf->title_bar)
60     {
61         style_mask = NSWindowStyleMaskTitled;
62         if (wf->close_button) style_mask |= NSWindowStyleMaskClosable;
63         if (wf->minimize_button) style_mask |= NSWindowStyleMaskMiniaturizable;
64         if (wf->resizable || wf->maximize_button) style_mask |= NSWindowStyleMaskResizable;
65         if (wf->utility) style_mask |= NSWindowStyleMaskUtilityWindow;
66     }
67     else style_mask = NSWindowStyleMaskBorderless;
69     if (wf->prevents_app_activation) style_mask |= NSWindowStyleMaskNonactivatingPanel;
71     return style_mask;
75 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
77     NSScreen* screen;
78     for (screen in screens)
79     {
80         if (NSIntersectsRect(frame, [screen frame]))
81             return TRUE;
82     }
83     return FALSE;
87 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
89     for (NSScreen* screen in screens)
90     {
91         if (NSContainsRect(rect, [screen frame]))
92             return screen;
93     }
94     return nil;
98 /* We rely on the supposedly device-dependent modifier flags to distinguish the
99    keys on the left side of the keyboard from those on the right.  Some event
100    sources don't set those device-depdendent flags.  If we see a device-independent
101    flag for a modifier without either corresponding device-dependent flag, assume
102    the left one. */
103 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
105     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
106         *modifiers |= NX_DEVICELCMDKEYMASK;
107     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
108         *modifiers |= NX_DEVICELSHIFTKEYMASK;
109     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
110         *modifiers |= NX_DEVICELCTLKEYMASK;
111     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
112         *modifiers |= NX_DEVICELALTKEYMASK;
115 /* As we manipulate individual bits of a modifier mask, we can end up with
116    inconsistent sets of flags.  In particular, we might set or clear one of the
117    left/right-specific bits, but not the corresponding non-side-specific bit.
118    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
119    otherwise clear it. */
120 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
122     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
123         *modifiers |= NX_COMMANDMASK;
124     else
125         *modifiers &= ~NX_COMMANDMASK;
126     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
127         *modifiers |= NX_SHIFTMASK;
128     else
129         *modifiers &= ~NX_SHIFTMASK;
130     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
131         *modifiers |= NX_CONTROLMASK;
132     else
133         *modifiers &= ~NX_CONTROLMASK;
134     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
135         *modifiers |= NX_ALTERNATEMASK;
136     else
137         *modifiers &= ~NX_ALTERNATEMASK;
140 static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
142     fix_device_modifiers_by_generic(&modifiers);
143     NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
144                                              NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
146     // The MACDRV keyboard driver translates Command keys to Alt. If the
147     // Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
148     // Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
149     if (modifiers & NX_DEVICELALTKEYMASK)
150         new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
151     if (modifiers & NX_DEVICERALTKEYMASK)
152         new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
154     if (modifiers & NX_DEVICELCMDKEYMASK)
155         new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
156     if (modifiers & NX_DEVICERCMDKEYMASK)
157         new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
159     fix_generic_modifiers_by_device(&new_modifiers);
160     return new_modifiers;
164 @interface NSWindow (WineAccessPrivateMethods)
165     - (id) _displayChanged;
166 @end
169 @interface WineDisplayLink : NSObject
171     CGDirectDisplayID _displayID;
172     CVDisplayLinkRef _link;
173     NSMutableSet* _windows;
175     NSTimeInterval _actualRefreshPeriod;
176     NSTimeInterval _nominalRefreshPeriod;
178     NSTimeInterval _lastDisplayTime;
181     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
183     - (void) addWindow:(WineWindow*)window;
184     - (void) removeWindow:(WineWindow*)window;
186     - (NSTimeInterval) refreshPeriod;
188     - (void) start;
190 @end
192 @implementation WineDisplayLink
194 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
196     - (id) initWithDisplayID:(CGDirectDisplayID)displayID
197     {
198         self = [super init];
199         if (self)
200         {
201             CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
202             if (status == kCVReturnSuccess && !_link)
203                 status = kCVReturnError;
204             if (status == kCVReturnSuccess)
205                 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
206             if (status != kCVReturnSuccess)
207             {
208                 [self release];
209                 return nil;
210             }
212             _displayID = displayID;
213             _windows = [[NSMutableSet alloc] init];
214         }
215         return self;
216     }
218     - (void) dealloc
219     {
220         if (_link)
221         {
222             CVDisplayLinkStop(_link);
223             CVDisplayLinkRelease(_link);
224         }
225         [_windows release];
226         [super dealloc];
227     }
229     - (void) addWindow:(WineWindow*)window
230     {
231         BOOL firstWindow;
232         @synchronized(self) {
233             firstWindow = !_windows.count;
234             [_windows addObject:window];
235         }
236         if (firstWindow || !CVDisplayLinkIsRunning(_link))
237             [self start];
238     }
240     - (void) removeWindow:(WineWindow*)window
241     {
242         BOOL lastWindow = FALSE;
243         @synchronized(self) {
244             BOOL hadWindows = _windows.count > 0;
245             [_windows removeObject:window];
246             if (hadWindows && !_windows.count)
247                 lastWindow = TRUE;
248         }
249         if (lastWindow && CVDisplayLinkIsRunning(_link))
250             CVDisplayLinkStop(_link);
251     }
253     - (void) fire
254     {
255         NSSet* windows;
256         @synchronized(self) {
257             windows = [_windows copy];
258         }
259         dispatch_async(dispatch_get_main_queue(), ^{
260             BOOL anyDisplayed = FALSE;
261             for (WineWindow* window in windows)
262             {
263                 if ([window viewsNeedDisplay])
264                 {
265                     [window displayIfNeeded];
266                     anyDisplayed = YES;
267                 }
268             }
270             NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
271             if (anyDisplayed)
272                 _lastDisplayTime = now;
273             else if (_lastDisplayTime + 2.0 < now)
274                 CVDisplayLinkStop(_link);
275         });
276         [windows release];
277     }
279     - (NSTimeInterval) refreshPeriod
280     {
281         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
282             return _actualRefreshPeriod;
284         if (_nominalRefreshPeriod)
285             return _nominalRefreshPeriod;
287         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
288         if (time.flags & kCVTimeIsIndefinite)
289             return 1.0 / 60.0;
290         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
291         return _nominalRefreshPeriod;
292     }
294     - (void) start
295     {
296         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
297         CVDisplayLinkStart(_link);
298     }
300 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
302     WineDisplayLink* link = displayLinkContext;
303     [link fire];
304     return kCVReturnSuccess;
307 @end
310 #ifndef MAC_OS_X_VERSION_10_14
311 @protocol NSViewLayerContentScaleDelegate <NSObject>
312 @optional
314     - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window;
316 @end
317 #endif
320 @interface CAShapeLayer (WineShapeMaskExtensions)
322 @property(readonly, nonatomic, getter=isEmptyShaped) BOOL emptyShaped;
324 @end
326 @implementation CAShapeLayer (WineShapeMaskExtensions)
328     - (BOOL) isEmptyShaped
329     {
330         return CGRectEqualToRect(CGPathGetBoundingBox(self.path), CGRectZero);
331     }
333 @end
336 @interface WineBaseView : NSView
337 @end
340 @interface WineMetalView : WineBaseView
342     id<MTLDevice> _device;
345     - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
347 @end
350 @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContentScaleDelegate>
352     NSMutableArray* glContexts;
353     NSMutableArray* pendingGlContexts;
354     BOOL _everHadGLContext;
355     BOOL _cachedHasGLDescendant;
356     BOOL _cachedHasGLDescendantValid;
357     BOOL clearedGlSurface;
359     NSMutableAttributedString* markedText;
360     NSRange markedTextSelection;
362     int backingSize[2];
364     WineMetalView *_metalView;
367 @property (readonly, nonatomic) BOOL everHadGLContext;
369     - (void) addGLContext:(WineOpenGLContext*)context;
370     - (void) removeGLContext:(WineOpenGLContext*)context;
371     - (void) updateGLContexts;
373     - (void) wine_getBackingSize:(int*)outBackingSize;
374     - (void) wine_setBackingSize:(const int*)newBackingSize;
376     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
378 @end
381 @interface WineWindow ()
383 @property (readwrite, nonatomic) BOOL disabled;
384 @property (readwrite, nonatomic) BOOL noForeground;
385 @property (readwrite, nonatomic) BOOL preventsAppActivation;
386 @property (readwrite, nonatomic) BOOL floating;
387 @property (readwrite, nonatomic) BOOL drawnSinceShown;
388 @property (readwrite, nonatomic) BOOL closing;
389 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
390 @property (retain, nonatomic) NSWindow* latentParentWindow;
392 @property (nonatomic) void* hwnd;
393 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
395 @property (nonatomic) void* surface;
396 @property (nonatomic) pthread_mutex_t* surface_mutex;
398 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
399 @property (readonly, nonatomic) BOOL needsTransparency;
401 @property (nonatomic) BOOL colorKeyed;
402 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
403 @property (nonatomic) BOOL usePerPixelAlpha;
405 @property (assign, nonatomic) void* himc;
406 @property (nonatomic) BOOL commandDone;
408 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
410     - (void) setShape:(CGPathRef)newShape;
412     - (void) updateForGLSubviews;
414     - (BOOL) becameEligibleParentOrChild;
415     - (void) becameIneligibleChild;
417     - (void) windowDidDrawContent;
419 @end
422 @implementation WineBaseView
424     - (void) setRetinaMode:(int)mode
425     {
426         for (WineBaseView* subview in [self subviews])
427         {
428             if ([subview isKindOfClass:[WineBaseView class]])
429                 [subview setRetinaMode:mode];
430         }
431     }
433     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
434     {
435         return YES;
436     }
438     - (BOOL) preservesContentDuringLiveResize
439     {
440         // Returning YES from this tells Cocoa to keep our view's content during
441         // a Cocoa-driven resize.  In theory, we're also supposed to override
442         // -setFrameSize: to mark exposed sections as needing redisplay, but
443         // user32 will take care of that in a roundabout way.  This way, we don't
444         // redraw until the window surface is flushed.
445         //
446         // This doesn't do anything when we resize the window ourselves.
447         return YES;
448     }
450     - (BOOL)acceptsFirstResponder
451     {
452         return [[self window] contentView] == self;
453     }
455     - (BOOL) mouseDownCanMoveWindow
456     {
457         return NO;
458     }
460     - (NSFocusRingType) focusRingType
461     {
462         return NSFocusRingTypeNone;
463     }
465 @end
468 @implementation WineContentView
470 @synthesize everHadGLContext = _everHadGLContext;
472     - (instancetype) initWithFrame:(NSRect)frame
473     {
474         self = [super initWithFrame:frame];
475         if (self)
476         {
477             [self setWantsLayer:YES];
478             [self setLayerRetinaProperties:retina_on];
479             [self setAutoresizesSubviews:NO];
480         }
481         return self;
482     }
484     - (void) dealloc
485     {
486         [markedText release];
487         [glContexts release];
488         [pendingGlContexts release];
489         [super dealloc];
490     }
492     - (BOOL) isFlipped
493     {
494         return YES;
495     }
497     - (BOOL) wantsUpdateLayer
498     {
499         return YES /*!_everHadGLContext*/;
500     }
502     - (void) updateLayer
503     {
504         WineWindow* window = (WineWindow*)[self window];
505         CGImageRef image = NULL;
506         CGRect imageRect;
507         CALayer* layer = [self layer];
509         if ([window contentView] != self)
510             return;
512         if (window.closing || !window.surface || !window.surface_mutex)
513             return;
515         pthread_mutex_lock(window.surface_mutex);
516         if (get_surface_blit_rects(window.surface, NULL, NULL))
517         {
518             imageRect = layer.bounds;
519             imageRect.origin.x *= layer.contentsScale;
520             imageRect.origin.y *= layer.contentsScale;
521             imageRect.size.width *= layer.contentsScale;
522             imageRect.size.height *= layer.contentsScale;
523             image = create_surface_image(window.surface, &imageRect, FALSE, window.colorKeyed,
524                                          window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue);
525         }
526         pthread_mutex_unlock(window.surface_mutex);
528         if (image)
529         {
530             layer.contents = (id)image;
531             CFRelease(image);
532             [window windowDidDrawContent];
534             // If the window may be transparent, then we have to invalidate the
535             // shadow every time we draw.  Also, if this is the first time we've
536             // drawn since changing from transparent to opaque.
537             if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
538             {
539                 window.shapeChangedSinceLastDraw = FALSE;
540                 [window invalidateShadow];
541             }
542         }
543     }
545     - (void) viewWillDraw
546     {
547         [super viewWillDraw];
549         for (WineOpenGLContext* context in pendingGlContexts)
550         {
551             if (!clearedGlSurface)
552             {
553                 context.shouldClearToBlack = TRUE;
554                 clearedGlSurface = TRUE;
555             }
556             context.needsUpdate = TRUE;
557         }
558         [glContexts addObjectsFromArray:pendingGlContexts];
559         [pendingGlContexts removeAllObjects];
560     }
562     - (void) addGLContext:(WineOpenGLContext*)context
563     {
564         BOOL hadContext = _everHadGLContext;
565         if (!glContexts)
566             glContexts = [[NSMutableArray alloc] init];
567         if (!pendingGlContexts)
568             pendingGlContexts = [[NSMutableArray alloc] init];
570         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
571         {
572             [glContexts addObject:context];
573             if (!clearedGlSurface)
574             {
575                 context.shouldClearToBlack = TRUE;
576                 clearedGlSurface = TRUE;
577             }
578             context.needsUpdate = TRUE;
579         }
580         else
581         {
582             [pendingGlContexts addObject:context];
583             [self setNeedsDisplay:YES];
584         }
586         _everHadGLContext = YES;
587         if (!hadContext)
588             [self invalidateHasGLDescendant];
589         [(WineWindow*)[self window] updateForGLSubviews];
590     }
592     - (void) removeGLContext:(WineOpenGLContext*)context
593     {
594         [glContexts removeObjectIdenticalTo:context];
595         [pendingGlContexts removeObjectIdenticalTo:context];
596         [(WineWindow*)[self window] updateForGLSubviews];
597     }
599     - (void) updateGLContexts:(BOOL)reattach
600     {
601         for (WineOpenGLContext* context in glContexts)
602         {
603             context.needsUpdate = TRUE;
604             if (reattach)
605                 context.needsReattach = TRUE;
606         }
607     }
609     - (void) updateGLContexts
610     {
611         [self updateGLContexts:NO];
612     }
614     - (BOOL) _hasGLDescendant
615     {
616         if ([self isHidden])
617             return NO;
618         if (_everHadGLContext)
619             return YES;
620         for (WineContentView* view in [self subviews])
621         {
622             if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
623                 return YES;
624         }
625         return NO;
626     }
628     - (BOOL) hasGLDescendant
629     {
630         if (!_cachedHasGLDescendantValid)
631         {
632             _cachedHasGLDescendant = [self _hasGLDescendant];
633             _cachedHasGLDescendantValid = YES;
634         }
635         return _cachedHasGLDescendant;
636     }
638     - (void) invalidateHasGLDescendant
639     {
640         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
641         _cachedHasGLDescendantValid = NO;
642         if (invalidateAncestors && self != [[self window] contentView])
643         {
644             WineContentView* superview = (WineContentView*)[self superview];
645             if ([superview isKindOfClass:[WineContentView class]])
646                 [superview invalidateHasGLDescendant];
647         }
648     }
650     - (void) wine_getBackingSize:(int*)outBackingSize
651     {
652         @synchronized(self) {
653             memcpy(outBackingSize, backingSize, sizeof(backingSize));
654         }
655     }
656     - (void) wine_setBackingSize:(const int*)newBackingSize
657     {
658         @synchronized(self) {
659             memcpy(backingSize, newBackingSize, sizeof(backingSize));
660         }
661     }
663     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
664     {
665         if (_metalView) return _metalView;
667         WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
668         [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
669         [self setAutoresizesSubviews:YES];
670         [self addSubview:view positioned:NSWindowBelow relativeTo:nil];
671         _metalView = view;
673         [(WineWindow*)self.window windowDidDrawContent];
675         return _metalView;
676     }
678     - (void) setLayerRetinaProperties:(int)mode
679     {
680         [self layer].contentsScale = mode ? 2.0 : 1.0;
681         [self layer].minificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
682         [self layer].magnificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
684         /* On macOS 10.13 and earlier, the desired minificationFilter seems to be
685          * ignored and "nearest" filtering is used, which looks terrible.
686          * Enabling rasterization seems to work around this, only enable
687          * it when there may be down-scaling (retina mode enabled).
688          */
689         if (floor(NSAppKitVersionNumber) < 1671 /*NSAppKitVersionNumber10_14*/)
690         {
691             if (mode)
692             {
693                 [self layer].shouldRasterize = YES;
694                 [self layer].rasterizationScale = 2.0;
695             }
696             else
697                 [self layer].shouldRasterize = NO;
698         }
699     }
701     - (void) setRetinaMode:(int)mode
702     {
703         double scale = mode ? 0.5 : 2.0;
704         NSRect frame = self.frame;
705         frame.origin.x *= scale;
706         frame.origin.y *= scale;
707         frame.size.width *= scale;
708         frame.size.height *= scale;
709         [self setFrame:frame];
710         [self setWantsBestResolutionOpenGLSurface:mode];
711         [self updateGLContexts];
712         [self setLayerRetinaProperties:mode];
714         [super setRetinaMode:mode];
715     }
717     - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window
718     {
719         /* This method is invoked when the contentsScale of the layer is not
720          * equal to the contentsScale of the window.
721          * (Initially when the layer is first created, and later if the window
722          * contentsScale changes, i.e. moved between retina/non-retina monitors).
723          *
724          * We usually want to return YES, so the "moving windows between
725          * retina/non-retina monitors" case works right.
726          * But return NO when we need an intentional mismatch between the
727          * window and layer contentsScale
728          * (non-retina mode with a retina monitor, and vice-versa).
729          */
730         if (layer.contentsScale != window.backingScaleFactor)
731             return NO;
732         return YES;
733     }
735     - (void) viewDidHide
736     {
737         [super viewDidHide];
738         [self invalidateHasGLDescendant];
739     }
741     - (void) viewDidUnhide
742     {
743         [super viewDidUnhide];
744         [self updateGLContexts:YES];
745         [self invalidateHasGLDescendant];
746     }
748     - (void) clearMarkedText
749     {
750         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
751         markedTextSelection = NSMakeRange(0, 0);
752         [[self inputContext] discardMarkedText];
753     }
755     - (void) completeText:(NSString*)text
756     {
757         macdrv_event* event;
758         WineWindow* window = (WineWindow*)[self window];
760         event = macdrv_create_event(IM_SET_TEXT, window);
761         event->im_set_text.himc = [window himc];
762         event->im_set_text.text = (CFStringRef)[text copy];
763         event->im_set_text.complete = TRUE;
765         [[window queue] postEvent:event];
767         macdrv_release_event(event);
769         [self clearMarkedText];
770     }
772     - (void) didAddSubview:(NSView*)subview
773     {
774         if ([subview isKindOfClass:[WineContentView class]])
775         {
776             WineContentView* view = (WineContentView*)subview;
777             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
778                 [self invalidateHasGLDescendant];
779         }
780         [super didAddSubview:subview];
781     }
783     - (void) willRemoveSubview:(NSView*)subview
784     {
785         if ([subview isKindOfClass:[WineContentView class]])
786         {
787             WineContentView* view = (WineContentView*)subview;
788             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
789                 [self invalidateHasGLDescendant];
790         }
791         if (subview == _metalView)
792             _metalView = nil;
793         [super willRemoveSubview:subview];
794     }
796     - (void) setLayer:(CALayer*)newLayer
797     {
798         [super setLayer:newLayer];
799         [self updateGLContexts];
800     }
802     /*
803      * ---------- NSTextInputClient methods ----------
804      */
805     - (NSTextInputContext*) inputContext
806     {
807         if (!markedText)
808             markedText = [[NSMutableAttributedString alloc] init];
809         return [super inputContext];
810     }
812     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
813     {
814         if ([string isKindOfClass:[NSAttributedString class]])
815             string = [string string];
817         if ([string isKindOfClass:[NSString class]])
818             [self completeText:string];
819     }
821     - (void) doCommandBySelector:(SEL)aSelector
822     {
823         [(WineWindow*)[self window] setCommandDone:TRUE];
824     }
826     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
827     {
828         if ([string isKindOfClass:[NSAttributedString class]])
829             string = [string string];
831         if ([string isKindOfClass:[NSString class]])
832         {
833             macdrv_event* event;
834             WineWindow* window = (WineWindow*)[self window];
836             if (replacementRange.location == NSNotFound)
837                 replacementRange = NSMakeRange(0, [markedText length]);
839             [markedText replaceCharactersInRange:replacementRange withString:string];
840             markedTextSelection = selectedRange;
841             markedTextSelection.location += replacementRange.location;
843             event = macdrv_create_event(IM_SET_TEXT, window);
844             event->im_set_text.himc = [window himc];
845             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
846             event->im_set_text.complete = FALSE;
847             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
849             [[window queue] postEvent:event];
851             macdrv_release_event(event);
853             [[self inputContext] invalidateCharacterCoordinates];
854         }
855     }
857     - (void) unmarkText
858     {
859         [self completeText:nil];
860     }
862     - (NSRange) selectedRange
863     {
864         return markedTextSelection;
865     }
867     - (NSRange) markedRange
868     {
869         NSRange range = NSMakeRange(0, [markedText length]);
870         if (!range.length)
871             range.location = NSNotFound;
872         return range;
873     }
875     - (BOOL) hasMarkedText
876     {
877         return [markedText length] > 0;
878     }
880     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
881     {
882         if (aRange.location >= [markedText length])
883             return nil;
885         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
886         if (actualRange)
887             *actualRange = aRange;
888         return [markedText attributedSubstringFromRange:aRange];
889     }
891     - (NSArray*) validAttributesForMarkedText
892     {
893         return [NSArray array];
894     }
896     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
897     {
898         macdrv_query* query;
899         WineWindow* window = (WineWindow*)[self window];
900         NSRect ret;
902         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
904         query = macdrv_create_query();
905         query->type = QUERY_IME_CHAR_RECT;
906         query->window = (macdrv_window)[window retain];
907         query->ime_char_rect.himc = [window himc];
908         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
910         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
911         {
912             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
913             ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
914             [[WineApplicationController sharedController] flipRect:&ret];
915         }
916         else
917             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
919         macdrv_release_query(query);
921         if (actualRange)
922             *actualRange = aRange;
923         return ret;
924     }
926     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
927     {
928         return NSNotFound;
929     }
931     - (NSInteger) windowLevel
932     {
933         return [[self window] level];
934     }
936 @end
939 @implementation WineMetalView
941     - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
942     {
943         self = [super initWithFrame:frame];
944         if (self)
945         {
946             _device = [device retain];
947             self.wantsLayer = YES;
948             self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
949         }
950         return self;
951     }
953     - (void) dealloc
954     {
955         [_device release];
956         [super dealloc];
957     }
959     - (void) setRetinaMode:(int)mode
960     {
961         self.layer.contentsScale = mode ? 2.0 : 1.0;
962         [super setRetinaMode:mode];
963     }
965     - (CALayer*) makeBackingLayer
966     {
967         CAMetalLayer *layer = [CAMetalLayer layer];
968         layer.device = _device;
969         layer.framebufferOnly = YES;
970         layer.magnificationFilter = kCAFilterNearest;
971         layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
972         layer.contentsScale = retina_on ? 2.0 : 1.0;
973         return layer;
974     }
976     - (BOOL) isOpaque
977     {
978         return YES;
979     }
981 @end
984 @implementation WineWindow
986     static WineWindow* causing_becomeKeyWindow;
988     @synthesize disabled, noForeground, preventsAppActivation, floating, fullscreen, fakingClose, closing, latentParentWindow, hwnd, queue;
989     @synthesize drawnSinceShown;
990     @synthesize surface, surface_mutex;
991     @synthesize shapeChangedSinceLastDraw;
992     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
993     @synthesize usePerPixelAlpha;
994     @synthesize himc, commandDone;
996     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
997                                  windowFrame:(NSRect)window_frame
998                                         hwnd:(void*)hwnd
999                                        queue:(WineEventQueue*)queue
1000     {
1001         WineWindow* window;
1002         WineContentView* contentView;
1003         NSTrackingArea* trackingArea;
1004         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1006         [[WineApplicationController sharedController] flipRect:&window_frame];
1008         window = [[[self alloc] initWithContentRect:window_frame
1009                                           styleMask:style_mask_for_features(wf)
1010                                             backing:NSBackingStoreBuffered
1011                                               defer:YES] autorelease];
1013         if (!window) return nil;
1015         /* Standardize windows to eliminate differences between titled and
1016            borderless windows and between NSWindow and NSPanel. */
1017         [window setHidesOnDeactivate:NO];
1018         [window setReleasedWhenClosed:NO];
1020         [window setOneShot:YES];
1021         [window disableCursorRects];
1022         [window setShowsResizeIndicator:NO];
1023         [window setHasShadow:wf->shadow];
1024         [window setAcceptsMouseMovedEvents:YES];
1025         [window setDelegate:window];
1026         [window setBackgroundColor:[NSColor clearColor]];
1027         [window setOpaque:NO];
1028         window.hwnd = hwnd;
1029         window.queue = queue;
1030         window->savedContentMinSize = NSZeroSize;
1031         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
1032         window->resizable = wf->resizable;
1033         window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
1035         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
1036                                                                   (NSString*)kUTTypeContent,
1037                                                                   nil]];
1039         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
1040         if (!contentView)
1041             return nil;
1043         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
1044            because they give us mouse moves in the background. */
1045         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
1046                                                      options:(NSTrackingMouseMoved |
1047                                                               NSTrackingActiveAlways |
1048                                                               NSTrackingInVisibleRect)
1049                                                        owner:window
1050                                                     userInfo:nil] autorelease];
1051         if (!trackingArea)
1052             return nil;
1053         [contentView addTrackingArea:trackingArea];
1055         [window setContentView:contentView];
1056         [window setInitialFirstResponder:contentView];
1058         [nc addObserver:window
1059                selector:@selector(updateFullscreen)
1060                    name:NSApplicationDidChangeScreenParametersNotification
1061                  object:NSApp];
1062         [window updateFullscreen];
1064         [nc addObserver:window
1065                selector:@selector(applicationWillHide)
1066                    name:NSApplicationWillHideNotification
1067                  object:NSApp];
1068         [nc addObserver:window
1069                selector:@selector(applicationDidUnhide)
1070                    name:NSApplicationDidUnhideNotification
1071                  object:NSApp];
1073         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1074                                                               selector:@selector(checkWineDisplayLink)
1075                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
1076                                                                 object:[NSWorkspace sharedWorkspace]];
1078         [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1080         return window;
1081     }
1083     - (void) dealloc
1084     {
1085         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1086         [[NSNotificationCenter defaultCenter] removeObserver:self];
1087         [queue release];
1088         [latentChildWindows release];
1089         [latentParentWindow release];
1090         [super dealloc];
1091     }
1093     - (BOOL) preventResizing
1094     {
1095         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1096         return ([self styleMask] & NSWindowStyleMaskResizable) && (disabled || !resizable || preventForClipping);
1097     }
1099     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1100     {
1101         if (allow_immovable_windows && (disabled || inMaximized))
1102             return NO;
1103         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1104             return NO;
1105         else
1106             return YES;
1107     }
1109     - (void) adjustFeaturesForState
1110     {
1111         NSUInteger style = [self styleMask];
1113         if (style & NSWindowStyleMaskClosable)
1114             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1115         if (style & NSWindowStyleMaskMiniaturizable)
1116             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1117         if (style & NSWindowStyleMaskResizable)
1118             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1119         if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1120             [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1122         if ([self preventResizing])
1123         {
1124             NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1125             [self setContentMinSize:size];
1126             [self setContentMaxSize:size];
1127         }
1128         else
1129         {
1130             [self setContentMaxSize:savedContentMaxSize];
1131             [self setContentMinSize:savedContentMinSize];
1132         }
1134         if (allow_immovable_windows || cursor_clipping_locks_windows)
1135             [self setMovable:[self allowsMovingWithMaximized:maximized]];
1136     }
1138     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1139     {
1140         NSUInteger style = [self styleMask];
1142         if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1143             style & NSWindowStyleMaskResizable && !(style & NSWindowStyleMaskUtilityWindow) && !maximized &&
1144             !(self.parentWindow || self.latentParentWindow))
1145         {
1146             behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1147             behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1148         }
1149         else
1150         {
1151             behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1152             behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1153             if (style & NSWindowStyleMaskFullScreen)
1154                 [super toggleFullScreen:nil];
1155         }
1157         if (behavior != [self collectionBehavior])
1158         {
1159             [self setCollectionBehavior:behavior];
1160             [self adjustFeaturesForState];
1161         }
1162     }
1164     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1165     {
1166         static const NSUInteger usedStyles = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable |
1167                                              NSWindowStyleMaskResizable | NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskBorderless |
1168                                              NSWindowStyleMaskNonactivatingPanel;
1169         NSUInteger currentStyle = [self styleMask];
1170         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1172         self.preventsAppActivation = wf->prevents_app_activation;
1174         if (newStyle != currentStyle)
1175         {
1176             NSString* title = [[[self title] copy] autorelease];
1177             BOOL showingButtons = (currentStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1178             BOOL shouldShowButtons = (newStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1179             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSWindowStyleMaskClosable))
1180             {
1181                 // -setStyleMask: is buggy on 10.7+ with respect to NSWindowStyleMaskResizable.
1182                 // If transitioning from NSWindowStyleMaskTitled | NSWindowStyleMaskResizable to
1183                 // just NSWindowStyleMaskTitled, the window buttons should disappear rather
1184                 // than just being disabled.  But they don't.  Similarly in reverse.
1185                 // The workaround is to also toggle NSWindowStyleMaskClosable at the same time.
1186                 [self setStyleMask:newStyle ^ NSWindowStyleMaskClosable];
1187             }
1188             [self setStyleMask:newStyle];
1190             BOOL isNonActivating = (currentStyle & NSWindowStyleMaskNonactivatingPanel) != 0;
1191             BOOL shouldBeNonActivating = (newStyle & NSWindowStyleMaskNonactivatingPanel) != 0;
1192             if (isNonActivating != shouldBeNonActivating) {
1193                 // Changing NSWindowStyleMaskNonactivatingPanel with -setStyleMask is also
1194                 // buggy. If it's added, clicking the title bar will still activate the
1195                 // app. If it's removed, nothing changes at all.
1196                 // This private method ensures the correct behavior.
1197                 if ([self respondsToSelector:@selector(_setPreventsActivation:)])
1198                     [self _setPreventsActivation:shouldBeNonActivating];
1199             }
1201             // -setStyleMask: resets the firstResponder to the window.  Set it
1202             // back to the content view.
1203             if ([[self contentView] acceptsFirstResponder])
1204                 [self makeFirstResponder:[self contentView]];
1206             [self adjustFullScreenBehavior:[self collectionBehavior]];
1208             if ([[self title] length] == 0 && [title length] > 0)
1209                 [self setTitle:title];
1210         }
1212         resizable = wf->resizable;
1213         [self adjustFeaturesForState];
1214         [self setHasShadow:wf->shadow];
1215     }
1217     // Indicates if the window would be visible if the app were not hidden.
1218     - (BOOL) wouldBeVisible
1219     {
1220         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1221     }
1223     - (BOOL) isOrderedIn
1224     {
1225         return [self wouldBeVisible] || [self isMiniaturized];
1226     }
1228     - (NSInteger) minimumLevelForActive:(BOOL)active
1229     {
1230         NSInteger level;
1232         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1233                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1234             level = NSFloatingWindowLevel;
1235         else
1236             level = NSNormalWindowLevel;
1238         if (active)
1239         {
1240             BOOL captured;
1242             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1244             if (captured || fullscreen)
1245             {
1246                 if (captured)
1247                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1248                 else
1249                     level = NSStatusWindowLevel + 1;
1251                 if (self.floating)
1252                     level++;
1253             }
1254         }
1256         return level;
1257     }
1259     - (void) postDidUnminimizeEvent
1260     {
1261         macdrv_event* event;
1263         /* Coalesce events by discarding any previous ones still in the queue. */
1264         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1265                                forWindow:self];
1267         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1268         [queue postEvent:event];
1269         macdrv_release_event(event);
1270     }
1272     - (void) sendResizeStartQuery
1273     {
1274         macdrv_query* query = macdrv_create_query();
1275         query->type = QUERY_RESIZE_START;
1276         query->window = (macdrv_window)[self retain];
1278         [self.queue query:query timeout:0.3];
1279         macdrv_release_query(query);
1280     }
1282     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1283     {
1284         NSWindowCollectionBehavior behavior;
1286         self.disabled = state->disabled;
1287         self.noForeground = state->no_foreground;
1289         if (self.floating != state->floating)
1290         {
1291             self.floating = state->floating;
1292             if (state->floating)
1293             {
1294                 // Became floating.  If child of non-floating window, make that
1295                 // relationship latent.
1296                 WineWindow* parent = (WineWindow*)[self parentWindow];
1297                 if (parent && !parent.floating)
1298                     [self becameIneligibleChild];
1299             }
1300             else
1301             {
1302                 // Became non-floating.  If parent of floating children, make that
1303                 // relationship latent.
1304                 WineWindow* child;
1305                 for (child in [self childWineWindows])
1306                 {
1307                     if (child.floating)
1308                         [child becameIneligibleChild];
1309                 }
1310             }
1312             // Check our latent relationships.  If floating status was the only
1313             // reason they were latent, then make them active.
1314             if ([self isVisible])
1315                 [self becameEligibleParentOrChild];
1317             [[WineApplicationController sharedController] adjustWindowLevels];
1318         }
1320         if (state->minimized_valid)
1321         {
1322             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1324             pendingMinimize = FALSE;
1325             if (state->minimized && ![self isMiniaturized])
1326             {
1327                 if ([self wouldBeVisible])
1328                 {
1329                     if ([self styleMask] & NSWindowStyleMaskFullScreen)
1330                     {
1331                         [self postDidUnminimizeEvent];
1332                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1333                     }
1334                     else
1335                     {
1336                         [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1337                         [super miniaturize:nil];
1338                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1339                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1340                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1341                     }
1342                 }
1343                 else
1344                     pendingMinimize = TRUE;
1345             }
1346             else if (!state->minimized && [self isMiniaturized])
1347             {
1348                 ignore_windowDeminiaturize = TRUE;
1349                 [self deminiaturize:nil];
1350                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1351             }
1353             if (discard)
1354                 [queue discardEventsMatchingMask:discard forWindow:self];
1355         }
1357         if (state->maximized != maximized)
1358         {
1359             maximized = state->maximized;
1360             [self adjustFeaturesForState];
1362             if (!maximized && [self inLiveResize])
1363                 [self sendResizeStartQuery];
1364         }
1366         behavior = NSWindowCollectionBehaviorDefault;
1367         if (state->excluded_by_expose)
1368             behavior |= NSWindowCollectionBehaviorTransient;
1369         else
1370             behavior |= NSWindowCollectionBehaviorManaged;
1371         if (state->excluded_by_cycle)
1372         {
1373             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1374             if ([self isOrderedIn])
1375                 [NSApp removeWindowsItem:self];
1376         }
1377         else
1378         {
1379             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1380             if ([self isOrderedIn])
1381                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1382         }
1383         [self adjustFullScreenBehavior:behavior];
1384     }
1386     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1387     {
1388         BOOL reordered = FALSE;
1390         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1391         {
1392             if ([self level] > [child level])
1393                 [child setLevel:[self level]];
1394             if (![child isVisible])
1395                 [child setAutodisplay:YES];
1396             [self addChildWindow:child ordered:NSWindowAbove];
1397             [child checkWineDisplayLink];
1398             [latentChildWindows removeObjectIdenticalTo:child];
1399             child.latentParentWindow = nil;
1400             reordered = TRUE;
1401         }
1402         else
1403         {
1404             if (!latentChildWindows)
1405                 latentChildWindows = [[NSMutableArray alloc] init];
1406             if (![latentChildWindows containsObject:child])
1407                 [latentChildWindows addObject:child];
1408             child.latentParentWindow = self;
1409         }
1411         return reordered;
1412     }
1414     - (BOOL) addChildWineWindow:(WineWindow*)child
1415     {
1416         return [self addChildWineWindow:child assumeVisible:FALSE];
1417     }
1419     - (void) removeChildWineWindow:(WineWindow*)child
1420     {
1421         [self removeChildWindow:child];
1422         if (child.latentParentWindow == self)
1423             child.latentParentWindow = nil;
1424         [latentChildWindows removeObjectIdenticalTo:child];
1425     }
1427     - (void) setChildWineWindows:(NSArray*)childWindows
1428     {
1429         NSArray* origChildren;
1430         NSUInteger count, start, limit, i;
1432         origChildren = self.childWineWindows;
1434         // If the current and desired children arrays match up to a point, leave
1435         // those matching children alone.
1436         count = childWindows.count;
1437         limit = MIN(origChildren.count, count);
1438         for (start = 0; start < limit; start++)
1439         {
1440             if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1441                 break;
1442         }
1444         // Remove all of the child windows and re-add them back-to-front so they
1445         // are in the desired order.
1446         for (i = start; i < count; i++)
1447         {
1448             WineWindow* child = [childWindows objectAtIndex:i];
1449             [self removeChildWindow:child];
1450         }
1451         for (i = start; i < count; i++)
1452         {
1453             WineWindow* child = [childWindows objectAtIndex:i];
1454             [self addChildWindow:child ordered:NSWindowAbove];
1455         }
1456     }
1458     static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1459     {
1460         NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1461         NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1462         NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1463         NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1464         if (index1 == NSNotFound)
1465         {
1466             if (index2 == NSNotFound)
1467                 return NSOrderedSame;
1468             else
1469                 return NSOrderedAscending;
1470         }
1471         else if (index2 == NSNotFound)
1472             return NSOrderedDescending;
1473         else if (index1 < index2)
1474             return NSOrderedDescending;
1475         else if (index2 < index1)
1476             return NSOrderedAscending;
1478         return NSOrderedSame;
1479     }
1481     - (BOOL) becameEligibleParentOrChild
1482     {
1483         BOOL reordered = FALSE;
1484         NSUInteger count;
1486         if (latentParentWindow.floating || !self.floating)
1487         {
1488             // If we aren't visible currently, we assume that we should be and soon
1489             // will be.  So, if the latent parent is visible that's enough to assume
1490             // we can establish the parent-child relationship in Cocoa.  That will
1491             // actually make us visible, which is fine.
1492             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1493                 reordered = TRUE;
1494         }
1496         // Here, though, we may not actually be visible yet and adding a child
1497         // won't make us visible.  The caller will have to call this method
1498         // again after actually making us visible.
1499         if ([self isVisible] && (count = [latentChildWindows count]))
1500         {
1501             NSMutableArray* windowNumbers;
1502             NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1503             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1504             NSUInteger i;
1506             windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1508             for (i = 0; i < count; i++)
1509             {
1510                 WineWindow* child = [latentChildWindows objectAtIndex:i];
1511                 if ([child isVisible] && (self.floating || !child.floating))
1512                 {
1513                     if (child.latentParentWindow == self)
1514                     {
1515                         if ([self level] > [child level])
1516                             [child setLevel:[self level]];
1517                         [childWindows addObject:child];
1518                         child.latentParentWindow = nil;
1519                         reordered = TRUE;
1520                     }
1521                     else
1522                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1523                     [indexesToRemove addIndex:i];
1524                 }
1525             }
1527             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1529             [childWindows sortWithOptions:NSSortStable
1530                           usingComparator:^NSComparisonResult(id obj1, id obj2){
1531                 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1532             }];
1533             [self setChildWineWindows:childWindows];
1534         }
1536         return reordered;
1537     }
1539     - (void) becameIneligibleChild
1540     {
1541         WineWindow* parent = (WineWindow*)[self parentWindow];
1542         if (parent)
1543         {
1544             if (!parent->latentChildWindows)
1545                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1546             [parent->latentChildWindows insertObject:self atIndex:0];
1547             self.latentParentWindow = parent;
1548             [parent removeChildWindow:self];
1549         }
1550     }
1552     - (void) becameIneligibleParentOrChild
1553     {
1554         NSArray* childWindows = [self childWineWindows];
1556         [self becameIneligibleChild];
1558         if ([childWindows count])
1559         {
1560             WineWindow* child;
1562             for (child in childWindows)
1563             {
1564                 child.latentParentWindow = self;
1565                 [self removeChildWindow:child];
1566             }
1568             if (latentChildWindows)
1569                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1570             else
1571                 latentChildWindows = [childWindows mutableCopy];
1572         }
1573     }
1575     // Determine if, among Wine windows, this window is directly above or below
1576     // a given other Wine window with no other Wine window intervening.
1577     // Intervening non-Wine windows are ignored.
1578     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1579     {
1580         NSNumber* windowNumber;
1581         NSNumber* otherWindowNumber;
1582         NSArray* windowNumbers;
1583         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1585         if (![self isVisible] || ![otherWindow isVisible])
1586             return FALSE;
1588         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1589         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1590         windowNumbers = [[self class] windowNumbersWithOptions:0];
1591         windowIndex = [windowNumbers indexOfObject:windowNumber];
1592         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1594         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1595             return FALSE;
1597         if (orderingMode == NSWindowAbove)
1598         {
1599             lowIndex = windowIndex;
1600             highIndex = otherWindowIndex;
1601         }
1602         else if (orderingMode == NSWindowBelow)
1603         {
1604             lowIndex = otherWindowIndex;
1605             highIndex = windowIndex;
1606         }
1607         else
1608             return FALSE;
1610         if (highIndex <= lowIndex)
1611             return FALSE;
1613         for (i = lowIndex + 1; i < highIndex; i++)
1614         {
1615             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1616             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1617             if ([interveningWindow isKindOfClass:[WineWindow class]])
1618                 return FALSE;
1619         }
1621         return TRUE;
1622     }
1624     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1625     {
1626         NSMutableArray* windowNumbers;
1627         NSNumber* childWindowNumber;
1628         NSUInteger otherIndex;
1629         NSArray* origChildren;
1630         NSMutableArray* children;
1632         // Get the z-order from the window server and modify it to reflect the
1633         // requested window ordering.
1634         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1635         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1636         [windowNumbers removeObject:childWindowNumber];
1637         if (other)
1638         {
1639             otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1640             [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1641         }
1642         else if (mode == NSWindowAbove)
1643             [windowNumbers insertObject:childWindowNumber atIndex:0];
1644         else
1645             [windowNumbers addObject:childWindowNumber];
1647         // Get our child windows and sort them in the reverse of the desired
1648         // z-order (back-to-front).
1649         origChildren = [self childWineWindows];
1650         children = [[origChildren mutableCopy] autorelease];
1651         [children sortWithOptions:NSSortStable
1652                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1653             return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1654         }];
1656         [self setChildWineWindows:children];
1657     }
1659     // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1660     // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1661     //      (non-nil, non-nil)  there is a level in the window tree where the two windows have sibling ancestors
1662     //                          if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1663     //                          otherwise, the two ancestors are each roots of disjoint window trees
1664     //      (nil, non-nil)      the other window is a descendent of self and *ancestorOfOther is the direct child
1665     //      (non-nil, nil)      self is a descendent of other and *ancestor is the direct child
1666     - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1667     {
1668         NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1669         WineWindow* child;
1670         WineWindow* parent;
1671         for (child = other;
1672              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1673              child = parent)
1674         {
1675             if (parent == self)
1676             {
1677                 *ancestor = nil;
1678                 *ancestorOfOther = child;
1679                 return;
1680             }
1682             [otherAncestors addObject:parent];
1683         }
1685         for (child = self;
1686              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1687              child = parent)
1688         {
1689             NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1690             if (index != NSNotFound)
1691             {
1692                 *ancestor = child;
1693                 if (index == 0)
1694                     *ancestorOfOther = nil;
1695                 else
1696                     *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1697                 return;
1698             }
1699         }
1701         *ancestor = child;
1702         *ancestorOfOther = otherAncestors.lastObject;;
1703     }
1705     /* Returns whether or not the window was ordered in, which depends on if
1706        its frame intersects any screen. */
1707     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1708     {
1709         WineApplicationController* controller = [WineApplicationController sharedController];
1710         if (![self isMiniaturized])
1711         {
1712             BOOL needAdjustWindowLevels = FALSE;
1713             BOOL wasVisible;
1714             WineWindow* parent;
1715             WineWindow* child;
1717             [controller transformProcessToForeground:!self.preventsAppActivation];
1718             if ([NSApp isHidden])
1719                 [NSApp unhide:nil];
1720             wasVisible = [self isVisible];
1722             if (activate)
1723                 [controller tryToActivateIgnoringOtherApps:YES];
1725             NSDisableScreenUpdates();
1727             if ([self becameEligibleParentOrChild])
1728                 needAdjustWindowLevels = TRUE;
1730             if (prev || next)
1731             {
1732                 WineWindow* other = [prev isVisible] ? prev : next;
1733                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1735                 if (![self isOrdered:orderingMode relativeTo:other])
1736                 {
1737                     WineWindow* ancestor;
1738                     WineWindow* ancestorOfOther;
1740                     [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1741                     if (ancestor)
1742                     {
1743                         [self setAutodisplay:YES];
1744                         if (ancestorOfOther)
1745                         {
1746                             // This window level may not be right for this window based
1747                             // on floating-ness, fullscreen-ness, etc.  But we set it
1748                             // temporarily to allow us to order the windows properly.
1749                             // Then the levels get fixed by -adjustWindowLevels.
1750                             if ([ancestor level] != [ancestorOfOther level])
1751                                 [ancestor setLevel:[ancestorOfOther level]];
1753                             parent = (WineWindow*)ancestor.parentWindow;
1754                             if ([parent isKindOfClass:[WineWindow class]])
1755                                 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1756                             else
1757                                 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1758                         }
1760                         if (!ancestorOfOther || ancestor != self)
1761                         {
1762                             for (child = self;
1763                                  (parent = (WineWindow*)child.parentWindow);
1764                                  child = parent)
1765                             {
1766                                 if ([parent isKindOfClass:[WineWindow class]])
1767                                     [parent order:-orderingMode childWindow:child relativeTo:nil];
1768                                 if (parent == ancestor)
1769                                     break;
1770                             }
1771                         }
1773                         [self checkWineDisplayLink];
1774                         needAdjustWindowLevels = TRUE;
1775                     }
1776                 }
1777             }
1778             else
1779             {
1780                 for (child = self;
1781                      (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1782                      child = parent)
1783                 {
1784                     [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1785                 }
1787                 // Again, temporarily set level to make sure we can order to
1788                 // the right place.
1789                 next = [controller frontWineWindow];
1790                 if (next && [self level] < [next level])
1791                     [self setLevel:[next level]];
1792                 [self setAutodisplay:YES];
1793                 [self orderFront:nil];
1794                 [self checkWineDisplayLink];
1795                 needAdjustWindowLevels = TRUE;
1796             }
1797             pendingOrderOut = FALSE;
1799             if ([self becameEligibleParentOrChild])
1800                 needAdjustWindowLevels = TRUE;
1802             if (needAdjustWindowLevels)
1803             {
1804                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1805                     [controller updateFullscreenWindows];
1806                 [controller adjustWindowLevels];
1807             }
1809             if (pendingMinimize)
1810             {
1811                 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1812                 [super miniaturize:nil];
1813                 pendingMinimize = FALSE;
1814             }
1816             NSEnableScreenUpdates();
1818             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1819                Generate a frame-changed event just in case.  The back end will ignore
1820                it if nothing actually changed. */
1821             [self windowDidResize:nil skipSizeMove:TRUE];
1823             if (![self isExcludedFromWindowsMenu])
1824                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1825         }
1826     }
1828     - (void) doOrderOut
1829     {
1830         WineApplicationController* controller = [WineApplicationController sharedController];
1831         BOOL wasVisible = [self isVisible];
1832         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1834         [self endWindowDragging];
1835         [controller windowWillOrderOut:self];
1837         if (enteringFullScreen || exitingFullScreen)
1838         {
1839             pendingOrderOut = TRUE;
1840             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1841                                              event_mask_for_type(WINDOW_GOT_FOCUS) |
1842                                              event_mask_for_type(WINDOW_LOST_FOCUS) |
1843                                              event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1844                                              event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1845                                              event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1846                                    forWindow:self];
1847             return;
1848         }
1850         pendingOrderOut = FALSE;
1852         if ([self isMiniaturized])
1853             pendingMinimize = TRUE;
1855         WineWindow* parent = (WineWindow*)self.parentWindow;
1856         if ([parent isKindOfClass:[WineWindow class]])
1857             [parent grabDockIconSnapshotFromWindow:self force:NO];
1859         [self becameIneligibleParentOrChild];
1860         if ([self isMiniaturized] || [self styleMask] & NSWindowStyleMaskFullScreen)
1861         {
1862             fakingClose = TRUE;
1863             [self close];
1864             fakingClose = FALSE;
1865         }
1866         else
1867             [self orderOut:nil];
1868         [self checkWineDisplayLink];
1869         [self setBackgroundColor:[NSColor clearColor]];
1870         [self setOpaque:NO];
1871         drawnSinceShown = NO;
1872         savedVisibleState = FALSE;
1873         if (wasVisible && wasOnActiveSpace && fullscreen)
1874             [controller updateFullscreenWindows];
1875         [controller adjustWindowLevels];
1876         [NSApp removeWindowsItem:self];
1878         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1879                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1880                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1881                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1882                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1883                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1884                                forWindow:self];
1885     }
1887     - (void) updateFullscreen
1888     {
1889         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1890         BOOL nowFullscreen = !([self styleMask] & NSWindowStyleMaskFullScreen) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1892         if (nowFullscreen != fullscreen)
1893         {
1894             WineApplicationController* controller = [WineApplicationController sharedController];
1896             fullscreen = nowFullscreen;
1897             if ([self isVisible] && [self isOnActiveSpace])
1898                 [controller updateFullscreenWindows];
1900             [controller adjustWindowLevels];
1901         }
1902     }
1904     - (void) setFrameAndWineFrame:(NSRect)frame
1905     {
1906         [self setFrame:frame display:YES];
1908         wineFrame = frame;
1909         roundedWineFrame = self.frame;
1910         CGFloat junk;
1911 #if CGFLOAT_IS_DOUBLE
1912         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1913              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1914             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1915             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1916             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1917             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1918             roundedWineFrame = wineFrame;
1919 #else
1920         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1921              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1922             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1923             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1924             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1925             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1926             roundedWineFrame = wineFrame;
1927 #endif
1928     }
1930     - (void) setFrameFromWine:(NSRect)contentRect
1931     {
1932         /* Origin is (left, top) in a top-down space.  Need to convert it to
1933            (left, bottom) in a bottom-up space. */
1934         [[WineApplicationController sharedController] flipRect:&contentRect];
1936         /* The back end is establishing a new window size and position.  It's
1937            not interested in any stale events regarding those that may be sitting
1938            in the queue. */
1939         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1940                                forWindow:self];
1942         if (!NSIsEmptyRect(contentRect))
1943         {
1944             NSRect frame, oldFrame;
1946             oldFrame = self.wine_fractionalFrame;
1947             frame = [self frameRectForContentRect:contentRect];
1948             if (!NSEqualRects(frame, oldFrame))
1949             {
1950                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1951                 BOOL needEnableScreenUpdates = FALSE;
1953                 if ([self preventResizing])
1954                 {
1955                     // Allow the following calls to -setFrame:display: to work even
1956                     // if they would violate the content size constraints. This
1957                     // shouldn't be necessary since the content size constraints are
1958                     // documented to not constrain that method, but it seems to be.
1959                     [self setContentMinSize:NSZeroSize];
1960                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1961                 }
1963                 if (equalSizes && [[self childWineWindows] count])
1964                 {
1965                     // If we change the window frame such that the origin moves
1966                     // but the size doesn't change, then Cocoa moves child
1967                     // windows with the parent.  We don't want that so we fake
1968                     // a change of the size and then change it back.
1969                     NSRect bogusFrame = frame;
1970                     bogusFrame.size.width++;
1972                     NSDisableScreenUpdates();
1973                     needEnableScreenUpdates = TRUE;
1975                     ignore_windowResize = TRUE;
1976                     [self setFrame:bogusFrame display:NO];
1977                     ignore_windowResize = FALSE;
1978                 }
1980                 [self setFrameAndWineFrame:frame];
1981                 if ([self preventResizing])
1982                 {
1983                     [self setContentMinSize:contentRect.size];
1984                     [self setContentMaxSize:contentRect.size];
1985                 }
1987                 if (needEnableScreenUpdates)
1988                     NSEnableScreenUpdates();
1990                 if (!enteringFullScreen &&
1991                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1992                     nonFullscreenFrame = frame;
1994                 [self updateFullscreen];
1996                 if ([self isOrderedIn])
1997                 {
1998                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1999                        event.  The back end will ignore it if nothing actually changed. */
2000                     [self windowDidResize:nil skipSizeMove:TRUE];
2001                 }
2002             }
2003         }
2004     }
2006     - (NSRect) wine_fractionalFrame
2007     {
2008         NSRect frame = self.frame;
2009         if (NSEqualRects(frame, roundedWineFrame))
2010             frame = wineFrame;
2011         return frame;
2012     }
2014     - (void) setMacDrvParentWindow:(WineWindow*)parent
2015     {
2016         WineWindow* oldParent = (WineWindow*)[self parentWindow];
2017         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
2018         {
2019             [oldParent removeChildWineWindow:self];
2020             [latentParentWindow removeChildWineWindow:self];
2021             if ([parent addChildWineWindow:self])
2022                 [[WineApplicationController sharedController] adjustWindowLevels];
2023             [self adjustFullScreenBehavior:[self collectionBehavior]];
2024         }
2025     }
2027     - (void) setDisabled:(BOOL)newValue
2028     {
2029         if (disabled != newValue)
2030         {
2031             disabled = newValue;
2032             [self adjustFeaturesForState];
2033         }
2034     }
2036     - (BOOL) needsTransparency
2037     {
2038         return self.contentView.layer.mask || self.colorKeyed || self.usePerPixelAlpha ||
2039                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
2040     }
2042     - (void) checkTransparency
2043     {
2044         if (![self isOpaque] && !self.needsTransparency)
2045         {
2046             self.shapeChangedSinceLastDraw = TRUE;
2047             [[self contentView] setNeedsDisplay:YES];
2048             [self setBackgroundColor:[NSColor windowBackgroundColor]];
2049             [self setOpaque:YES];
2050         }
2051         else if ([self isOpaque] && self.needsTransparency)
2052         {
2053             self.shapeChangedSinceLastDraw = TRUE;
2054             [[self contentView] setNeedsDisplay:YES];
2055             [self setBackgroundColor:[NSColor clearColor]];
2056             [self setOpaque:NO];
2057         }
2058     }
2060     - (void) setShape:(CGPathRef)newShape
2061     {
2062         CALayer* layer = [[self contentView] layer];
2063         CAShapeLayer* mask = (CAShapeLayer*)layer.mask;
2064         if (CGPathEqualToPath(newShape, mask.path)) return;
2066         if (newShape && !layer.mask)
2067             layer.mask = mask = [CAShapeLayer layer];
2068         else if (!newShape)
2069             layer.mask = mask = nil;
2071         if (mask.path)
2072             [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(mask.path))];
2073         if (newShape)
2074             [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(newShape))];
2076         mask.path = newShape;
2077         self.shapeChangedSinceLastDraw = TRUE;
2079         [self checkTransparency];
2080         [self checkEmptyShaped];
2081     }
2083     - (void) makeFocused:(BOOL)activate
2084     {
2085         if (activate)
2086         {
2087             WineApplicationController *controller = [WineApplicationController sharedController];
2088             [controller transformProcessToForeground:YES];
2089             [controller tryToActivateIgnoringOtherApps:YES];
2090         }
2092         causing_becomeKeyWindow = self;
2093         [self makeKeyWindow];
2094         causing_becomeKeyWindow = nil;
2096         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2097                                          event_mask_for_type(WINDOW_LOST_FOCUS)
2098                                forWindow:self];
2099     }
2101     - (void) postKey:(uint16_t)keyCode
2102              pressed:(BOOL)pressed
2103            modifiers:(NSUInteger)modifiers
2104                event:(NSEvent*)theEvent
2105     {
2106         macdrv_event* event;
2107         CGEventRef cgevent;
2108         WineApplicationController* controller = [WineApplicationController sharedController];
2110         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2111         event->key.keycode   = keyCode;
2112         event->key.modifiers = modifiers;
2113         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
2115         if ((cgevent = [theEvent CGEvent]))
2116             controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2118         [queue postEvent:event];
2120         macdrv_release_event(event);
2122         [controller noteKey:keyCode pressed:pressed];
2123     }
2125     - (void) postKeyEvent:(NSEvent *)theEvent
2126     {
2127         [self flagsChanged:theEvent];
2128         [self postKey:[theEvent keyCode]
2129               pressed:[theEvent type] == NSEventTypeKeyDown
2130             modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2131                 event:theEvent];
2132     }
2134     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2135     {
2136         savedContentMinSize = minSize;
2137         savedContentMaxSize = maxSize;
2138         if (![self preventResizing])
2139         {
2140             [self setContentMinSize:minSize];
2141             [self setContentMaxSize:maxSize];
2142         }
2143     }
2145     - (WineWindow*) ancestorWineWindow
2146     {
2147         WineWindow* ancestor = self;
2148         for (;;)
2149         {
2150             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2151             if ([parent isKindOfClass:[WineWindow class]])
2152                 ancestor = parent;
2153             else
2154                 break;
2155         }
2156         return ancestor;
2157     }
2159     - (void) postBroughtForwardEvent
2160     {
2161         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2162         [queue postEvent:event];
2163         macdrv_release_event(event);
2164     }
2166     - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing skipSizeMove:(BOOL)skipSizeMove
2167     {
2168         macdrv_event* event;
2169         NSUInteger style = self.styleMask;
2171         if (isFullscreen)
2172             style |= NSWindowStyleMaskFullScreen;
2173         else
2174             style &= ~NSWindowStyleMaskFullScreen;
2175         frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2176         [[WineApplicationController sharedController] flipRect:&frame];
2178         /* Coalesce events by discarding any previous ones still in the queue. */
2179         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2180                                forWindow:self];
2182         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2183         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2184         event->window_frame_changed.fullscreen = isFullscreen;
2185         event->window_frame_changed.in_resize = resizing;
2186         event->window_frame_changed.skip_size_move_loop = skipSizeMove;
2187         [queue postEvent:event];
2188         macdrv_release_event(event);
2189     }
2191     - (void) updateForCursorClipping
2192     {
2193         [self adjustFeaturesForState];
2194     }
2196     - (void) endWindowDragging
2197     {
2198         if (draggingPhase)
2199         {
2200             if (draggingPhase == 3)
2201             {
2202                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2203                 [queue postEvent:event];
2204                 macdrv_release_event(event);
2205             }
2207             draggingPhase = 0;
2208             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2209         }
2210     }
2212     - (NSMutableDictionary*) displayIDToDisplayLinkMap
2213     {
2214         static NSMutableDictionary* displayIDToDisplayLinkMap;
2215         if (!displayIDToDisplayLinkMap)
2216         {
2217             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2219             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2220                                                               object:NSApp
2221                                                                queue:nil
2222                                                           usingBlock:^(NSNotification *note){
2223                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2224                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2225                 [badDisplayIDs minusSet:validDisplayIDs];
2226                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2227             }];
2228         }
2229         return displayIDToDisplayLinkMap;
2230     }
2232     - (WineDisplayLink*) wineDisplayLink
2233     {
2234         if (!_lastDisplayID)
2235             return nil;
2237         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2238         return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2239     }
2241     - (void) checkWineDisplayLink
2242     {
2243         NSScreen* screen = self.screen;
2244         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2245             screen = nil;
2246 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2247         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2248             screen = nil;
2249 #endif
2251         NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2252         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2253         if (displayID == _lastDisplayID)
2254             return;
2256         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2258         if (_lastDisplayID)
2259         {
2260             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2261             [link removeWindow:self];
2262         }
2263         if (displayID)
2264         {
2265             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2266             if (!link)
2267             {
2268                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2269                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2270             }
2271             [link addWindow:self];
2272             [self displayIfNeeded];
2273         }
2274         _lastDisplayID = displayID;
2275     }
2277     - (BOOL) isEmptyShaped
2278     {
2279         CAShapeLayer* mask = (CAShapeLayer*)[[self contentView] layer].mask;
2280         return ([mask isEmptyShaped]);
2281     }
2283     - (BOOL) canProvideSnapshot
2284     {
2285         return (self.windowNumber > 0 && ![self isEmptyShaped]);
2286     }
2288     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2289     {
2290         if (![self isEmptyShaped])
2291             return;
2293         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2294         if (!force && now < lastDockIconSnapshot + 1)
2295             return;
2297         if (window)
2298         {
2299             if (![window canProvideSnapshot])
2300                 return;
2301         }
2302         else
2303         {
2304             CGFloat bestArea;
2305             for (WineWindow* childWindow in self.childWindows)
2306             {
2307                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2308                     continue;
2310                 NSSize size = childWindow.frame.size;
2311                 CGFloat area = size.width * size.height;
2312                 if (!window || area > bestArea)
2313                 {
2314                     window = childWindow;
2315                     bestArea = area;
2316                 }
2317             }
2319             if (!window)
2320                 return;
2321         }
2323         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2324         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2325         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2326         CFRelease(windowIDs);
2327         if (!windowImage)
2328             return;
2330         NSImage* appImage = [NSApp applicationIconImage];
2331         if (!appImage)
2332             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2334         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2335         [dockIcon lockFocus];
2337         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2339         CGRect rect = CGRectMake(8, 8, 240, 240);
2340         size_t width = CGImageGetWidth(windowImage);
2341         size_t height = CGImageGetHeight(windowImage);
2342         if (width > height)
2343         {
2344             rect.size.height *= height / (double)width;
2345             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2346         }
2347         else if (width != height)
2348         {
2349             rect.size.width *= width / (double)height;
2350             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2351         }
2353         CGContextDrawImage(cgcontext, rect, windowImage);
2354         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2355                     fromRect:NSZeroRect
2356                    operation:NSCompositingOperationSourceOver
2357                     fraction:1
2358               respectFlipped:YES
2359                        hints:nil];
2361         [dockIcon unlockFocus];
2363         CGImageRelease(windowImage);
2365         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2366         if (![imageView isKindOfClass:[NSImageView class]])
2367         {
2368             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2369             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2370             self.dockTile.contentView = imageView;
2371         }
2372         imageView.image = dockIcon;
2373         [self.dockTile display];
2374         lastDockIconSnapshot = now;
2375     }
2377     - (void) checkEmptyShaped
2378     {
2379         if (self.dockTile.contentView && ![self isEmptyShaped])
2380         {
2381             self.dockTile.contentView = nil;
2382             lastDockIconSnapshot = 0;
2383         }
2384         [self checkWineDisplayLink];
2385     }
2388     /*
2389      * ---------- NSWindow method overrides ----------
2390      */
2391     - (BOOL) canBecomeKeyWindow
2392     {
2393         if (causing_becomeKeyWindow == self) return YES;
2394         if (self.disabled || self.noForeground) return NO;
2395         if ([self isKeyWindow]) return YES;
2397         // If a window's collectionBehavior says it participates in cycling,
2398         // it must return YES from this method to actually be eligible.
2399         return ![self isExcludedFromWindowsMenu];
2400     }
2402     - (BOOL) canBecomeMainWindow
2403     {
2404         return [self canBecomeKeyWindow];
2405     }
2407     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2408     {
2409         // If a window is sized to completely cover a screen, then it's in
2410         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2411         // it.
2412         NSArray* screens = [NSScreen screens];
2413         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2414         if (!screen_covered_by_rect(contentRect, screens) &&
2415             frame_intersects_screens(frameRect, screens))
2416             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2417         return frameRect;
2418     }
2420     // This private method of NSWindow is called as Cocoa reacts to the display
2421     // configuration changing.  Among other things, it adjusts the window's
2422     // frame based on how the screen(s) changed size.  That tells Wine that the
2423     // window has been moved.  We don't want that.  Rather, we want to make
2424     // sure that the WinAPI notion of the window position is maintained/
2425     // restored, possibly undoing or overriding Cocoa's adjustment.
2426     //
2427     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2428     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2429     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2430     // reassert its notion of the position.  That call won't get processed
2431     // until after this method returns, so it will override whatever this
2432     // method does to the window position.  It will also discard any pending
2433     // WINDOW_FRAME_CHANGED events.
2434     //
2435     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2436     // adjust the window's position due to a display change is to hook into
2437     // this private method.  This private method has remained stable from 10.6
2438     // through 10.11.  If it does change, the most likely thing is that it
2439     // will be removed and no longer called and this fix will simply stop
2440     // working.  The only real danger would be if Apple changed the return type
2441     // to a struct or floating-point type, which would change the calling
2442     // convention.
2443     - (id) _displayChanged
2444     {
2445         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2446         [queue postEvent:event];
2447         macdrv_release_event(event);
2449         return [super _displayChanged];
2450     }
2452     - (BOOL) isExcludedFromWindowsMenu
2453     {
2454         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2455     }
2457     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2458     {
2459         BOOL ret = [super validateMenuItem:menuItem];
2461         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2462             ret = [self isKeyWindow] || (!self.disabled && !self.noForeground);
2463         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2464             ret = NO;
2466         return ret;
2467     }
2469     /* We don't call this.  It's the action method of the items in the Window menu. */
2470     - (void) makeKeyAndOrderFront:(id)sender
2471     {
2472         if ([self isMiniaturized])
2473             [self deminiaturize:nil];
2474         [self orderBelow:nil orAbove:nil activate:NO];
2475         [[self ancestorWineWindow] postBroughtForwardEvent];
2477         if (![self isKeyWindow] && !self.disabled && !self.noForeground)
2478             [[WineApplicationController sharedController] windowGotFocus:self];
2479     }
2481     - (void) sendEvent:(NSEvent*)event
2482     {
2483         NSEventType type = event.type;
2485         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2486            interface control.  For example, Control-Tab switches focus among
2487            views.  We want to bypass that feature, so directly route key-down
2488            events to -keyDown:. */
2489         if (type == NSEventTypeKeyDown)
2490             [[self firstResponder] keyDown:event];
2491         else
2492         {
2493             if (!draggingPhase && maximized && ![self isMovable] &&
2494                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2495                 type == NSEventTypeLeftMouseDown && (self.styleMask & NSWindowStyleMaskTitled))
2496             {
2497                 NSRect titleBar = self.frame;
2498                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2499                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2500                 titleBar.origin.y = NSMaxY(contentRect);
2502                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2504                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2505                 {
2506                     static const NSWindowButton buttons[] = {
2507                         NSWindowCloseButton,
2508                         NSWindowMiniaturizeButton,
2509                         NSWindowZoomButton,
2510                         NSWindowFullScreenButton,
2511                     };
2512                     BOOL hitButton = NO;
2513                     int i;
2515                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2516                     {
2517                         NSButton* button = [self standardWindowButton:buttons[i]];
2518                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2519                         {
2520                             hitButton = YES;
2521                             break;
2522                         }
2523                     }
2525                     if (!hitButton)
2526                     {
2527                         draggingPhase = 1;
2528                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2529                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2530                     }
2531                 }
2532             }
2533             else if (draggingPhase && (type == NSEventTypeLeftMouseDragged || type == NSEventTypeLeftMouseUp))
2534             {
2535                 if ([self isMovable])
2536                 {
2537                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2538                     NSPoint newTopLeft = dragWindowStartPosition;
2540                     newTopLeft.x += point.x - dragStartPosition.x;
2541                     newTopLeft.y += point.y - dragStartPosition.y;
2543                     if (draggingPhase == 2)
2544                     {
2545                         macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2546                         mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2547                         [queue postEvent:mevent];
2548                         macdrv_release_event(mevent);
2550                         draggingPhase = 3;
2551                     }
2553                     [self setFrameTopLeftPoint:newTopLeft];
2554                 }
2555                 else if (draggingPhase == 1 && type == NSEventTypeLeftMouseDragged)
2556                 {
2557                     macdrv_event* event;
2558                     NSRect frame = [self contentRectForFrameRect:self.frame];
2560                     [[WineApplicationController sharedController] flipRect:&frame];
2562                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2563                     event->window_restore_requested.keep_frame = TRUE;
2564                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2565                     [queue postEvent:event];
2566                     macdrv_release_event(event);
2568                     draggingPhase = 2;
2569                 }
2571                 if (type == NSEventTypeLeftMouseUp)
2572                     [self endWindowDragging];
2573             }
2575             [super sendEvent:event];
2576         }
2577     }
2579     - (void) miniaturize:(id)sender
2580     {
2581         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2582         [queue postEvent:event];
2583         macdrv_release_event(event);
2585         WineWindow* parent = (WineWindow*)self.parentWindow;
2586         if ([parent isKindOfClass:[WineWindow class]])
2587             [parent grabDockIconSnapshotFromWindow:self force:YES];
2588     }
2590     - (void) toggleFullScreen:(id)sender
2591     {
2592         if (!self.disabled && !maximized)
2593             [super toggleFullScreen:sender];
2594     }
2596     - (void) setViewsNeedDisplay:(BOOL)value
2597     {
2598         if (value && ![self viewsNeedDisplay])
2599         {
2600             WineDisplayLink* link = [self wineDisplayLink];
2601             if (link)
2602             {
2603                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2604                 if (_lastDisplayTime + [link refreshPeriod] < now)
2605                     [self setAutodisplay:YES];
2606                 else
2607                 {
2608                     [link start];
2609                     _lastDisplayTime = now;
2610                 }
2611             }
2612             else
2613                 [self setAutodisplay:YES];
2614         }
2615         [super setViewsNeedDisplay:value];
2616     }
2618     - (void) display
2619     {
2620         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2621         [super display];
2622         if (_lastDisplayID)
2623             [self setAutodisplay:NO];
2624     }
2626     - (void) displayIfNeeded
2627     {
2628         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2629         [super displayIfNeeded];
2630         if (_lastDisplayID)
2631             [self setAutodisplay:NO];
2632     }
2634     - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2635     {
2636         if (flag)
2637             [self setAutodisplay:YES];
2638         [super setFrame:frameRect display:flag];
2639     }
2641     - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2642     {
2643         if (displayFlag)
2644             [self setAutodisplay:YES];
2645         [super setFrame:frameRect display:displayFlag animate:animateFlag];
2646     }
2648     - (void) windowDidDrawContent
2649     {
2650         if (!drawnSinceShown)
2651         {
2652             drawnSinceShown = YES;
2653             dispatch_async(dispatch_get_main_queue(), ^{
2654                 [self checkTransparency];
2655             });
2656         }
2657     }
2659     - (NSArray*) childWineWindows
2660     {
2661         NSArray* childWindows = self.childWindows;
2662         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2663             return [child isKindOfClass:[WineWindow class]];
2664         }];
2665         return [childWindows objectsAtIndexes:indexes];
2666     }
2668     - (void) updateForGLSubviews
2669     {
2670         if (gl_surface_mode == GL_SURFACE_BEHIND)
2671             [self checkTransparency];
2672     }
2674     - (void) setRetinaMode:(int)mode
2675     {
2676         NSRect frame;
2677         double scale = mode ? 0.5 : 2.0;
2678         NSAffineTransform* transform = [NSAffineTransform transform];
2680         [transform scaleBy:scale];
2682         [[self contentView] layer].mask.contentsScale = mode ? 2.0 : 1.0;
2684         for (WineBaseView* subview in [self.contentView subviews])
2685         {
2686             if ([subview isKindOfClass:[WineBaseView class]])
2687                 [subview setRetinaMode:mode];
2688         }
2690         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2691         frame.origin.x *= scale;
2692         frame.origin.y *= scale;
2693         frame.size.width *= scale;
2694         frame.size.height *= scale;
2695         frame = [self frameRectForContentRect:frame];
2697         savedContentMinSize = [transform transformSize:savedContentMinSize];
2698         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2699             savedContentMaxSize.width *= scale;
2700         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2701             savedContentMaxSize.height *= scale;
2703         self.contentMinSize = [transform transformSize:self.contentMinSize];
2704         NSSize temp = self.contentMaxSize;
2705         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2706             temp.width *= scale;
2707         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2708             temp.height *= scale;
2709         self.contentMaxSize = temp;
2711         ignore_windowResize = TRUE;
2712         [self setFrameAndWineFrame:frame];
2713         ignore_windowResize = FALSE;
2714     }
2717     /*
2718      * ---------- NSResponder method overrides ----------
2719      */
2720     - (void) keyDown:(NSEvent *)theEvent
2721     {
2722         if ([theEvent isARepeat])
2723         {
2724             if (!allowKeyRepeats)
2725                 return;
2726         }
2727         else
2728             allowKeyRepeats = YES;
2730         [self postKeyEvent:theEvent];
2731     }
2733     - (void) flagsChanged:(NSEvent *)theEvent
2734     {
2735         static const struct {
2736             NSUInteger  mask;
2737             uint16_t    keycode;
2738         } modifiers[] = {
2739             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2740             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2741             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2742             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2743             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2744             { NX_DEVICELALTKEYMASK,     kVK_Option },
2745             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2746             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2747             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2748         };
2750         NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2751         NSUInteger changed;
2752         int i, last_changed;
2754         fix_device_modifiers_by_generic(&modifierFlags);
2755         changed = modifierFlags ^ lastModifierFlags;
2757         last_changed = -1;
2758         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2759             if (changed & modifiers[i].mask)
2760                 last_changed = i;
2762         for (i = 0; i <= last_changed; i++)
2763         {
2764             if (changed & modifiers[i].mask)
2765             {
2766                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2768                 if (pressed)
2769                     allowKeyRepeats = NO;
2771                 if (i == last_changed)
2772                     lastModifierFlags = modifierFlags;
2773                 else
2774                 {
2775                     lastModifierFlags ^= modifiers[i].mask;
2776                     fix_generic_modifiers_by_device(&lastModifierFlags);
2777                 }
2779                 // Caps lock generates one event for each press-release action.
2780                 // We need to simulate a pair of events for each actual event.
2781                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2782                 {
2783                     [self postKey:modifiers[i].keycode
2784                           pressed:TRUE
2785                         modifiers:lastModifierFlags
2786                             event:(NSEvent*)theEvent];
2787                     pressed = FALSE;
2788                 }
2790                 [self postKey:modifiers[i].keycode
2791                       pressed:pressed
2792                     modifiers:lastModifierFlags
2793                         event:(NSEvent*)theEvent];
2794             }
2795         }
2796     }
2798     - (void) applicationWillHide
2799     {
2800         savedVisibleState = [self isVisible];
2801     }
2803     - (void) applicationDidUnhide
2804     {
2805         if ([self isVisible])
2806             [self becameEligibleParentOrChild];
2807     }
2810     /*
2811      * ---------- NSWindowDelegate methods ----------
2812      */
2813     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2814     {
2815         macdrv_query* query;
2816         NSSize size;
2818         query = macdrv_create_query();
2819         query->type = QUERY_MIN_MAX_INFO;
2820         query->window = (macdrv_window)[self retain];
2821         [self.queue query:query timeout:0.5];
2822         macdrv_release_query(query);
2824         size = [self contentMaxSize];
2825         if (proposedSize.width < size.width)
2826             size.width = proposedSize.width;
2827         if (proposedSize.height < size.height)
2828             size.height = proposedSize.height;
2829         return size;
2830     }
2832     - (void)windowDidBecomeKey:(NSNotification *)notification
2833     {
2834         WineApplicationController* controller = [WineApplicationController sharedController];
2835         NSEvent* event = [controller lastFlagsChanged];
2836         if (event)
2837             [self flagsChanged:event];
2839         if (causing_becomeKeyWindow == self) return;
2841         [controller windowGotFocus:self];
2842     }
2844     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2845     {
2846         [self checkWineDisplayLink];
2847     }
2849     - (void) windowDidChangeScreen:(NSNotification*)notification
2850     {
2851         [self checkWineDisplayLink];
2852     }
2854     - (void)windowDidDeminiaturize:(NSNotification *)notification
2855     {
2856         WineApplicationController* controller = [WineApplicationController sharedController];
2858         if (!ignore_windowDeminiaturize)
2859             [self postDidUnminimizeEvent];
2860         ignore_windowDeminiaturize = FALSE;
2862         [self becameEligibleParentOrChild];
2864         if (fullscreen && [self isOnActiveSpace])
2865             [controller updateFullscreenWindows];
2866         [controller adjustWindowLevels];
2868         if (![self parentWindow])
2869             [self postBroughtForwardEvent];
2871         if (!self.disabled && !self.noForeground)
2872         {
2873             causing_becomeKeyWindow = self;
2874             [self makeKeyWindow];
2875             causing_becomeKeyWindow = nil;
2876             [controller windowGotFocus:self];
2877         }
2879         [self windowDidResize:notification];
2880         [self checkWineDisplayLink];
2881     }
2883     - (void) windowDidEndLiveResize:(NSNotification *)notification
2884     {
2885         if (!maximized)
2886         {
2887             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2888             [queue postEvent:event];
2889             macdrv_release_event(event);
2890         }
2891     }
2893     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2894     {
2895         enteringFullScreen = FALSE;
2896         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2897         if (pendingOrderOut)
2898             [self doOrderOut];
2899     }
2901     - (void) windowDidExitFullScreen:(NSNotification*)notification
2902     {
2903         exitingFullScreen = FALSE;
2904         [self setFrameAndWineFrame:nonFullscreenFrame];
2905         [self windowDidResize:nil];
2906         if (pendingOrderOut)
2907             [self doOrderOut];
2908     }
2910     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2911     {
2912         enteringFullScreen = FALSE;
2913         enteredFullScreenTime = 0;
2914         if (pendingOrderOut)
2915             [self doOrderOut];
2916     }
2918     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2919     {
2920         exitingFullScreen = FALSE;
2921         [self windowDidResize:nil];
2922         if (pendingOrderOut)
2923             [self doOrderOut];
2924     }
2926     - (void)windowDidMiniaturize:(NSNotification *)notification
2927     {
2928         macdrv_event* event;
2930         if (fullscreen && [self isOnActiveSpace])
2931             [[WineApplicationController sharedController] updateFullscreenWindows];
2933         [self checkWineDisplayLink];
2935         event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
2936         [queue postEvent:event];
2937         macdrv_release_event(event);
2938     }
2940     - (void)windowDidMove:(NSNotification *)notification
2941     {
2942         [self windowDidResize:notification];
2943     }
2945     - (void)windowDidResignKey:(NSNotification *)notification
2946     {
2947         macdrv_event* event;
2949         if (causing_becomeKeyWindow) return;
2951         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2952         [queue postEvent:event];
2953         macdrv_release_event(event);
2954     }
2956     - (void)windowDidResize:(NSNotification *)notification skipSizeMove:(BOOL)skipSizeMove
2957     {
2958         NSRect frame = self.wine_fractionalFrame;
2960         if ([self inLiveResize])
2961         {
2962             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2963                 resizingFromLeft = TRUE;
2964             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2965                 resizingFromTop = TRUE;
2966         }
2968         if (ignore_windowResize || exitingFullScreen) return;
2970         if ([self preventResizing])
2971         {
2972             NSRect contentRect = [self contentRectForFrameRect:frame];
2973             [self setContentMinSize:contentRect.size];
2974             [self setContentMaxSize:contentRect.size];
2975         }
2977         [self postWindowFrameChanged:frame
2978                           fullscreen:([self styleMask] & NSWindowStyleMaskFullScreen) != 0
2979                             resizing:[self inLiveResize]
2980                         skipSizeMove:skipSizeMove];
2982         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2983         [self updateFullscreen];
2984     }
2986     - (void)windowDidResize:(NSNotification *)notification
2987     {
2988         [self windowDidResize:notification skipSizeMove:FALSE];
2989     }
2991     - (BOOL)windowShouldClose:(id)sender
2992     {
2993         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2994         [queue postEvent:event];
2995         macdrv_release_event(event);
2996         return NO;
2997     }
2999     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
3000     {
3001         if (maximized)
3002         {
3003             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3004             [queue postEvent:event];
3005             macdrv_release_event(event);
3006             return NO;
3007         }
3008         else if (!resizable)
3009         {
3010             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
3011             [queue postEvent:event];
3012             macdrv_release_event(event);
3013             return NO;
3014         }
3016         return YES;
3017     }
3019     - (void) windowWillClose:(NSNotification*)notification
3020     {
3021         WineWindow* child;
3023         if (fakingClose) return;
3024         if (latentParentWindow)
3025         {
3026             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
3027             self.latentParentWindow = nil;
3028         }
3030         for (child in latentChildWindows)
3031         {
3032             if (child.latentParentWindow == self)
3033                 child.latentParentWindow = nil;
3034         }
3035         [latentChildWindows removeAllObjects];
3036     }
3038     - (void) windowWillEnterFullScreen:(NSNotification*)notification
3039     {
3040         enteringFullScreen = TRUE;
3041         nonFullscreenFrame = self.wine_fractionalFrame;
3042     }
3044     - (void) windowWillExitFullScreen:(NSNotification*)notification
3045     {
3046         exitingFullScreen = TRUE;
3047         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE skipSizeMove:FALSE];
3048     }
3050     - (void)windowWillMiniaturize:(NSNotification *)notification
3051     {
3052         [self becameIneligibleParentOrChild];
3053         [self grabDockIconSnapshotFromWindow:nil force:NO];
3054     }
3056     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
3057     {
3058         if ([self inLiveResize])
3059         {
3060             if (maximized)
3061                 return self.wine_fractionalFrame.size;
3063             NSRect rect;
3064             macdrv_query* query;
3066             rect = [self frame];
3067             if (resizingFromLeft)
3068                 rect.origin.x = NSMaxX(rect) - frameSize.width;
3069             if (!resizingFromTop)
3070                 rect.origin.y = NSMaxY(rect) - frameSize.height;
3071             rect.size = frameSize;
3072             rect = [self contentRectForFrameRect:rect];
3073             [[WineApplicationController sharedController] flipRect:&rect];
3075             query = macdrv_create_query();
3076             query->type = QUERY_RESIZE_SIZE;
3077             query->window = (macdrv_window)[self retain];
3078             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3079             query->resize_size.from_left = resizingFromLeft;
3080             query->resize_size.from_top = resizingFromTop;
3082             if ([self.queue query:query timeout:0.1])
3083             {
3084                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3085                 rect = [self frameRectForContentRect:rect];
3086                 frameSize = rect.size;
3087             }
3089             macdrv_release_query(query);
3090         }
3092         return frameSize;
3093     }
3095     - (void) windowWillStartLiveResize:(NSNotification *)notification
3096     {
3097         [self endWindowDragging];
3099         if (maximized)
3100         {
3101             macdrv_event* event;
3102             NSRect frame = [self contentRectForFrameRect:self.frame];
3104             [[WineApplicationController sharedController] flipRect:&frame];
3106             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3107             event->window_restore_requested.keep_frame = TRUE;
3108             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3109             [queue postEvent:event];
3110             macdrv_release_event(event);
3111         }
3112         else
3113             [self sendResizeStartQuery];
3115         frameAtResizeStart = [self frame];
3116         resizingFromLeft = resizingFromTop = FALSE;
3117     }
3119     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3120     {
3121         macdrv_query* query;
3122         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3123         NSSize maxSize;
3125         query = macdrv_create_query();
3126         query->type = QUERY_MIN_MAX_INFO;
3127         query->window = (macdrv_window)[self retain];
3128         [self.queue query:query timeout:0.5];
3129         macdrv_release_query(query);
3131         currentContentRect = [self contentRectForFrameRect:[self frame]];
3132         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3134         maxSize = [self contentMaxSize];
3135         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3136         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3138         // Try to keep the top-left corner where it is.
3139         newContentRect.origin.x = NSMinX(currentContentRect);
3140         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3142         // If that pushes the bottom or right off the screen, pull it up and to the left.
3143         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3144         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3145             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3146         if (NSMinY(newContentRect) < NSMinY(screenRect))
3147             newContentRect.origin.y = NSMinY(screenRect);
3149         // If that pushes the top or left off the screen, push it down and the right
3150         // again.  Do this last because the top-left corner is more important than the
3151         // bottom-right.
3152         if (NSMinX(newContentRect) < NSMinX(screenRect))
3153             newContentRect.origin.x = NSMinX(screenRect);
3154         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3155             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3157         return [self frameRectForContentRect:newContentRect];
3158     }
3161     /*
3162      * ---------- NSPasteboardOwner methods ----------
3163      */
3164     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3165     {
3166         macdrv_query* query = macdrv_create_query();
3167         query->type = QUERY_PASTEBOARD_DATA;
3168         query->window = (macdrv_window)[self retain];
3169         query->pasteboard_data.type = (CFStringRef)[type copy];
3171         [self.queue query:query timeout:3];
3172         macdrv_release_query(query);
3173     }
3175     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3176     {
3177         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3178         [queue postEvent:event];
3179         macdrv_release_event(event);
3180     }
3183     /*
3184      * ---------- NSDraggingDestination methods ----------
3185      */
3186     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3187     {
3188         return [self draggingUpdated:sender];
3189     }
3191     - (void) draggingExited:(id <NSDraggingInfo>)sender
3192     {
3193         // This isn't really a query.  We don't need any response.  However, it
3194         // has to be processed in a similar manner as the other drag-and-drop
3195         // queries in order to maintain the proper order of operations.
3196         macdrv_query* query = macdrv_create_query();
3197         query->type = QUERY_DRAG_EXITED;
3198         query->window = (macdrv_window)[self retain];
3200         [self.queue query:query timeout:0.1];
3201         macdrv_release_query(query);
3202     }
3204     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3205     {
3206         NSDragOperation ret;
3207         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3208         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3209         NSPasteboard* pb = [sender draggingPasteboard];
3211         macdrv_query* query = macdrv_create_query();
3212         query->type = QUERY_DRAG_OPERATION;
3213         query->window = (macdrv_window)[self retain];
3214         query->drag_operation.x = floor(cgpt.x);
3215         query->drag_operation.y = floor(cgpt.y);
3216         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3217         query->drag_operation.accepted_op = NSDragOperationNone;
3218         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3220         [self.queue query:query timeout:3];
3221         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3222         macdrv_release_query(query);
3224         return ret;
3225     }
3227     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3228     {
3229         BOOL ret;
3230         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3231         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3232         NSPasteboard* pb = [sender draggingPasteboard];
3234         macdrv_query* query = macdrv_create_query();
3235         query->type = QUERY_DRAG_DROP;
3236         query->window = (macdrv_window)[self retain];
3237         query->drag_drop.x = floor(cgpt.x);
3238         query->drag_drop.y = floor(cgpt.y);
3239         query->drag_drop.op = [sender draggingSourceOperationMask];
3240         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3242         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3243         ret = query->status;
3244         macdrv_release_query(query);
3246         return ret;
3247     }
3249     - (BOOL) wantsPeriodicDraggingUpdates
3250     {
3251         return NO;
3252     }
3254 @end
3257 /***********************************************************************
3258  *              macdrv_create_cocoa_window
3260  * Create a Cocoa window with the given content frame and features (e.g.
3261  * title bar, close box, etc.).
3262  */
3263 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3264         CGRect frame, void* hwnd, macdrv_event_queue queue)
3266     __block WineWindow* window;
3268     OnMainThread(^{
3269         window = [[WineWindow createWindowWithFeatures:wf
3270                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3271                                                   hwnd:hwnd
3272                                                  queue:(WineEventQueue*)queue] retain];
3273     });
3275     return (macdrv_window)window;
3278 /***********************************************************************
3279  *              macdrv_destroy_cocoa_window
3281  * Destroy a Cocoa window.
3282  */
3283 void macdrv_destroy_cocoa_window(macdrv_window w)
3285     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3286     WineWindow* window = (WineWindow*)w;
3288     OnMainThread(^{
3289         window.closing = TRUE;
3290         [window doOrderOut];
3291         [window close];
3292     });
3293     [window.queue discardEventsMatchingMask:-1 forWindow:window];
3294     [window release];
3296     [pool release];
3299 /***********************************************************************
3300  *              macdrv_get_window_hwnd
3302  * Get the hwnd that was set for the window at creation.
3303  */
3304 void* macdrv_get_window_hwnd(macdrv_window w)
3306     WineWindow* window = (WineWindow*)w;
3307     return window.hwnd;
3310 /***********************************************************************
3311  *              macdrv_set_cocoa_window_features
3313  * Update a Cocoa window's features.
3314  */
3315 void macdrv_set_cocoa_window_features(macdrv_window w,
3316         const struct macdrv_window_features* wf)
3318     WineWindow* window = (WineWindow*)w;
3320     OnMainThread(^{
3321         [window setWindowFeatures:wf];
3322     });
3325 /***********************************************************************
3326  *              macdrv_set_cocoa_window_state
3328  * Update a Cocoa window's state.
3329  */
3330 void macdrv_set_cocoa_window_state(macdrv_window w,
3331         const struct macdrv_window_state* state)
3333     WineWindow* window = (WineWindow*)w;
3335     OnMainThread(^{
3336         [window setMacDrvState:state];
3337     });
3340 /***********************************************************************
3341  *              macdrv_set_cocoa_window_title
3343  * Set a Cocoa window's title.
3344  */
3345 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3346         size_t length)
3348     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3349     WineWindow* window = (WineWindow*)w;
3350     NSString* titleString;
3352     if (title)
3353         titleString = [NSString stringWithCharacters:title length:length];
3354     else
3355         titleString = @"";
3356     OnMainThreadAsync(^{
3357         [window setTitle:titleString];
3358         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3359             [NSApp changeWindowsItem:window title:titleString filename:NO];
3360     });
3362     [pool release];
3365 /***********************************************************************
3366  *              macdrv_order_cocoa_window
3368  * Reorder a Cocoa window relative to other windows.  If prev is
3369  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3370  * it is ordered above that window.  Otherwise, it is ordered to the
3371  * front.
3372  */
3373 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3374         macdrv_window n, int activate)
3376     WineWindow* window = (WineWindow*)w;
3377     WineWindow* prev = (WineWindow*)p;
3378     WineWindow* next = (WineWindow*)n;
3380     OnMainThreadAsync(^{
3381         [window orderBelow:prev
3382                    orAbove:next
3383                   activate:activate];
3384     });
3385     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3386                                   forWindow:window];
3387     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3388                                 forWindow:next];
3391 /***********************************************************************
3392  *              macdrv_hide_cocoa_window
3394  * Hides a Cocoa window.
3395  */
3396 void macdrv_hide_cocoa_window(macdrv_window w)
3398     WineWindow* window = (WineWindow*)w;
3400     OnMainThread(^{
3401         [window doOrderOut];
3402     });
3405 /***********************************************************************
3406  *              macdrv_set_cocoa_window_frame
3408  * Move a Cocoa window.
3409  */
3410 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3412     WineWindow* window = (WineWindow*)w;
3414     OnMainThread(^{
3415         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3416     });
3419 /***********************************************************************
3420  *              macdrv_get_cocoa_window_frame
3422  * Gets the frame of a Cocoa window.
3423  */
3424 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3426     WineWindow* window = (WineWindow*)w;
3428     OnMainThread(^{
3429         NSRect frame;
3431         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3432         [[WineApplicationController sharedController] flipRect:&frame];
3433         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3434     });
3437 /***********************************************************************
3438  *              macdrv_set_cocoa_parent_window
3440  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3441  * the parent window.
3442  */
3443 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3445     WineWindow* window = (WineWindow*)w;
3447     OnMainThread(^{
3448         [window setMacDrvParentWindow:(WineWindow*)parent];
3449     });
3452 /***********************************************************************
3453  *              macdrv_set_window_surface
3454  */
3455 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3457     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3458     WineWindow* window = (WineWindow*)w;
3460     OnMainThread(^{
3461         window.surface = surface;
3462         window.surface_mutex = mutex;
3463     });
3465     [pool release];
3468 /***********************************************************************
3469  *              macdrv_window_needs_display
3471  * Mark a window as needing display in a specified rect (in non-client
3472  * area coordinates).
3473  */
3474 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3476     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3477     WineWindow* window = (WineWindow*)w;
3479     OnMainThreadAsync(^{
3480         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3481     });
3483     [pool release];
3486 /***********************************************************************
3487  *              macdrv_set_window_shape
3489  * Sets the shape of a Cocoa window from an array of rectangles.  If
3490  * rects is NULL, resets the window's shape to its frame.
3491  */
3492 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3494     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3495     WineWindow* window = (WineWindow*)w;
3497     OnMainThread(^{
3498         if (!rects || !count)
3499         {
3500             [window setShape:NULL];
3501             [window checkEmptyShaped];
3502         }
3503         else
3504         {
3505             CGMutablePathRef path;
3506             unsigned int i;
3508             path = CGPathCreateMutable();
3509             for (i = 0; i < count; i++)
3510                 CGPathAddRect(path, NULL, cgrect_mac_from_win(rects[i]));
3511             [window setShape:path];
3512             CGPathRelease(path);
3513         }
3514     });
3516     [pool release];
3519 /***********************************************************************
3520  *              macdrv_set_window_alpha
3521  */
3522 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3524     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3525     WineWindow* window = (WineWindow*)w;
3527     [window setAlphaValue:alpha];
3529     [pool release];
3532 /***********************************************************************
3533  *              macdrv_set_window_color_key
3534  */
3535 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3536                                  CGFloat keyBlue)
3538     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3539     WineWindow* window = (WineWindow*)w;
3541     OnMainThread(^{
3542         window.colorKeyed       = TRUE;
3543         window.colorKeyRed      = keyRed;
3544         window.colorKeyGreen    = keyGreen;
3545         window.colorKeyBlue     = keyBlue;
3546         [window checkTransparency];
3547     });
3549     [pool release];
3552 /***********************************************************************
3553  *              macdrv_clear_window_color_key
3554  */
3555 void macdrv_clear_window_color_key(macdrv_window w)
3557     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3558     WineWindow* window = (WineWindow*)w;
3560     OnMainThread(^{
3561         window.colorKeyed = FALSE;
3562         [window checkTransparency];
3563     });
3565     [pool release];
3568 /***********************************************************************
3569  *              macdrv_window_use_per_pixel_alpha
3570  */
3571 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3573     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3574     WineWindow* window = (WineWindow*)w;
3576     OnMainThread(^{
3577         window.usePerPixelAlpha = use_per_pixel_alpha;
3578         [window checkTransparency];
3579     });
3581     [pool release];
3584 /***********************************************************************
3585  *              macdrv_give_cocoa_window_focus
3587  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3588  * orders it front and, if its frame was not within the desktop bounds,
3589  * Cocoa will typically move it on-screen.
3590  */
3591 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3593     WineWindow* window = (WineWindow*)w;
3595     OnMainThread(^{
3596         [window makeFocused:activate];
3597     });
3600 /***********************************************************************
3601  *              macdrv_set_window_min_max_sizes
3603  * Sets the window's minimum and maximum content sizes.
3604  */
3605 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3607     WineWindow* window = (WineWindow*)w;
3609     OnMainThread(^{
3610         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3611     });
3614 /***********************************************************************
3615  *              macdrv_create_view
3617  * Creates and returns a view with the specified frame rect.  The
3618  * caller is responsible for calling macdrv_dispose_view() on the view
3619  * when it is done with it.
3620  */
3621 macdrv_view macdrv_create_view(CGRect rect)
3623     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3624     __block WineContentView* view;
3626     if (CGRectIsNull(rect)) rect = CGRectZero;
3628     OnMainThread(^{
3629         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3631         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3632         [view setAutoresizingMask:NSViewNotSizable];
3633         [view setHidden:YES];
3634         [view setWantsBestResolutionOpenGLSurface:retina_on];
3635         [nc addObserver:view
3636                selector:@selector(updateGLContexts)
3637                    name:NSViewGlobalFrameDidChangeNotification
3638                  object:view];
3639         [nc addObserver:view
3640                selector:@selector(updateGLContexts)
3641                    name:NSApplicationDidChangeScreenParametersNotification
3642                  object:NSApp];
3643     });
3645     [pool release];
3646     return (macdrv_view)view;
3649 /***********************************************************************
3650  *              macdrv_dispose_view
3652  * Destroys a view previously returned by macdrv_create_view.
3653  */
3654 void macdrv_dispose_view(macdrv_view v)
3656     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3657     WineContentView* view = (WineContentView*)v;
3659     OnMainThread(^{
3660         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3661         WineWindow* window = (WineWindow*)[view window];
3663         [nc removeObserver:view
3664                       name:NSViewGlobalFrameDidChangeNotification
3665                     object:view];
3666         [nc removeObserver:view
3667                       name:NSApplicationDidChangeScreenParametersNotification
3668                     object:NSApp];
3669         [view removeFromSuperview];
3670         [view release];
3671         [window updateForGLSubviews];
3672     });
3674     [pool release];
3677 /***********************************************************************
3678  *              macdrv_set_view_frame
3679  */
3680 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3682     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3683     WineContentView* view = (WineContentView*)v;
3685     if (CGRectIsNull(rect)) rect = CGRectZero;
3687     OnMainThreadAsync(^{
3688         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3689         NSRect oldFrame = [view frame];
3691         if (!NSEqualRects(oldFrame, newFrame))
3692         {
3693             [[view superview] setNeedsDisplayInRect:oldFrame];
3694             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3695                 [view setFrameSize:newFrame.size];
3696             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3697                 [view setFrameOrigin:newFrame.origin];
3698             else
3699                 [view setFrame:newFrame];
3700             [view setNeedsDisplay:YES];
3702             if (retina_enabled)
3703             {
3704                 int backing_size[2] = { 0 };
3705                 [view wine_setBackingSize:backing_size];
3706             }
3707             [(WineWindow*)[view window] updateForGLSubviews];
3708         }
3709     });
3711     [pool release];
3714 /***********************************************************************
3715  *              macdrv_set_view_superview
3717  * Move a view to a new superview and position it relative to its
3718  * siblings.  If p is non-NULL, the view is ordered behind it.
3719  * Otherwise, the view is ordered above n.  If s is NULL, use the
3720  * content view of w as the new superview.
3721  */
3722 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3724     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3725     WineContentView* view = (WineContentView*)v;
3726     WineContentView* superview = (WineContentView*)s;
3727     WineWindow* window = (WineWindow*)w;
3728     WineContentView* prev = (WineContentView*)p;
3729     WineContentView* next = (WineContentView*)n;
3731     if (!superview)
3732         superview = [window contentView];
3734     OnMainThreadAsync(^{
3735         if (superview == [view superview])
3736         {
3737             NSArray* subviews = [superview subviews];
3738             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3739             if (!prev && !next && index == [subviews count] - 1)
3740                 return;
3741             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3742                 return;
3743             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3744                 return;
3745         }
3747         WineWindow* oldWindow = (WineWindow*)[view window];
3748         WineWindow* newWindow = (WineWindow*)[superview window];
3750 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3751         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3752             [view removeFromSuperview];
3753 #endif
3754         if (prev)
3755             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3756         else
3757             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3759         if (oldWindow != newWindow)
3760         {
3761             [oldWindow updateForGLSubviews];
3762             [newWindow updateForGLSubviews];
3763         }
3764     });
3766     [pool release];
3769 /***********************************************************************
3770  *              macdrv_set_view_hidden
3771  */
3772 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3774     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3775     WineContentView* view = (WineContentView*)v;
3777     OnMainThreadAsync(^{
3778         [view setHidden:hidden];
3779         [(WineWindow*)view.window updateForGLSubviews];
3780     });
3782     [pool release];
3785 /***********************************************************************
3786  *              macdrv_add_view_opengl_context
3788  * Add an OpenGL context to the list being tracked for each view.
3789  */
3790 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3792     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3793     WineContentView* view = (WineContentView*)v;
3794     WineOpenGLContext *context = (WineOpenGLContext*)c;
3796     OnMainThread(^{
3797         [view addGLContext:context];
3798     });
3800     [pool release];
3803 /***********************************************************************
3804  *              macdrv_remove_view_opengl_context
3806  * Add an OpenGL context to the list being tracked for each view.
3807  */
3808 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3810     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3811     WineContentView* view = (WineContentView*)v;
3812     WineOpenGLContext *context = (WineOpenGLContext*)c;
3814     OnMainThreadAsync(^{
3815         [view removeGLContext:context];
3816     });
3818     [pool release];
3821 macdrv_metal_device macdrv_create_metal_device(void)
3823     macdrv_metal_device ret;
3825 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3826     if (MTLCreateSystemDefaultDevice == NULL)
3827         return NULL;
3828 #endif
3830     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3831     ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3832     [pool release];
3833     return ret;
3836 void macdrv_release_metal_device(macdrv_metal_device d)
3838     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3839     [(id<MTLDevice>)d release];
3840     [pool release];
3843 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3845     id<MTLDevice> device = (id<MTLDevice>)d;
3846     WineContentView* view = (WineContentView*)v;
3847     __block WineMetalView *metalView;
3849     OnMainThread(^{
3850         metalView = [view newMetalViewWithDevice:device];
3851     });
3853     return (macdrv_metal_view)metalView;
3856 macdrv_metal_layer macdrv_view_get_metal_layer(macdrv_metal_view v)
3858     WineMetalView* view = (WineMetalView*)v;
3859     __block CAMetalLayer* layer;
3861     OnMainThread(^{
3862         layer = (CAMetalLayer*)view.layer;
3863     });
3865     return (macdrv_metal_layer)layer;
3868 void macdrv_view_release_metal_view(macdrv_metal_view v)
3870     WineMetalView* view = (WineMetalView*)v;
3871     OnMainThread(^{
3872         [view removeFromSuperview];
3873         [view release];
3874     });
3877 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3879     WineContentView* view = (WineContentView*)v;
3881     if (![view isKindOfClass:[WineContentView class]])
3882         return FALSE;
3884     [view wine_getBackingSize:backing_size];
3885     return TRUE;
3888 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3890     WineContentView* view = (WineContentView*)v;
3892     if ([view isKindOfClass:[WineContentView class]])
3893         [view wine_setBackingSize:backing_size];
3896 /***********************************************************************
3897  *              macdrv_window_background_color
3899  * Returns the standard Mac window background color as a 32-bit value of
3900  * the form 0x00rrggbb.
3901  */
3902 uint32_t macdrv_window_background_color(void)
3904     static uint32_t result;
3905     static dispatch_once_t once;
3907     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3908     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3909     // of it is to draw with it.
3910     dispatch_once(&once, ^{
3911         OnMainThread(^{
3912             unsigned char rgbx[4];
3913             unsigned char *planes = rgbx;
3914             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3915                                                                                pixelsWide:1
3916                                                                                pixelsHigh:1
3917                                                                             bitsPerSample:8
3918                                                                           samplesPerPixel:3
3919                                                                                  hasAlpha:NO
3920                                                                                  isPlanar:NO
3921                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3922                                                                              bitmapFormat:0
3923                                                                               bytesPerRow:4
3924                                                                              bitsPerPixel:32];
3925             [NSGraphicsContext saveGraphicsState];
3926             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3927             [[NSColor windowBackgroundColor] set];
3928             NSRectFill(NSMakeRect(0, 0, 1, 1));
3929             [NSGraphicsContext restoreGraphicsState];
3930             [bitmap release];
3931             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3932         });
3933     });
3935     return result;
3938 /***********************************************************************
3939  *              macdrv_send_text_input_event
3940  */
3941 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* himc, int* done)
3943     OnMainThreadAsync(^{
3944         BOOL ret;
3945         macdrv_event* event;
3946         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3947         if (![window isKindOfClass:[WineWindow class]])
3948         {
3949             window = (WineWindow*)[NSApp mainWindow];
3950             if (![window isKindOfClass:[WineWindow class]])
3951                 window = [[WineApplicationController sharedController] frontWineWindow];
3952         }
3954         if (window)
3955         {
3956             NSUInteger localFlags = flags;
3957             CGEventRef c;
3958             NSEvent* event;
3960             window.himc = himc;
3961             fix_device_modifiers_by_generic(&localFlags);
3963             // An NSEvent created with +keyEventWithType:... is internally marked
3964             // as synthetic and doesn't get sent through input methods.  But one
3965             // created from a CGEvent doesn't have that problem.
3966             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3967             CGEventSetFlags(c, localFlags);
3968             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3969             event = [NSEvent eventWithCGEvent:c];
3970             CFRelease(c);
3972             window.commandDone = FALSE;
3973             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3974         }
3975         else
3976             ret = FALSE;
3978         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3979         event->sent_text_input.handled = ret;
3980         event->sent_text_input.done = done;
3981         [[window queue] postEvent:event];
3982         macdrv_release_event(event);
3983     });
3986 void macdrv_clear_ime_text(void)
3988     OnMainThreadAsync(^{
3989         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3990         if (![window isKindOfClass:[WineWindow class]])
3991         {
3992             window = (WineWindow*)[NSApp mainWindow];
3993             if (![window isKindOfClass:[WineWindow class]])
3994                 window = [[WineApplicationController sharedController] frontWineWindow];
3995         }
3996         if (window)
3997             [[window contentView] clearMarkedText];
3998     });