d2d1: Create feature level 10.0 device context state objects.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blobd0672b7fb06eb56393aa3c84a13b2ff1e146e59b
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 #ifdef HAVE_METAL_METAL_H
27 #import <Metal/Metal.h>
28 #import <QuartzCore/QuartzCore.h>
29 #endif
31 #import "cocoa_window.h"
33 #include "macdrv_cocoa.h"
34 #import "cocoa_app.h"
35 #import "cocoa_event.h"
36 #import "cocoa_opengl.h"
39 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
40 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
41 enum {
42     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
44 #endif
47 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
49     NSUInteger style_mask;
51     if (wf->title_bar)
52     {
53         style_mask = NSWindowStyleMaskTitled;
54         if (wf->close_button) style_mask |= NSWindowStyleMaskClosable;
55         if (wf->minimize_button) style_mask |= NSWindowStyleMaskMiniaturizable;
56         if (wf->resizable || wf->maximize_button) style_mask |= NSWindowStyleMaskResizable;
57         if (wf->utility) style_mask |= NSWindowStyleMaskUtilityWindow;
58     }
59     else style_mask = NSWindowStyleMaskBorderless;
61     return style_mask;
65 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
67     NSScreen* screen;
68     for (screen in screens)
69     {
70         if (NSIntersectsRect(frame, [screen frame]))
71             return TRUE;
72     }
73     return FALSE;
77 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
79     for (NSScreen* screen in screens)
80     {
81         if (NSContainsRect(rect, [screen frame]))
82             return screen;
83     }
84     return nil;
88 /* We rely on the supposedly device-dependent modifier flags to distinguish the
89    keys on the left side of the keyboard from those on the right.  Some event
90    sources don't set those device-depdendent flags.  If we see a device-independent
91    flag for a modifier without either corresponding device-dependent flag, assume
92    the left one. */
93 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
95     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
96         *modifiers |= NX_DEVICELCMDKEYMASK;
97     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
98         *modifiers |= NX_DEVICELSHIFTKEYMASK;
99     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
100         *modifiers |= NX_DEVICELCTLKEYMASK;
101     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
102         *modifiers |= NX_DEVICELALTKEYMASK;
105 /* As we manipulate individual bits of a modifier mask, we can end up with
106    inconsistent sets of flags.  In particular, we might set or clear one of the
107    left/right-specific bits, but not the corresponding non-side-specific bit.
108    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
109    otherwise clear it. */
110 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
112     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
113         *modifiers |= NX_COMMANDMASK;
114     else
115         *modifiers &= ~NX_COMMANDMASK;
116     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
117         *modifiers |= NX_SHIFTMASK;
118     else
119         *modifiers &= ~NX_SHIFTMASK;
120     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
121         *modifiers |= NX_CONTROLMASK;
122     else
123         *modifiers &= ~NX_CONTROLMASK;
124     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
125         *modifiers |= NX_ALTERNATEMASK;
126     else
127         *modifiers &= ~NX_ALTERNATEMASK;
130 static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
132     fix_device_modifiers_by_generic(&modifiers);
133     NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
134                                              NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
136     // The MACDRV keyboard driver translates Command keys to Alt. If the
137     // Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
138     // Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
139     if (modifiers & NX_DEVICELALTKEYMASK)
140         new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
141     if (modifiers & NX_DEVICERALTKEYMASK)
142         new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
144     if (modifiers & NX_DEVICELCMDKEYMASK)
145         new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
146     if (modifiers & NX_DEVICERCMDKEYMASK)
147         new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
149     fix_generic_modifiers_by_device(&new_modifiers);
150     return new_modifiers;
154 @interface NSWindow (WineAccessPrivateMethods)
155     - (id) _displayChanged;
156 @end
159 @interface WineDisplayLink : NSObject
161     CGDirectDisplayID _displayID;
162     CVDisplayLinkRef _link;
163     NSMutableSet* _windows;
165     NSTimeInterval _actualRefreshPeriod;
166     NSTimeInterval _nominalRefreshPeriod;
168     NSTimeInterval _lastDisplayTime;
171     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
173     - (void) addWindow:(WineWindow*)window;
174     - (void) removeWindow:(WineWindow*)window;
176     - (NSTimeInterval) refreshPeriod;
178     - (void) start;
180 @end
182 @implementation WineDisplayLink
184 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
186     - (id) initWithDisplayID:(CGDirectDisplayID)displayID
187     {
188         self = [super init];
189         if (self)
190         {
191             CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
192             if (status == kCVReturnSuccess && !_link)
193                 status = kCVReturnError;
194             if (status == kCVReturnSuccess)
195                 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
196             if (status != kCVReturnSuccess)
197             {
198                 [self release];
199                 return nil;
200             }
202             _displayID = displayID;
203             _windows = [[NSMutableSet alloc] init];
204         }
205         return self;
206     }
208     - (void) dealloc
209     {
210         if (_link)
211         {
212             CVDisplayLinkStop(_link);
213             CVDisplayLinkRelease(_link);
214         }
215         [_windows release];
216         [super dealloc];
217     }
219     - (void) addWindow:(WineWindow*)window
220     {
221         BOOL firstWindow;
222         @synchronized(self) {
223             firstWindow = !_windows.count;
224             [_windows addObject:window];
225         }
226         if (firstWindow || !CVDisplayLinkIsRunning(_link))
227             [self start];
228     }
230     - (void) removeWindow:(WineWindow*)window
231     {
232         BOOL lastWindow = FALSE;
233         @synchronized(self) {
234             BOOL hadWindows = _windows.count > 0;
235             [_windows removeObject:window];
236             if (hadWindows && !_windows.count)
237                 lastWindow = TRUE;
238         }
239         if (lastWindow && CVDisplayLinkIsRunning(_link))
240             CVDisplayLinkStop(_link);
241     }
243     - (void) fire
244     {
245         NSSet* windows;
246         @synchronized(self) {
247             windows = [_windows copy];
248         }
249         dispatch_async(dispatch_get_main_queue(), ^{
250             BOOL anyDisplayed = FALSE;
251             for (WineWindow* window in windows)
252             {
253                 if ([window viewsNeedDisplay])
254                 {
255                     [window displayIfNeeded];
256                     anyDisplayed = YES;
257                 }
258             }
260             NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
261             if (anyDisplayed)
262                 _lastDisplayTime = now;
263             else if (_lastDisplayTime + 2.0 < now)
264                 CVDisplayLinkStop(_link);
265         });
266         [windows release];
267     }
269     - (NSTimeInterval) refreshPeriod
270     {
271         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
272             return _actualRefreshPeriod;
274         if (_nominalRefreshPeriod)
275             return _nominalRefreshPeriod;
277         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
278         if (time.flags & kCVTimeIsIndefinite)
279             return 1.0 / 60.0;
280         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
281         return _nominalRefreshPeriod;
282     }
284     - (void) start
285     {
286         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
287         CVDisplayLinkStart(_link);
288     }
290 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
292     WineDisplayLink* link = displayLinkContext;
293     [link fire];
294     return kCVReturnSuccess;
297 @end
300 #ifndef MAC_OS_X_VERSION_10_14
301 @protocol NSViewLayerContentScaleDelegate <NSObject>
302 @optional
304     - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window;
306 @end
307 #endif
310 @interface CAShapeLayer (WineShapeMaskExtensions)
312 @property(readonly, nonatomic, getter=isEmptyShaped) BOOL emptyShaped;
314 @end
316 @implementation CAShapeLayer (WineShapeMaskExtensions)
318     - (BOOL) isEmptyShaped
319     {
320         return CGRectEqualToRect(CGPathGetBoundingBox(self.path), CGRectZero);
321     }
323 @end
326 @interface WineBaseView : NSView
327 @end
330 #ifdef HAVE_METAL_METAL_H
331 @interface WineMetalView : WineBaseView
333     id<MTLDevice> _device;
336     - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
338 @end
339 #endif
342 @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContentScaleDelegate>
344     NSMutableArray* glContexts;
345     NSMutableArray* pendingGlContexts;
346     BOOL _everHadGLContext;
347     BOOL _cachedHasGLDescendant;
348     BOOL _cachedHasGLDescendantValid;
349     BOOL clearedGlSurface;
351     NSMutableAttributedString* markedText;
352     NSRange markedTextSelection;
354     BOOL _retinaMode;
355     int backingSize[2];
357 #ifdef HAVE_METAL_METAL_H
358     WineMetalView *_metalView;
359 #endif
362 @property (readonly, nonatomic) BOOL everHadGLContext;
364     - (void) addGLContext:(WineOpenGLContext*)context;
365     - (void) removeGLContext:(WineOpenGLContext*)context;
366     - (void) updateGLContexts;
368     - (void) wine_getBackingSize:(int*)outBackingSize;
369     - (void) wine_setBackingSize:(const int*)newBackingSize;
371 #ifdef HAVE_METAL_METAL_H
372     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
373 #endif
375 @end
378 @interface WineWindow ()
380 @property (readwrite, nonatomic) BOOL disabled;
381 @property (readwrite, nonatomic) BOOL noActivate;
382 @property (readwrite, nonatomic) BOOL floating;
383 @property (readwrite, nonatomic) BOOL drawnSinceShown;
384 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
385 @property (retain, nonatomic) NSWindow* latentParentWindow;
387 @property (nonatomic) void* hwnd;
388 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
390 @property (nonatomic) void* surface;
391 @property (nonatomic) pthread_mutex_t* surface_mutex;
393 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
394 @property (readonly, nonatomic) BOOL needsTransparency;
396 @property (nonatomic) BOOL colorKeyed;
397 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
398 @property (nonatomic) BOOL usePerPixelAlpha;
400 @property (assign, nonatomic) void* imeData;
401 @property (nonatomic) BOOL commandDone;
403 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
405     - (void) setShape:(CGPathRef)newShape;
407     - (void) updateForGLSubviews;
409     - (BOOL) becameEligibleParentOrChild;
410     - (void) becameIneligibleChild;
412     - (void) windowDidDrawContent;
414 @end
417 @implementation WineBaseView
419     - (void) setRetinaMode:(int)mode
420     {
421         for (WineBaseView* subview in [self subviews])
422         {
423             if ([subview isKindOfClass:[WineBaseView class]])
424                 [subview setRetinaMode:mode];
425         }
426     }
428     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
429     {
430         return YES;
431     }
433     - (BOOL) preservesContentDuringLiveResize
434     {
435         // Returning YES from this tells Cocoa to keep our view's content during
436         // a Cocoa-driven resize.  In theory, we're also supposed to override
437         // -setFrameSize: to mark exposed sections as needing redisplay, but
438         // user32 will take care of that in a roundabout way.  This way, we don't
439         // redraw until the window surface is flushed.
440         //
441         // This doesn't do anything when we resize the window ourselves.
442         return YES;
443     }
445     - (BOOL)acceptsFirstResponder
446     {
447         return [[self window] contentView] == self;
448     }
450     - (BOOL) mouseDownCanMoveWindow
451     {
452         return NO;
453     }
455     - (NSFocusRingType) focusRingType
456     {
457         return NSFocusRingTypeNone;
458     }
460 @end
463 @implementation WineContentView
465 @synthesize everHadGLContext = _everHadGLContext;
467     - (void) dealloc
468     {
469         [markedText release];
470         [glContexts release];
471         [pendingGlContexts release];
472         [super dealloc];
473     }
475     - (BOOL) isFlipped
476     {
477         return YES;
478     }
480     - (BOOL) wantsUpdateLayer
481     {
482         return YES /*!_everHadGLContext*/;
483     }
485     - (void) updateLayer
486     {
487         WineWindow* window = (WineWindow*)[self window];
488         CGImageRef image = NULL;
489         CGRect imageRect;
490         CALayer* layer = [self layer];
492         if ([window contentView] != self)
493             return;
495         if (!window.surface || !window.surface_mutex)
496             return;
498         pthread_mutex_lock(window.surface_mutex);
499         if (get_surface_blit_rects(window.surface, NULL, NULL))
500         {
501             imageRect = layer.bounds;
502             imageRect.origin.x *= layer.contentsScale;
503             imageRect.origin.y *= layer.contentsScale;
504             imageRect.size.width *= layer.contentsScale;
505             imageRect.size.height *= layer.contentsScale;
506             image = create_surface_image(window.surface, &imageRect, FALSE, window.colorKeyed,
507                                          window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue);
508         }
509         pthread_mutex_unlock(window.surface_mutex);
511         if (image)
512         {
513             layer.contents = (id)image;
514             CFRelease(image);
515             [window windowDidDrawContent];
517             // If the window may be transparent, then we have to invalidate the
518             // shadow every time we draw.  Also, if this is the first time we've
519             // drawn since changing from transparent to opaque.
520             if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
521             {
522                 window.shapeChangedSinceLastDraw = FALSE;
523                 [window invalidateShadow];
524             }
525         }
526     }
528     - (void) viewWillDraw
529     {
530         [super viewWillDraw];
532         for (WineOpenGLContext* context in pendingGlContexts)
533         {
534             if (!clearedGlSurface)
535             {
536                 context.shouldClearToBlack = TRUE;
537                 clearedGlSurface = TRUE;
538             }
539             context.needsUpdate = TRUE;
540         }
541         [glContexts addObjectsFromArray:pendingGlContexts];
542         [pendingGlContexts removeAllObjects];
543     }
545     - (void) addGLContext:(WineOpenGLContext*)context
546     {
547         BOOL hadContext = _everHadGLContext;
548         if (!glContexts)
549             glContexts = [[NSMutableArray alloc] init];
550         if (!pendingGlContexts)
551             pendingGlContexts = [[NSMutableArray alloc] init];
553         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
554         {
555             [glContexts addObject:context];
556             if (!clearedGlSurface)
557             {
558                 context.shouldClearToBlack = TRUE;
559                 clearedGlSurface = TRUE;
560             }
561             context.needsUpdate = TRUE;
562         }
563         else
564         {
565             [pendingGlContexts addObject:context];
566             [self setNeedsDisplay:YES];
567         }
569         _everHadGLContext = YES;
570         if (!hadContext)
571             [self invalidateHasGLDescendant];
572         [(WineWindow*)[self window] updateForGLSubviews];
573     }
575     - (void) removeGLContext:(WineOpenGLContext*)context
576     {
577         [glContexts removeObjectIdenticalTo:context];
578         [pendingGlContexts removeObjectIdenticalTo:context];
579         [(WineWindow*)[self window] updateForGLSubviews];
580     }
582     - (void) updateGLContexts:(BOOL)reattach
583     {
584         for (WineOpenGLContext* context in glContexts)
585         {
586             context.needsUpdate = TRUE;
587             if (reattach)
588                 context.needsReattach = TRUE;
589         }
590     }
592     - (void) updateGLContexts
593     {
594         [self updateGLContexts:NO];
595     }
597     - (BOOL) _hasGLDescendant
598     {
599         if ([self isHidden])
600             return NO;
601         if (_everHadGLContext)
602             return YES;
603         for (WineContentView* view in [self subviews])
604         {
605             if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
606                 return YES;
607         }
608         return NO;
609     }
611     - (BOOL) hasGLDescendant
612     {
613         if (!_cachedHasGLDescendantValid)
614         {
615             _cachedHasGLDescendant = [self _hasGLDescendant];
616             _cachedHasGLDescendantValid = YES;
617         }
618         return _cachedHasGLDescendant;
619     }
621     - (void) invalidateHasGLDescendant
622     {
623         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
624         _cachedHasGLDescendantValid = NO;
625         if (invalidateAncestors && self != [[self window] contentView])
626         {
627             WineContentView* superview = (WineContentView*)[self superview];
628             if ([superview isKindOfClass:[WineContentView class]])
629                 [superview invalidateHasGLDescendant];
630         }
631     }
633     - (void) wine_getBackingSize:(int*)outBackingSize
634     {
635         @synchronized(self) {
636             memcpy(outBackingSize, backingSize, sizeof(backingSize));
637         }
638     }
639     - (void) wine_setBackingSize:(const int*)newBackingSize
640     {
641         @synchronized(self) {
642             memcpy(backingSize, newBackingSize, sizeof(backingSize));
643         }
644     }
646 #ifdef HAVE_METAL_METAL_H
647     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
648     {
649         if (_metalView) return _metalView;
651         WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
652         [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
653         [self setAutoresizesSubviews:YES];
654         [self addSubview:view positioned:NSWindowBelow relativeTo:nil];
655         _metalView = view;
657         [(WineWindow*)self.window windowDidDrawContent];
659         return _metalView;
660     }
661 #endif
663     - (void) setRetinaMode:(int)mode
664     {
665         double scale = mode ? 0.5 : 2.0;
666         NSRect frame = self.frame;
667         frame.origin.x *= scale;
668         frame.origin.y *= scale;
669         frame.size.width *= scale;
670         frame.size.height *= scale;
671         [self setFrame:frame];
672         [self setWantsBestResolutionOpenGLSurface:mode];
673         [self updateGLContexts];
675         _retinaMode = !!mode;
676         [self layer].contentsScale = mode ? 2.0 : 1.0;
677         [self layer].minificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
678         [self layer].magnificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
679         [super setRetinaMode:mode];
680     }
682     - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window
683     {
684         return (_retinaMode || newScale == 1.0);
685     }
687     - (void) viewDidHide
688     {
689         [super viewDidHide];
690         [self invalidateHasGLDescendant];
691     }
693     - (void) viewDidUnhide
694     {
695         [super viewDidUnhide];
696         [self updateGLContexts:YES];
697         [self invalidateHasGLDescendant];
698     }
700     - (void) clearMarkedText
701     {
702         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
703         markedTextSelection = NSMakeRange(0, 0);
704         [[self inputContext] discardMarkedText];
705     }
707     - (void) completeText:(NSString*)text
708     {
709         macdrv_event* event;
710         WineWindow* window = (WineWindow*)[self window];
712         event = macdrv_create_event(IM_SET_TEXT, window);
713         event->im_set_text.data = [window imeData];
714         event->im_set_text.text = (CFStringRef)[text copy];
715         event->im_set_text.complete = TRUE;
717         [[window queue] postEvent:event];
719         macdrv_release_event(event);
721         [self clearMarkedText];
722     }
724     - (void) didAddSubview:(NSView*)subview
725     {
726         if ([subview isKindOfClass:[WineContentView class]])
727         {
728             WineContentView* view = (WineContentView*)subview;
729             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
730                 [self invalidateHasGLDescendant];
731         }
732         [super didAddSubview:subview];
733     }
735     - (void) willRemoveSubview:(NSView*)subview
736     {
737         if ([subview isKindOfClass:[WineContentView class]])
738         {
739             WineContentView* view = (WineContentView*)subview;
740             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
741                 [self invalidateHasGLDescendant];
742         }
743 #ifdef HAVE_METAL_METAL_H
744         if (subview == _metalView)
745             _metalView = nil;
746 #endif
747         [super willRemoveSubview:subview];
748     }
750     - (void) setLayer:(CALayer*)newLayer
751     {
752         [super setLayer:newLayer];
753         [self updateGLContexts];
754     }
756     /*
757      * ---------- NSTextInputClient methods ----------
758      */
759     - (NSTextInputContext*) inputContext
760     {
761         if (!markedText)
762             markedText = [[NSMutableAttributedString alloc] init];
763         return [super inputContext];
764     }
766     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
767     {
768         if ([string isKindOfClass:[NSAttributedString class]])
769             string = [string string];
771         if ([string isKindOfClass:[NSString class]])
772             [self completeText:string];
773     }
775     - (void) doCommandBySelector:(SEL)aSelector
776     {
777         [(WineWindow*)[self window] setCommandDone:TRUE];
778     }
780     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
781     {
782         if ([string isKindOfClass:[NSAttributedString class]])
783             string = [string string];
785         if ([string isKindOfClass:[NSString class]])
786         {
787             macdrv_event* event;
788             WineWindow* window = (WineWindow*)[self window];
790             if (replacementRange.location == NSNotFound)
791                 replacementRange = NSMakeRange(0, [markedText length]);
793             [markedText replaceCharactersInRange:replacementRange withString:string];
794             markedTextSelection = selectedRange;
795             markedTextSelection.location += replacementRange.location;
797             event = macdrv_create_event(IM_SET_TEXT, window);
798             event->im_set_text.data = [window imeData];
799             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
800             event->im_set_text.complete = FALSE;
801             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
803             [[window queue] postEvent:event];
805             macdrv_release_event(event);
807             [[self inputContext] invalidateCharacterCoordinates];
808         }
809     }
811     - (void) unmarkText
812     {
813         [self completeText:nil];
814     }
816     - (NSRange) selectedRange
817     {
818         return markedTextSelection;
819     }
821     - (NSRange) markedRange
822     {
823         NSRange range = NSMakeRange(0, [markedText length]);
824         if (!range.length)
825             range.location = NSNotFound;
826         return range;
827     }
829     - (BOOL) hasMarkedText
830     {
831         return [markedText length] > 0;
832     }
834     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
835     {
836         if (aRange.location >= [markedText length])
837             return nil;
839         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
840         if (actualRange)
841             *actualRange = aRange;
842         return [markedText attributedSubstringFromRange:aRange];
843     }
845     - (NSArray*) validAttributesForMarkedText
846     {
847         return [NSArray array];
848     }
850     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
851     {
852         macdrv_query* query;
853         WineWindow* window = (WineWindow*)[self window];
854         NSRect ret;
856         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
858         query = macdrv_create_query();
859         query->type = QUERY_IME_CHAR_RECT;
860         query->window = (macdrv_window)[window retain];
861         query->ime_char_rect.data = [window imeData];
862         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
864         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
865         {
866             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
867             ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
868             [[WineApplicationController sharedController] flipRect:&ret];
869         }
870         else
871             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
873         macdrv_release_query(query);
875         if (actualRange)
876             *actualRange = aRange;
877         return ret;
878     }
880     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
881     {
882         return NSNotFound;
883     }
885     - (NSInteger) windowLevel
886     {
887         return [[self window] level];
888     }
890 @end
893 #ifdef HAVE_METAL_METAL_H
894 @implementation WineMetalView
896     - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
897     {
898         self = [super initWithFrame:frame];
899         if (self)
900         {
901             _device = [device retain];
902             self.wantsLayer = YES;
903             self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
904         }
905         return self;
906     }
908     - (void) dealloc
909     {
910         [_device release];
911         [super dealloc];
912     }
914     - (void) setRetinaMode:(int)mode
915     {
916         self.layer.contentsScale = mode ? 2.0 : 1.0;
917         [super setRetinaMode:mode];
918     }
920     - (CALayer*) makeBackingLayer
921     {
922         CAMetalLayer *layer = [CAMetalLayer layer];
923         layer.device = _device;
924         layer.framebufferOnly = YES;
925         layer.magnificationFilter = kCAFilterNearest;
926         layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
927         layer.contentsScale = retina_on ? 2.0 : 1.0;
928         return layer;
929     }
931     - (BOOL) isOpaque
932     {
933         return YES;
934     }
936 @end
937 #endif
940 @implementation WineWindow
942     static WineWindow* causing_becomeKeyWindow;
944     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
945     @synthesize drawnSinceShown;
946     @synthesize surface, surface_mutex;
947     @synthesize shapeChangedSinceLastDraw;
948     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
949     @synthesize usePerPixelAlpha;
950     @synthesize imeData, commandDone;
952     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
953                                  windowFrame:(NSRect)window_frame
954                                         hwnd:(void*)hwnd
955                                        queue:(WineEventQueue*)queue
956     {
957         WineWindow* window;
958         WineContentView* contentView;
959         NSTrackingArea* trackingArea;
960         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
962         [[WineApplicationController sharedController] flipRect:&window_frame];
964         window = [[[self alloc] initWithContentRect:window_frame
965                                           styleMask:style_mask_for_features(wf)
966                                             backing:NSBackingStoreBuffered
967                                               defer:YES] autorelease];
969         if (!window) return nil;
971         /* Standardize windows to eliminate differences between titled and
972            borderless windows and between NSWindow and NSPanel. */
973         [window setHidesOnDeactivate:NO];
974         [window setReleasedWhenClosed:NO];
976         [window setOneShot:YES];
977         [window disableCursorRects];
978         [window setShowsResizeIndicator:NO];
979         [window setHasShadow:wf->shadow];
980         [window setAcceptsMouseMovedEvents:YES];
981         [window setDelegate:window];
982         [window setBackgroundColor:[NSColor clearColor]];
983         [window setOpaque:NO];
984         window.hwnd = hwnd;
985         window.queue = queue;
986         window->savedContentMinSize = NSZeroSize;
987         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
988         window->resizable = wf->resizable;
989         window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
991         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
992                                                                   (NSString*)kUTTypeContent,
993                                                                   nil]];
995         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
996         if (!contentView)
997             return nil;
998         [contentView setWantsLayer:YES];
999         [contentView layer].minificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
1000         [contentView layer].magnificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
1001         [contentView layer].contentsScale = retina_on ? 2.0 : 1.0;
1002         [contentView setAutoresizesSubviews:NO];
1004         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
1005            because they give us mouse moves in the background. */
1006         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
1007                                                      options:(NSTrackingMouseMoved |
1008                                                               NSTrackingActiveAlways |
1009                                                               NSTrackingInVisibleRect)
1010                                                        owner:window
1011                                                     userInfo:nil] autorelease];
1012         if (!trackingArea)
1013             return nil;
1014         [contentView addTrackingArea:trackingArea];
1016         [window setContentView:contentView];
1017         [window setInitialFirstResponder:contentView];
1019         [nc addObserver:window
1020                selector:@selector(updateFullscreen)
1021                    name:NSApplicationDidChangeScreenParametersNotification
1022                  object:NSApp];
1023         [window updateFullscreen];
1025         [nc addObserver:window
1026                selector:@selector(applicationWillHide)
1027                    name:NSApplicationWillHideNotification
1028                  object:NSApp];
1029         [nc addObserver:window
1030                selector:@selector(applicationDidUnhide)
1031                    name:NSApplicationDidUnhideNotification
1032                  object:NSApp];
1034         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1035                                                               selector:@selector(checkWineDisplayLink)
1036                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
1037                                                                 object:[NSWorkspace sharedWorkspace]];
1039         [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1041         return window;
1042     }
1044     - (void) dealloc
1045     {
1046         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1047         [[NSNotificationCenter defaultCenter] removeObserver:self];
1048         [queue release];
1049         [latentChildWindows release];
1050         [latentParentWindow release];
1051         [super dealloc];
1052     }
1054     - (BOOL) preventResizing
1055     {
1056         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1057         return ([self styleMask] & NSWindowStyleMaskResizable) && (disabled || !resizable || preventForClipping);
1058     }
1060     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1061     {
1062         if (allow_immovable_windows && (disabled || inMaximized))
1063             return NO;
1064         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1065             return NO;
1066         else
1067             return YES;
1068     }
1070     - (void) adjustFeaturesForState
1071     {
1072         NSUInteger style = [self styleMask];
1074         if (style & NSWindowStyleMaskClosable)
1075             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1076         if (style & NSWindowStyleMaskMiniaturizable)
1077             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1078         if (style & NSWindowStyleMaskResizable)
1079             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1080         if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1081             [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1083         if ([self preventResizing])
1084         {
1085             NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1086             [self setContentMinSize:size];
1087             [self setContentMaxSize:size];
1088         }
1089         else
1090         {
1091             [self setContentMaxSize:savedContentMaxSize];
1092             [self setContentMinSize:savedContentMinSize];
1093         }
1095         if (allow_immovable_windows || cursor_clipping_locks_windows)
1096             [self setMovable:[self allowsMovingWithMaximized:maximized]];
1097     }
1099     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1100     {
1101         NSUInteger style = [self styleMask];
1103         if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1104             style & NSWindowStyleMaskResizable && !(style & NSWindowStyleMaskUtilityWindow) && !maximized &&
1105             !(self.parentWindow || self.latentParentWindow))
1106         {
1107             behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1108             behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1109         }
1110         else
1111         {
1112             behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1113             behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1114             if (style & NSWindowStyleMaskFullScreen)
1115                 [super toggleFullScreen:nil];
1116         }
1118         if (behavior != [self collectionBehavior])
1119         {
1120             [self setCollectionBehavior:behavior];
1121             [self adjustFeaturesForState];
1122         }
1123     }
1125     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1126     {
1127         static const NSUInteger usedStyles = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable |
1128                                              NSWindowStyleMaskResizable | NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskBorderless;
1129         NSUInteger currentStyle = [self styleMask];
1130         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1132         if (newStyle != currentStyle)
1133         {
1134             NSString* title = [[[self title] copy] autorelease];
1135             BOOL showingButtons = (currentStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1136             BOOL shouldShowButtons = (newStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1137             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSWindowStyleMaskClosable))
1138             {
1139                 // -setStyleMask: is buggy on 10.7+ with respect to NSWindowStyleMaskResizable.
1140                 // If transitioning from NSWindowStyleMaskTitled | NSWindowStyleMaskResizable to
1141                 // just NSWindowStyleMaskTitled, the window buttons should disappear rather
1142                 // than just being disabled.  But they don't.  Similarly in reverse.
1143                 // The workaround is to also toggle NSWindowStyleMaskClosable at the same time.
1144                 [self setStyleMask:newStyle ^ NSWindowStyleMaskClosable];
1145             }
1146             [self setStyleMask:newStyle];
1148             // -setStyleMask: resets the firstResponder to the window.  Set it
1149             // back to the content view.
1150             if ([[self contentView] acceptsFirstResponder])
1151                 [self makeFirstResponder:[self contentView]];
1153             [self adjustFullScreenBehavior:[self collectionBehavior]];
1155             if ([[self title] length] == 0 && [title length] > 0)
1156                 [self setTitle:title];
1157         }
1159         resizable = wf->resizable;
1160         [self adjustFeaturesForState];
1161         [self setHasShadow:wf->shadow];
1162     }
1164     // Indicates if the window would be visible if the app were not hidden.
1165     - (BOOL) wouldBeVisible
1166     {
1167         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1168     }
1170     - (BOOL) isOrderedIn
1171     {
1172         return [self wouldBeVisible] || [self isMiniaturized];
1173     }
1175     - (NSInteger) minimumLevelForActive:(BOOL)active
1176     {
1177         NSInteger level;
1179         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1180                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1181             level = NSFloatingWindowLevel;
1182         else
1183             level = NSNormalWindowLevel;
1185         if (active)
1186         {
1187             BOOL captured;
1189             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1191             if (captured || fullscreen)
1192             {
1193                 if (captured)
1194                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1195                 else
1196                     level = NSStatusWindowLevel + 1;
1198                 if (self.floating)
1199                     level++;
1200             }
1201         }
1203         return level;
1204     }
1206     - (void) postDidUnminimizeEvent
1207     {
1208         macdrv_event* event;
1210         /* Coalesce events by discarding any previous ones still in the queue. */
1211         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1212                                forWindow:self];
1214         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1215         [queue postEvent:event];
1216         macdrv_release_event(event);
1217     }
1219     - (void) sendResizeStartQuery
1220     {
1221         macdrv_query* query = macdrv_create_query();
1222         query->type = QUERY_RESIZE_START;
1223         query->window = (macdrv_window)[self retain];
1225         [self.queue query:query timeout:0.3];
1226         macdrv_release_query(query);
1227     }
1229     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1230     {
1231         NSWindowCollectionBehavior behavior;
1233         self.disabled = state->disabled;
1234         self.noActivate = state->no_activate;
1236         if (self.floating != state->floating)
1237         {
1238             self.floating = state->floating;
1239             if (state->floating)
1240             {
1241                 // Became floating.  If child of non-floating window, make that
1242                 // relationship latent.
1243                 WineWindow* parent = (WineWindow*)[self parentWindow];
1244                 if (parent && !parent.floating)
1245                     [self becameIneligibleChild];
1246             }
1247             else
1248             {
1249                 // Became non-floating.  If parent of floating children, make that
1250                 // relationship latent.
1251                 WineWindow* child;
1252                 for (child in [self childWineWindows])
1253                 {
1254                     if (child.floating)
1255                         [child becameIneligibleChild];
1256                 }
1257             }
1259             // Check our latent relationships.  If floating status was the only
1260             // reason they were latent, then make them active.
1261             if ([self isVisible])
1262                 [self becameEligibleParentOrChild];
1264             [[WineApplicationController sharedController] adjustWindowLevels];
1265         }
1267         if (state->minimized_valid)
1268         {
1269             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1271             pendingMinimize = FALSE;
1272             if (state->minimized && ![self isMiniaturized])
1273             {
1274                 if ([self wouldBeVisible])
1275                 {
1276                     if ([self styleMask] & NSWindowStyleMaskFullScreen)
1277                     {
1278                         [self postDidUnminimizeEvent];
1279                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1280                     }
1281                     else
1282                     {
1283                         [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1284                         [super miniaturize:nil];
1285                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1286                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1287                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1288                     }
1289                 }
1290                 else
1291                     pendingMinimize = TRUE;
1292             }
1293             else if (!state->minimized && [self isMiniaturized])
1294             {
1295                 ignore_windowDeminiaturize = TRUE;
1296                 [self deminiaturize:nil];
1297                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1298             }
1300             if (discard)
1301                 [queue discardEventsMatchingMask:discard forWindow:self];
1302         }
1304         if (state->maximized != maximized)
1305         {
1306             maximized = state->maximized;
1307             [self adjustFeaturesForState];
1309             if (!maximized && [self inLiveResize])
1310                 [self sendResizeStartQuery];
1311         }
1313         behavior = NSWindowCollectionBehaviorDefault;
1314         if (state->excluded_by_expose)
1315             behavior |= NSWindowCollectionBehaviorTransient;
1316         else
1317             behavior |= NSWindowCollectionBehaviorManaged;
1318         if (state->excluded_by_cycle)
1319         {
1320             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1321             if ([self isOrderedIn])
1322                 [NSApp removeWindowsItem:self];
1323         }
1324         else
1325         {
1326             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1327             if ([self isOrderedIn])
1328                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1329         }
1330         [self adjustFullScreenBehavior:behavior];
1331     }
1333     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1334     {
1335         BOOL reordered = FALSE;
1337         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1338         {
1339             if ([self level] > [child level])
1340                 [child setLevel:[self level]];
1341             if (![child isVisible])
1342                 [child setAutodisplay:YES];
1343             [self addChildWindow:child ordered:NSWindowAbove];
1344             [child checkWineDisplayLink];
1345             [latentChildWindows removeObjectIdenticalTo:child];
1346             child.latentParentWindow = nil;
1347             reordered = TRUE;
1348         }
1349         else
1350         {
1351             if (!latentChildWindows)
1352                 latentChildWindows = [[NSMutableArray alloc] init];
1353             if (![latentChildWindows containsObject:child])
1354                 [latentChildWindows addObject:child];
1355             child.latentParentWindow = self;
1356         }
1358         return reordered;
1359     }
1361     - (BOOL) addChildWineWindow:(WineWindow*)child
1362     {
1363         return [self addChildWineWindow:child assumeVisible:FALSE];
1364     }
1366     - (void) removeChildWineWindow:(WineWindow*)child
1367     {
1368         [self removeChildWindow:child];
1369         if (child.latentParentWindow == self)
1370             child.latentParentWindow = nil;
1371         [latentChildWindows removeObjectIdenticalTo:child];
1372     }
1374     - (void) setChildWineWindows:(NSArray*)childWindows
1375     {
1376         NSArray* origChildren;
1377         NSUInteger count, start, limit, i;
1379         origChildren = self.childWineWindows;
1381         // If the current and desired children arrays match up to a point, leave
1382         // those matching children alone.
1383         count = childWindows.count;
1384         limit = MIN(origChildren.count, count);
1385         for (start = 0; start < limit; start++)
1386         {
1387             if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1388                 break;
1389         }
1391         // Remove all of the child windows and re-add them back-to-front so they
1392         // are in the desired order.
1393         for (i = start; i < count; i++)
1394         {
1395             WineWindow* child = [childWindows objectAtIndex:i];
1396             [self removeChildWindow:child];
1397         }
1398         for (i = start; i < count; i++)
1399         {
1400             WineWindow* child = [childWindows objectAtIndex:i];
1401             [self addChildWindow:child ordered:NSWindowAbove];
1402         }
1403     }
1405     static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1406     {
1407         NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1408         NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1409         NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1410         NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1411         if (index1 == NSNotFound)
1412         {
1413             if (index2 == NSNotFound)
1414                 return NSOrderedSame;
1415             else
1416                 return NSOrderedAscending;
1417         }
1418         else if (index2 == NSNotFound)
1419             return NSOrderedDescending;
1420         else if (index1 < index2)
1421             return NSOrderedDescending;
1422         else if (index2 < index1)
1423             return NSOrderedAscending;
1425         return NSOrderedSame;
1426     }
1428     - (BOOL) becameEligibleParentOrChild
1429     {
1430         BOOL reordered = FALSE;
1431         NSUInteger count;
1433         if (latentParentWindow.floating || !self.floating)
1434         {
1435             // If we aren't visible currently, we assume that we should be and soon
1436             // will be.  So, if the latent parent is visible that's enough to assume
1437             // we can establish the parent-child relationship in Cocoa.  That will
1438             // actually make us visible, which is fine.
1439             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1440                 reordered = TRUE;
1441         }
1443         // Here, though, we may not actually be visible yet and adding a child
1444         // won't make us visible.  The caller will have to call this method
1445         // again after actually making us visible.
1446         if ([self isVisible] && (count = [latentChildWindows count]))
1447         {
1448             NSMutableArray* windowNumbers;
1449             NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1450             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1451             NSUInteger i;
1453             windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1455             for (i = 0; i < count; i++)
1456             {
1457                 WineWindow* child = [latentChildWindows objectAtIndex:i];
1458                 if ([child isVisible] && (self.floating || !child.floating))
1459                 {
1460                     if (child.latentParentWindow == self)
1461                     {
1462                         if ([self level] > [child level])
1463                             [child setLevel:[self level]];
1464                         [childWindows addObject:child];
1465                         child.latentParentWindow = nil;
1466                         reordered = TRUE;
1467                     }
1468                     else
1469                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1470                     [indexesToRemove addIndex:i];
1471                 }
1472             }
1474             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1476             [childWindows sortWithOptions:NSSortStable
1477                           usingComparator:^NSComparisonResult(id obj1, id obj2){
1478                 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1479             }];
1480             [self setChildWineWindows:childWindows];
1481         }
1483         return reordered;
1484     }
1486     - (void) becameIneligibleChild
1487     {
1488         WineWindow* parent = (WineWindow*)[self parentWindow];
1489         if (parent)
1490         {
1491             if (!parent->latentChildWindows)
1492                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1493             [parent->latentChildWindows insertObject:self atIndex:0];
1494             self.latentParentWindow = parent;
1495             [parent removeChildWindow:self];
1496         }
1497     }
1499     - (void) becameIneligibleParentOrChild
1500     {
1501         NSArray* childWindows = [self childWineWindows];
1503         [self becameIneligibleChild];
1505         if ([childWindows count])
1506         {
1507             WineWindow* child;
1509             for (child in childWindows)
1510             {
1511                 child.latentParentWindow = self;
1512                 [self removeChildWindow:child];
1513             }
1515             if (latentChildWindows)
1516                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1517             else
1518                 latentChildWindows = [childWindows mutableCopy];
1519         }
1520     }
1522     // Determine if, among Wine windows, this window is directly above or below
1523     // a given other Wine window with no other Wine window intervening.
1524     // Intervening non-Wine windows are ignored.
1525     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1526     {
1527         NSNumber* windowNumber;
1528         NSNumber* otherWindowNumber;
1529         NSArray* windowNumbers;
1530         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1532         if (![self isVisible] || ![otherWindow isVisible])
1533             return FALSE;
1535         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1536         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1537         windowNumbers = [[self class] windowNumbersWithOptions:0];
1538         windowIndex = [windowNumbers indexOfObject:windowNumber];
1539         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1541         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1542             return FALSE;
1544         if (orderingMode == NSWindowAbove)
1545         {
1546             lowIndex = windowIndex;
1547             highIndex = otherWindowIndex;
1548         }
1549         else if (orderingMode == NSWindowBelow)
1550         {
1551             lowIndex = otherWindowIndex;
1552             highIndex = windowIndex;
1553         }
1554         else
1555             return FALSE;
1557         if (highIndex <= lowIndex)
1558             return FALSE;
1560         for (i = lowIndex + 1; i < highIndex; i++)
1561         {
1562             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1563             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1564             if ([interveningWindow isKindOfClass:[WineWindow class]])
1565                 return FALSE;
1566         }
1568         return TRUE;
1569     }
1571     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1572     {
1573         NSMutableArray* windowNumbers;
1574         NSNumber* childWindowNumber;
1575         NSUInteger otherIndex;
1576         NSArray* origChildren;
1577         NSMutableArray* children;
1579         // Get the z-order from the window server and modify it to reflect the
1580         // requested window ordering.
1581         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1582         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1583         [windowNumbers removeObject:childWindowNumber];
1584         if (other)
1585         {
1586             otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1587             [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1588         }
1589         else if (mode == NSWindowAbove)
1590             [windowNumbers insertObject:childWindowNumber atIndex:0];
1591         else
1592             [windowNumbers addObject:childWindowNumber];
1594         // Get our child windows and sort them in the reverse of the desired
1595         // z-order (back-to-front).
1596         origChildren = [self childWineWindows];
1597         children = [[origChildren mutableCopy] autorelease];
1598         [children sortWithOptions:NSSortStable
1599                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1600             return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1601         }];
1603         [self setChildWineWindows:children];
1604     }
1606     // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1607     // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1608     //      (non-nil, non-nil)  there is a level in the window tree where the two windows have sibling ancestors
1609     //                          if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1610     //                          otherwise, the two ancestors are each roots of disjoint window trees
1611     //      (nil, non-nil)      the other window is a descendent of self and *ancestorOfOther is the direct child
1612     //      (non-nil, nil)      self is a descendent of other and *ancestor is the direct child
1613     - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1614     {
1615         NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1616         WineWindow* child;
1617         WineWindow* parent;
1618         for (child = other;
1619              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1620              child = parent)
1621         {
1622             if (parent == self)
1623             {
1624                 *ancestor = nil;
1625                 *ancestorOfOther = child;
1626                 return;
1627             }
1629             [otherAncestors addObject:parent];
1630         }
1632         for (child = self;
1633              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1634              child = parent)
1635         {
1636             NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1637             if (index != NSNotFound)
1638             {
1639                 *ancestor = child;
1640                 if (index == 0)
1641                     *ancestorOfOther = nil;
1642                 else
1643                     *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1644                 return;
1645             }
1646         }
1648         *ancestor = child;
1649         *ancestorOfOther = otherAncestors.lastObject;;
1650     }
1652     /* Returns whether or not the window was ordered in, which depends on if
1653        its frame intersects any screen. */
1654     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1655     {
1656         WineApplicationController* controller = [WineApplicationController sharedController];
1657         if (![self isMiniaturized])
1658         {
1659             BOOL needAdjustWindowLevels = FALSE;
1660             BOOL wasVisible;
1661             WineWindow* parent;
1662             WineWindow* child;
1664             [controller transformProcessToForeground];
1665             if ([NSApp isHidden])
1666                 [NSApp unhide:nil];
1667             wasVisible = [self isVisible];
1669             if (activate)
1670                 [NSApp activateIgnoringOtherApps:YES];
1672             NSDisableScreenUpdates();
1674             if ([self becameEligibleParentOrChild])
1675                 needAdjustWindowLevels = TRUE;
1677             if (prev || next)
1678             {
1679                 WineWindow* other = [prev isVisible] ? prev : next;
1680                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1682                 if (![self isOrdered:orderingMode relativeTo:other])
1683                 {
1684                     WineWindow* ancestor;
1685                     WineWindow* ancestorOfOther;
1687                     [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1688                     if (ancestor)
1689                     {
1690                         [self setAutodisplay:YES];
1691                         if (ancestorOfOther)
1692                         {
1693                             // This window level may not be right for this window based
1694                             // on floating-ness, fullscreen-ness, etc.  But we set it
1695                             // temporarily to allow us to order the windows properly.
1696                             // Then the levels get fixed by -adjustWindowLevels.
1697                             if ([ancestor level] != [ancestorOfOther level])
1698                                 [ancestor setLevel:[ancestorOfOther level]];
1700                             parent = (WineWindow*)ancestor.parentWindow;
1701                             if ([parent isKindOfClass:[WineWindow class]])
1702                                 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1703                             else
1704                                 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1705                         }
1707                         if (!ancestorOfOther || ancestor != self)
1708                         {
1709                             for (child = self;
1710                                  (parent = (WineWindow*)child.parentWindow);
1711                                  child = parent)
1712                             {
1713                                 if ([parent isKindOfClass:[WineWindow class]])
1714                                     [parent order:-orderingMode childWindow:child relativeTo:nil];
1715                                 if (parent == ancestor)
1716                                     break;
1717                             }
1718                         }
1720                         [self checkWineDisplayLink];
1721                         needAdjustWindowLevels = TRUE;
1722                     }
1723                 }
1724             }
1725             else
1726             {
1727                 for (child = self;
1728                      (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1729                      child = parent)
1730                 {
1731                     [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1732                 }
1734                 // Again, temporarily set level to make sure we can order to
1735                 // the right place.
1736                 next = [controller frontWineWindow];
1737                 if (next && [self level] < [next level])
1738                     [self setLevel:[next level]];
1739                 [self setAutodisplay:YES];
1740                 [self orderFront:nil];
1741                 [self checkWineDisplayLink];
1742                 needAdjustWindowLevels = TRUE;
1743             }
1744             pendingOrderOut = FALSE;
1746             if ([self becameEligibleParentOrChild])
1747                 needAdjustWindowLevels = TRUE;
1749             if (needAdjustWindowLevels)
1750             {
1751                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1752                     [controller updateFullscreenWindows];
1753                 [controller adjustWindowLevels];
1754             }
1756             if (pendingMinimize)
1757             {
1758                 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1759                 [super miniaturize:nil];
1760                 pendingMinimize = FALSE;
1761             }
1763             NSEnableScreenUpdates();
1765             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1766                Generate a frame-changed event just in case.  The back end will ignore
1767                it if nothing actually changed. */
1768             [self windowDidResize:nil skipSizeMove:TRUE];
1770             if (![self isExcludedFromWindowsMenu])
1771                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1772         }
1773     }
1775     - (void) doOrderOut
1776     {
1777         WineApplicationController* controller = [WineApplicationController sharedController];
1778         BOOL wasVisible = [self isVisible];
1779         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1781         [self endWindowDragging];
1782         [controller windowWillOrderOut:self];
1784         if (enteringFullScreen || exitingFullScreen)
1785         {
1786             pendingOrderOut = TRUE;
1787             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1788                                              event_mask_for_type(WINDOW_GOT_FOCUS) |
1789                                              event_mask_for_type(WINDOW_LOST_FOCUS) |
1790                                              event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1791                                              event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1792                                              event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1793                                    forWindow:self];
1794             return;
1795         }
1797         pendingOrderOut = FALSE;
1799         if ([self isMiniaturized])
1800             pendingMinimize = TRUE;
1802         WineWindow* parent = (WineWindow*)self.parentWindow;
1803         if ([parent isKindOfClass:[WineWindow class]])
1804             [parent grabDockIconSnapshotFromWindow:self force:NO];
1806         [self becameIneligibleParentOrChild];
1807         if ([self isMiniaturized] || [self styleMask] & NSWindowStyleMaskFullScreen)
1808         {
1809             fakingClose = TRUE;
1810             [self close];
1811             fakingClose = FALSE;
1812         }
1813         else
1814             [self orderOut:nil];
1815         [self checkWineDisplayLink];
1816         [self setBackgroundColor:[NSColor clearColor]];
1817         [self setOpaque:NO];
1818         drawnSinceShown = NO;
1819         savedVisibleState = FALSE;
1820         if (wasVisible && wasOnActiveSpace && fullscreen)
1821             [controller updateFullscreenWindows];
1822         [controller adjustWindowLevels];
1823         [NSApp removeWindowsItem:self];
1825         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1826                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1827                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1828                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1829                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1830                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1831                                forWindow:self];
1832     }
1834     - (void) updateFullscreen
1835     {
1836         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1837         BOOL nowFullscreen = !([self styleMask] & NSWindowStyleMaskFullScreen) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1839         if (nowFullscreen != fullscreen)
1840         {
1841             WineApplicationController* controller = [WineApplicationController sharedController];
1843             fullscreen = nowFullscreen;
1844             if ([self isVisible] && [self isOnActiveSpace])
1845                 [controller updateFullscreenWindows];
1847             [controller adjustWindowLevels];
1848         }
1849     }
1851     - (void) setFrameAndWineFrame:(NSRect)frame
1852     {
1853         [self setFrame:frame display:YES];
1855         wineFrame = frame;
1856         roundedWineFrame = self.frame;
1857         CGFloat junk;
1858 #if CGFLOAT_IS_DOUBLE
1859         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1860              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1861             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1862             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1863             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1864             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1865             roundedWineFrame = wineFrame;
1866 #else
1867         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1868              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1869             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1870             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1871             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1872             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1873             roundedWineFrame = wineFrame;
1874 #endif
1875     }
1877     - (void) setFrameFromWine:(NSRect)contentRect
1878     {
1879         /* Origin is (left, top) in a top-down space.  Need to convert it to
1880            (left, bottom) in a bottom-up space. */
1881         [[WineApplicationController sharedController] flipRect:&contentRect];
1883         /* The back end is establishing a new window size and position.  It's
1884            not interested in any stale events regarding those that may be sitting
1885            in the queue. */
1886         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1887                                forWindow:self];
1889         if (!NSIsEmptyRect(contentRect))
1890         {
1891             NSRect frame, oldFrame;
1893             oldFrame = self.wine_fractionalFrame;
1894             frame = [self frameRectForContentRect:contentRect];
1895             if (!NSEqualRects(frame, oldFrame))
1896             {
1897                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1898                 BOOL needEnableScreenUpdates = FALSE;
1900                 if ([self preventResizing])
1901                 {
1902                     // Allow the following calls to -setFrame:display: to work even
1903                     // if they would violate the content size constraints. This
1904                     // shouldn't be necessary since the content size constraints are
1905                     // documented to not constrain that method, but it seems to be.
1906                     [self setContentMinSize:NSZeroSize];
1907                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1908                 }
1910                 if (equalSizes && [[self childWineWindows] count])
1911                 {
1912                     // If we change the window frame such that the origin moves
1913                     // but the size doesn't change, then Cocoa moves child
1914                     // windows with the parent.  We don't want that so we fake
1915                     // a change of the size and then change it back.
1916                     NSRect bogusFrame = frame;
1917                     bogusFrame.size.width++;
1919                     NSDisableScreenUpdates();
1920                     needEnableScreenUpdates = TRUE;
1922                     ignore_windowResize = TRUE;
1923                     [self setFrame:bogusFrame display:NO];
1924                     ignore_windowResize = FALSE;
1925                 }
1927                 [self setFrameAndWineFrame:frame];
1928                 if ([self preventResizing])
1929                 {
1930                     [self setContentMinSize:contentRect.size];
1931                     [self setContentMaxSize:contentRect.size];
1932                 }
1934                 if (needEnableScreenUpdates)
1935                     NSEnableScreenUpdates();
1937                 if (!enteringFullScreen &&
1938                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1939                     nonFullscreenFrame = frame;
1941                 [self updateFullscreen];
1943                 if ([self isOrderedIn])
1944                 {
1945                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1946                        event.  The back end will ignore it if nothing actually changed. */
1947                     [self windowDidResize:nil skipSizeMove:TRUE];
1948                 }
1949             }
1950         }
1951     }
1953     - (NSRect) wine_fractionalFrame
1954     {
1955         NSRect frame = self.frame;
1956         if (NSEqualRects(frame, roundedWineFrame))
1957             frame = wineFrame;
1958         return frame;
1959     }
1961     - (void) setMacDrvParentWindow:(WineWindow*)parent
1962     {
1963         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1964         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1965         {
1966             [oldParent removeChildWineWindow:self];
1967             [latentParentWindow removeChildWineWindow:self];
1968             if ([parent addChildWineWindow:self])
1969                 [[WineApplicationController sharedController] adjustWindowLevels];
1970             [self adjustFullScreenBehavior:[self collectionBehavior]];
1971         }
1972     }
1974     - (void) setDisabled:(BOOL)newValue
1975     {
1976         if (disabled != newValue)
1977         {
1978             disabled = newValue;
1979             [self adjustFeaturesForState];
1980         }
1981     }
1983     - (BOOL) needsTransparency
1984     {
1985         return self.contentView.layer.mask || self.colorKeyed || self.usePerPixelAlpha ||
1986                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1987     }
1989     - (void) checkTransparency
1990     {
1991         if (![self isOpaque] && !self.needsTransparency)
1992         {
1993             self.shapeChangedSinceLastDraw = TRUE;
1994             [[self contentView] setNeedsDisplay:YES];
1995             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1996             [self setOpaque:YES];
1997         }
1998         else if ([self isOpaque] && self.needsTransparency)
1999         {
2000             self.shapeChangedSinceLastDraw = TRUE;
2001             [[self contentView] setNeedsDisplay:YES];
2002             [self setBackgroundColor:[NSColor clearColor]];
2003             [self setOpaque:NO];
2004         }
2005     }
2007     - (void) setShape:(CGPathRef)newShape
2008     {
2009         CALayer* layer = [[self contentView] layer];
2010         CAShapeLayer* mask = (CAShapeLayer*)layer.mask;
2011         if (CGPathEqualToPath(newShape, mask.path)) return;
2013         if (newShape && !layer.mask)
2014             layer.mask = mask = [CAShapeLayer layer];
2015         else if (!newShape)
2016             layer.mask = mask = nil;
2018         if (mask.path)
2019             [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(mask.path))];
2020         if (newShape)
2021             [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(newShape))];
2023         mask.path = newShape;
2024         self.shapeChangedSinceLastDraw = TRUE;
2026         [self checkTransparency];
2027         [self checkEmptyShaped];
2028     }
2030     - (void) makeFocused:(BOOL)activate
2031     {
2032         if (activate)
2033         {
2034             [[WineApplicationController sharedController] transformProcessToForeground];
2035             [NSApp activateIgnoringOtherApps:YES];
2036         }
2038         causing_becomeKeyWindow = self;
2039         [self makeKeyWindow];
2040         causing_becomeKeyWindow = nil;
2042         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2043                                          event_mask_for_type(WINDOW_LOST_FOCUS)
2044                                forWindow:self];
2045     }
2047     - (void) postKey:(uint16_t)keyCode
2048              pressed:(BOOL)pressed
2049            modifiers:(NSUInteger)modifiers
2050                event:(NSEvent*)theEvent
2051     {
2052         macdrv_event* event;
2053         CGEventRef cgevent;
2054         WineApplicationController* controller = [WineApplicationController sharedController];
2056         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2057         event->key.keycode   = keyCode;
2058         event->key.modifiers = modifiers;
2059         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
2061         if ((cgevent = [theEvent CGEvent]))
2062             controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2064         [queue postEvent:event];
2066         macdrv_release_event(event);
2068         [controller noteKey:keyCode pressed:pressed];
2069     }
2071     - (void) postKeyEvent:(NSEvent *)theEvent
2072     {
2073         [self flagsChanged:theEvent];
2074         [self postKey:[theEvent keyCode]
2075               pressed:[theEvent type] == NSEventTypeKeyDown
2076             modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2077                 event:theEvent];
2078     }
2080     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2081     {
2082         savedContentMinSize = minSize;
2083         savedContentMaxSize = maxSize;
2084         if (![self preventResizing])
2085         {
2086             [self setContentMinSize:minSize];
2087             [self setContentMaxSize:maxSize];
2088         }
2089     }
2091     - (WineWindow*) ancestorWineWindow
2092     {
2093         WineWindow* ancestor = self;
2094         for (;;)
2095         {
2096             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2097             if ([parent isKindOfClass:[WineWindow class]])
2098                 ancestor = parent;
2099             else
2100                 break;
2101         }
2102         return ancestor;
2103     }
2105     - (void) postBroughtForwardEvent
2106     {
2107         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2108         [queue postEvent:event];
2109         macdrv_release_event(event);
2110     }
2112     - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing skipSizeMove:(BOOL)skipSizeMove
2113     {
2114         macdrv_event* event;
2115         NSUInteger style = self.styleMask;
2117         if (isFullscreen)
2118             style |= NSWindowStyleMaskFullScreen;
2119         else
2120             style &= ~NSWindowStyleMaskFullScreen;
2121         frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2122         [[WineApplicationController sharedController] flipRect:&frame];
2124         /* Coalesce events by discarding any previous ones still in the queue. */
2125         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2126                                forWindow:self];
2128         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2129         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2130         event->window_frame_changed.fullscreen = isFullscreen;
2131         event->window_frame_changed.in_resize = resizing;
2132         event->window_frame_changed.skip_size_move_loop = skipSizeMove;
2133         [queue postEvent:event];
2134         macdrv_release_event(event);
2135     }
2137     - (void) updateForCursorClipping
2138     {
2139         [self adjustFeaturesForState];
2140     }
2142     - (void) endWindowDragging
2143     {
2144         if (draggingPhase)
2145         {
2146             if (draggingPhase == 3)
2147             {
2148                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2149                 [queue postEvent:event];
2150                 macdrv_release_event(event);
2151             }
2153             draggingPhase = 0;
2154             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2155         }
2156     }
2158     - (NSMutableDictionary*) displayIDToDisplayLinkMap
2159     {
2160         static NSMutableDictionary* displayIDToDisplayLinkMap;
2161         if (!displayIDToDisplayLinkMap)
2162         {
2163             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2165             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2166                                                               object:NSApp
2167                                                                queue:nil
2168                                                           usingBlock:^(NSNotification *note){
2169                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2170                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2171                 [badDisplayIDs minusSet:validDisplayIDs];
2172                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2173             }];
2174         }
2175         return displayIDToDisplayLinkMap;
2176     }
2178     - (WineDisplayLink*) wineDisplayLink
2179     {
2180         if (!_lastDisplayID)
2181             return nil;
2183         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2184         return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2185     }
2187     - (void) checkWineDisplayLink
2188     {
2189         NSScreen* screen = self.screen;
2190         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2191             screen = nil;
2192 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2193         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2194             screen = nil;
2195 #endif
2197         NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2198         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2199         if (displayID == _lastDisplayID)
2200             return;
2202         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2204         if (_lastDisplayID)
2205         {
2206             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2207             [link removeWindow:self];
2208         }
2209         if (displayID)
2210         {
2211             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2212             if (!link)
2213             {
2214                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2215                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2216             }
2217             [link addWindow:self];
2218             [self displayIfNeeded];
2219         }
2220         _lastDisplayID = displayID;
2221     }
2223     - (BOOL) isEmptyShaped
2224     {
2225         CAShapeLayer* mask = (CAShapeLayer*)[[self contentView] layer].mask;
2226         return ([mask isEmptyShaped]);
2227     }
2229     - (BOOL) canProvideSnapshot
2230     {
2231         return (self.windowNumber > 0 && ![self isEmptyShaped]);
2232     }
2234     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2235     {
2236         if (![self isEmptyShaped])
2237             return;
2239         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2240         if (!force && now < lastDockIconSnapshot + 1)
2241             return;
2243         if (window)
2244         {
2245             if (![window canProvideSnapshot])
2246                 return;
2247         }
2248         else
2249         {
2250             CGFloat bestArea;
2251             for (WineWindow* childWindow in self.childWindows)
2252             {
2253                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2254                     continue;
2256                 NSSize size = childWindow.frame.size;
2257                 CGFloat area = size.width * size.height;
2258                 if (!window || area > bestArea)
2259                 {
2260                     window = childWindow;
2261                     bestArea = area;
2262                 }
2263             }
2265             if (!window)
2266                 return;
2267         }
2269         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2270         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2271         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2272         CFRelease(windowIDs);
2273         if (!windowImage)
2274             return;
2276         NSImage* appImage = [NSApp applicationIconImage];
2277         if (!appImage)
2278             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2280         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2281         [dockIcon lockFocus];
2283         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2285         CGRect rect = CGRectMake(8, 8, 240, 240);
2286         size_t width = CGImageGetWidth(windowImage);
2287         size_t height = CGImageGetHeight(windowImage);
2288         if (width > height)
2289         {
2290             rect.size.height *= height / (double)width;
2291             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2292         }
2293         else if (width != height)
2294         {
2295             rect.size.width *= width / (double)height;
2296             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2297         }
2299         CGContextDrawImage(cgcontext, rect, windowImage);
2300         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2301                     fromRect:NSZeroRect
2302                    operation:NSCompositingOperationSourceOver
2303                     fraction:1
2304               respectFlipped:YES
2305                        hints:nil];
2307         [dockIcon unlockFocus];
2309         CGImageRelease(windowImage);
2311         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2312         if (![imageView isKindOfClass:[NSImageView class]])
2313         {
2314             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2315             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2316             self.dockTile.contentView = imageView;
2317         }
2318         imageView.image = dockIcon;
2319         [self.dockTile display];
2320         lastDockIconSnapshot = now;
2321     }
2323     - (void) checkEmptyShaped
2324     {
2325         if (self.dockTile.contentView && ![self isEmptyShaped])
2326         {
2327             self.dockTile.contentView = nil;
2328             lastDockIconSnapshot = 0;
2329         }
2330         [self checkWineDisplayLink];
2331     }
2334     /*
2335      * ---------- NSWindow method overrides ----------
2336      */
2337     - (BOOL) canBecomeKeyWindow
2338     {
2339         if (causing_becomeKeyWindow == self) return YES;
2340         if (self.disabled || self.noActivate) return NO;
2341         if ([self isKeyWindow]) return YES;
2343         // If a window's collectionBehavior says it participates in cycling,
2344         // it must return YES from this method to actually be eligible.
2345         return ![self isExcludedFromWindowsMenu];
2346     }
2348     - (BOOL) canBecomeMainWindow
2349     {
2350         return [self canBecomeKeyWindow];
2351     }
2353     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2354     {
2355         // If a window is sized to completely cover a screen, then it's in
2356         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2357         // it.
2358         NSArray* screens = [NSScreen screens];
2359         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2360         if (!screen_covered_by_rect(contentRect, screens) &&
2361             frame_intersects_screens(frameRect, screens))
2362             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2363         return frameRect;
2364     }
2366     // This private method of NSWindow is called as Cocoa reacts to the display
2367     // configuration changing.  Among other things, it adjusts the window's
2368     // frame based on how the screen(s) changed size.  That tells Wine that the
2369     // window has been moved.  We don't want that.  Rather, we want to make
2370     // sure that the WinAPI notion of the window position is maintained/
2371     // restored, possibly undoing or overriding Cocoa's adjustment.
2372     //
2373     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2374     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2375     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2376     // reassert its notion of the position.  That call won't get processed
2377     // until after this method returns, so it will override whatever this
2378     // method does to the window position.  It will also discard any pending
2379     // WINDOW_FRAME_CHANGED events.
2380     //
2381     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2382     // adjust the window's position due to a display change is to hook into
2383     // this private method.  This private method has remained stable from 10.6
2384     // through 10.11.  If it does change, the most likely thing is that it
2385     // will be removed and no longer called and this fix will simply stop
2386     // working.  The only real danger would be if Apple changed the return type
2387     // to a struct or floating-point type, which would change the calling
2388     // convention.
2389     - (id) _displayChanged
2390     {
2391         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2392         [queue postEvent:event];
2393         macdrv_release_event(event);
2395         return [super _displayChanged];
2396     }
2398     - (BOOL) isExcludedFromWindowsMenu
2399     {
2400         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2401     }
2403     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2404     {
2405         BOOL ret = [super validateMenuItem:menuItem];
2407         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2408             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2409         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2410             ret = NO;
2412         return ret;
2413     }
2415     /* We don't call this.  It's the action method of the items in the Window menu. */
2416     - (void) makeKeyAndOrderFront:(id)sender
2417     {
2418         if ([self isMiniaturized])
2419             [self deminiaturize:nil];
2420         [self orderBelow:nil orAbove:nil activate:NO];
2421         [[self ancestorWineWindow] postBroughtForwardEvent];
2423         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2424             [[WineApplicationController sharedController] windowGotFocus:self];
2425     }
2427     - (void) sendEvent:(NSEvent*)event
2428     {
2429         NSEventType type = event.type;
2431         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2432            interface control.  For example, Control-Tab switches focus among
2433            views.  We want to bypass that feature, so directly route key-down
2434            events to -keyDown:. */
2435         if (type == NSEventTypeKeyDown)
2436             [[self firstResponder] keyDown:event];
2437         else
2438         {
2439             if (!draggingPhase && maximized && ![self isMovable] &&
2440                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2441                 type == NSEventTypeLeftMouseDown && (self.styleMask & NSWindowStyleMaskTitled))
2442             {
2443                 NSRect titleBar = self.frame;
2444                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2445                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2446                 titleBar.origin.y = NSMaxY(contentRect);
2448                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2450                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2451                 {
2452                     static const NSWindowButton buttons[] = {
2453                         NSWindowCloseButton,
2454                         NSWindowMiniaturizeButton,
2455                         NSWindowZoomButton,
2456                         NSWindowFullScreenButton,
2457                     };
2458                     BOOL hitButton = NO;
2459                     int i;
2461                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2462                     {
2463                         NSButton* button = [self standardWindowButton:buttons[i]];
2464                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2465                         {
2466                             hitButton = YES;
2467                             break;
2468                         }
2469                     }
2471                     if (!hitButton)
2472                     {
2473                         draggingPhase = 1;
2474                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2475                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2476                     }
2477                 }
2478             }
2479             else if (draggingPhase && (type == NSEventTypeLeftMouseDragged || type == NSEventTypeLeftMouseUp))
2480             {
2481                 if ([self isMovable])
2482                 {
2483                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2484                     NSPoint newTopLeft = dragWindowStartPosition;
2486                     newTopLeft.x += point.x - dragStartPosition.x;
2487                     newTopLeft.y += point.y - dragStartPosition.y;
2489                     if (draggingPhase == 2)
2490                     {
2491                         macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2492                         mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2493                         [queue postEvent:mevent];
2494                         macdrv_release_event(mevent);
2496                         draggingPhase = 3;
2497                     }
2499                     [self setFrameTopLeftPoint:newTopLeft];
2500                 }
2501                 else if (draggingPhase == 1 && type == NSEventTypeLeftMouseDragged)
2502                 {
2503                     macdrv_event* event;
2504                     NSRect frame = [self contentRectForFrameRect:self.frame];
2506                     [[WineApplicationController sharedController] flipRect:&frame];
2508                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2509                     event->window_restore_requested.keep_frame = TRUE;
2510                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2511                     [queue postEvent:event];
2512                     macdrv_release_event(event);
2514                     draggingPhase = 2;
2515                 }
2517                 if (type == NSEventTypeLeftMouseUp)
2518                     [self endWindowDragging];
2519             }
2521             [super sendEvent:event];
2522         }
2523     }
2525     - (void) miniaturize:(id)sender
2526     {
2527         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2528         [queue postEvent:event];
2529         macdrv_release_event(event);
2531         WineWindow* parent = (WineWindow*)self.parentWindow;
2532         if ([parent isKindOfClass:[WineWindow class]])
2533             [parent grabDockIconSnapshotFromWindow:self force:YES];
2534     }
2536     - (void) toggleFullScreen:(id)sender
2537     {
2538         if (!self.disabled && !maximized)
2539             [super toggleFullScreen:sender];
2540     }
2542     - (void) setViewsNeedDisplay:(BOOL)value
2543     {
2544         if (value && ![self viewsNeedDisplay])
2545         {
2546             WineDisplayLink* link = [self wineDisplayLink];
2547             if (link)
2548             {
2549                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2550                 if (_lastDisplayTime + [link refreshPeriod] < now)
2551                     [self setAutodisplay:YES];
2552                 else
2553                 {
2554                     [link start];
2555                     _lastDisplayTime = now;
2556                 }
2557             }
2558             else
2559                 [self setAutodisplay:YES];
2560         }
2561         [super setViewsNeedDisplay:value];
2562     }
2564     - (void) display
2565     {
2566         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2567         [super display];
2568         if (_lastDisplayID)
2569             [self setAutodisplay:NO];
2570     }
2572     - (void) displayIfNeeded
2573     {
2574         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2575         [super displayIfNeeded];
2576         if (_lastDisplayID)
2577             [self setAutodisplay:NO];
2578     }
2580     - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2581     {
2582         if (flag)
2583             [self setAutodisplay:YES];
2584         [super setFrame:frameRect display:flag];
2585     }
2587     - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2588     {
2589         if (displayFlag)
2590             [self setAutodisplay:YES];
2591         [super setFrame:frameRect display:displayFlag animate:animateFlag];
2592     }
2594     - (void) windowDidDrawContent
2595     {
2596         if (!drawnSinceShown)
2597         {
2598             drawnSinceShown = YES;
2599             dispatch_async(dispatch_get_main_queue(), ^{
2600                 [self checkTransparency];
2601             });
2602         }
2603     }
2605     - (NSArray*) childWineWindows
2606     {
2607         NSArray* childWindows = self.childWindows;
2608         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2609             return [child isKindOfClass:[WineWindow class]];
2610         }];
2611         return [childWindows objectsAtIndexes:indexes];
2612     }
2614     - (void) updateForGLSubviews
2615     {
2616         if (gl_surface_mode == GL_SURFACE_BEHIND)
2617             [self checkTransparency];
2618     }
2620     - (void) setRetinaMode:(int)mode
2621     {
2622         NSRect frame;
2623         double scale = mode ? 0.5 : 2.0;
2624         NSAffineTransform* transform = [NSAffineTransform transform];
2626         [transform scaleBy:scale];
2628         [[self contentView] layer].mask.contentsScale = mode ? 2.0 : 1.0;
2630         for (WineBaseView* subview in [self.contentView subviews])
2631         {
2632             if ([subview isKindOfClass:[WineBaseView class]])
2633                 [subview setRetinaMode:mode];
2634         }
2636         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2637         frame.origin.x *= scale;
2638         frame.origin.y *= scale;
2639         frame.size.width *= scale;
2640         frame.size.height *= scale;
2641         frame = [self frameRectForContentRect:frame];
2643         savedContentMinSize = [transform transformSize:savedContentMinSize];
2644         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2645             savedContentMaxSize.width *= scale;
2646         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2647             savedContentMaxSize.height *= scale;
2649         self.contentMinSize = [transform transformSize:self.contentMinSize];
2650         NSSize temp = self.contentMaxSize;
2651         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2652             temp.width *= scale;
2653         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2654             temp.height *= scale;
2655         self.contentMaxSize = temp;
2657         ignore_windowResize = TRUE;
2658         [self setFrameAndWineFrame:frame];
2659         ignore_windowResize = FALSE;
2660     }
2663     /*
2664      * ---------- NSResponder method overrides ----------
2665      */
2666     - (void) keyDown:(NSEvent *)theEvent
2667     {
2668         if ([theEvent isARepeat])
2669         {
2670             if (!allowKeyRepeats)
2671                 return;
2672         }
2673         else
2674             allowKeyRepeats = YES;
2676         [self postKeyEvent:theEvent];
2677     }
2679     - (void) flagsChanged:(NSEvent *)theEvent
2680     {
2681         static const struct {
2682             NSUInteger  mask;
2683             uint16_t    keycode;
2684         } modifiers[] = {
2685             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2686             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2687             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2688             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2689             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2690             { NX_DEVICELALTKEYMASK,     kVK_Option },
2691             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2692             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2693             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2694         };
2696         NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2697         NSUInteger changed;
2698         int i, last_changed;
2700         fix_device_modifiers_by_generic(&modifierFlags);
2701         changed = modifierFlags ^ lastModifierFlags;
2703         last_changed = -1;
2704         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2705             if (changed & modifiers[i].mask)
2706                 last_changed = i;
2708         for (i = 0; i <= last_changed; i++)
2709         {
2710             if (changed & modifiers[i].mask)
2711             {
2712                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2714                 if (pressed)
2715                     allowKeyRepeats = NO;
2717                 if (i == last_changed)
2718                     lastModifierFlags = modifierFlags;
2719                 else
2720                 {
2721                     lastModifierFlags ^= modifiers[i].mask;
2722                     fix_generic_modifiers_by_device(&lastModifierFlags);
2723                 }
2725                 // Caps lock generates one event for each press-release action.
2726                 // We need to simulate a pair of events for each actual event.
2727                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2728                 {
2729                     [self postKey:modifiers[i].keycode
2730                           pressed:TRUE
2731                         modifiers:lastModifierFlags
2732                             event:(NSEvent*)theEvent];
2733                     pressed = FALSE;
2734                 }
2736                 [self postKey:modifiers[i].keycode
2737                       pressed:pressed
2738                     modifiers:lastModifierFlags
2739                         event:(NSEvent*)theEvent];
2740             }
2741         }
2742     }
2744     - (void) applicationWillHide
2745     {
2746         savedVisibleState = [self isVisible];
2747     }
2749     - (void) applicationDidUnhide
2750     {
2751         if ([self isVisible])
2752             [self becameEligibleParentOrChild];
2753     }
2756     /*
2757      * ---------- NSWindowDelegate methods ----------
2758      */
2759     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2760     {
2761         macdrv_query* query;
2762         NSSize size;
2764         query = macdrv_create_query();
2765         query->type = QUERY_MIN_MAX_INFO;
2766         query->window = (macdrv_window)[self retain];
2767         [self.queue query:query timeout:0.5];
2768         macdrv_release_query(query);
2770         size = [self contentMaxSize];
2771         if (proposedSize.width < size.width)
2772             size.width = proposedSize.width;
2773         if (proposedSize.height < size.height)
2774             size.height = proposedSize.height;
2775         return size;
2776     }
2778     - (void)windowDidBecomeKey:(NSNotification *)notification
2779     {
2780         WineApplicationController* controller = [WineApplicationController sharedController];
2781         NSEvent* event = [controller lastFlagsChanged];
2782         if (event)
2783             [self flagsChanged:event];
2785         if (causing_becomeKeyWindow == self) return;
2787         [controller windowGotFocus:self];
2788     }
2790     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2791     {
2792         [self checkWineDisplayLink];
2793     }
2795     - (void) windowDidChangeScreen:(NSNotification*)notification
2796     {
2797         [self checkWineDisplayLink];
2798     }
2800     - (void)windowDidDeminiaturize:(NSNotification *)notification
2801     {
2802         WineApplicationController* controller = [WineApplicationController sharedController];
2804         if (!ignore_windowDeminiaturize)
2805             [self postDidUnminimizeEvent];
2806         ignore_windowDeminiaturize = FALSE;
2808         [self becameEligibleParentOrChild];
2810         if (fullscreen && [self isOnActiveSpace])
2811             [controller updateFullscreenWindows];
2812         [controller adjustWindowLevels];
2814         if (![self parentWindow])
2815             [self postBroughtForwardEvent];
2817         if (!self.disabled && !self.noActivate)
2818         {
2819             causing_becomeKeyWindow = self;
2820             [self makeKeyWindow];
2821             causing_becomeKeyWindow = nil;
2822             [controller windowGotFocus:self];
2823         }
2825         [self windowDidResize:notification];
2826         [self checkWineDisplayLink];
2827     }
2829     - (void) windowDidEndLiveResize:(NSNotification *)notification
2830     {
2831         if (!maximized)
2832         {
2833             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2834             [queue postEvent:event];
2835             macdrv_release_event(event);
2836         }
2837     }
2839     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2840     {
2841         enteringFullScreen = FALSE;
2842         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2843         if (pendingOrderOut)
2844             [self doOrderOut];
2845     }
2847     - (void) windowDidExitFullScreen:(NSNotification*)notification
2848     {
2849         exitingFullScreen = FALSE;
2850         [self setFrameAndWineFrame:nonFullscreenFrame];
2851         [self windowDidResize:nil];
2852         if (pendingOrderOut)
2853             [self doOrderOut];
2854     }
2856     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2857     {
2858         enteringFullScreen = FALSE;
2859         enteredFullScreenTime = 0;
2860         if (pendingOrderOut)
2861             [self doOrderOut];
2862     }
2864     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2865     {
2866         exitingFullScreen = FALSE;
2867         [self windowDidResize:nil];
2868         if (pendingOrderOut)
2869             [self doOrderOut];
2870     }
2872     - (void)windowDidMiniaturize:(NSNotification *)notification
2873     {
2874         macdrv_event* event;
2876         if (fullscreen && [self isOnActiveSpace])
2877             [[WineApplicationController sharedController] updateFullscreenWindows];
2879         [self checkWineDisplayLink];
2881         event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
2882         [queue postEvent:event];
2883         macdrv_release_event(event);
2884     }
2886     - (void)windowDidMove:(NSNotification *)notification
2887     {
2888         [self windowDidResize:notification];
2889     }
2891     - (void)windowDidResignKey:(NSNotification *)notification
2892     {
2893         macdrv_event* event;
2895         if (causing_becomeKeyWindow) return;
2897         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2898         [queue postEvent:event];
2899         macdrv_release_event(event);
2900     }
2902     - (void)windowDidResize:(NSNotification *)notification skipSizeMove:(BOOL)skipSizeMove
2903     {
2904         NSRect frame = self.wine_fractionalFrame;
2906         if ([self inLiveResize])
2907         {
2908             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2909                 resizingFromLeft = TRUE;
2910             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2911                 resizingFromTop = TRUE;
2912         }
2914         if (ignore_windowResize || exitingFullScreen) return;
2916         if ([self preventResizing])
2917         {
2918             NSRect contentRect = [self contentRectForFrameRect:frame];
2919             [self setContentMinSize:contentRect.size];
2920             [self setContentMaxSize:contentRect.size];
2921         }
2923         [self postWindowFrameChanged:frame
2924                           fullscreen:([self styleMask] & NSWindowStyleMaskFullScreen) != 0
2925                             resizing:[self inLiveResize]
2926                         skipSizeMove:skipSizeMove];
2928         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2929         [self updateFullscreen];
2930     }
2932     - (void)windowDidResize:(NSNotification *)notification
2933     {
2934         [self windowDidResize:notification skipSizeMove:FALSE];
2935     }
2937     - (BOOL)windowShouldClose:(id)sender
2938     {
2939         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2940         [queue postEvent:event];
2941         macdrv_release_event(event);
2942         return NO;
2943     }
2945     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2946     {
2947         if (maximized)
2948         {
2949             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2950             [queue postEvent:event];
2951             macdrv_release_event(event);
2952             return NO;
2953         }
2954         else if (!resizable)
2955         {
2956             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2957             [queue postEvent:event];
2958             macdrv_release_event(event);
2959             return NO;
2960         }
2962         return YES;
2963     }
2965     - (void) windowWillClose:(NSNotification*)notification
2966     {
2967         WineWindow* child;
2969         if (fakingClose) return;
2970         if (latentParentWindow)
2971         {
2972             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2973             self.latentParentWindow = nil;
2974         }
2976         for (child in latentChildWindows)
2977         {
2978             if (child.latentParentWindow == self)
2979                 child.latentParentWindow = nil;
2980         }
2981         [latentChildWindows removeAllObjects];
2982     }
2984     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2985     {
2986         enteringFullScreen = TRUE;
2987         nonFullscreenFrame = self.wine_fractionalFrame;
2988     }
2990     - (void) windowWillExitFullScreen:(NSNotification*)notification
2991     {
2992         exitingFullScreen = TRUE;
2993         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE skipSizeMove:FALSE];
2994     }
2996     - (void)windowWillMiniaturize:(NSNotification *)notification
2997     {
2998         [self becameIneligibleParentOrChild];
2999         [self grabDockIconSnapshotFromWindow:nil force:NO];
3000     }
3002     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
3003     {
3004         if ([self inLiveResize])
3005         {
3006             if (maximized)
3007                 return self.wine_fractionalFrame.size;
3009             NSRect rect;
3010             macdrv_query* query;
3012             rect = [self frame];
3013             if (resizingFromLeft)
3014                 rect.origin.x = NSMaxX(rect) - frameSize.width;
3015             if (!resizingFromTop)
3016                 rect.origin.y = NSMaxY(rect) - frameSize.height;
3017             rect.size = frameSize;
3018             rect = [self contentRectForFrameRect:rect];
3019             [[WineApplicationController sharedController] flipRect:&rect];
3021             query = macdrv_create_query();
3022             query->type = QUERY_RESIZE_SIZE;
3023             query->window = (macdrv_window)[self retain];
3024             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3025             query->resize_size.from_left = resizingFromLeft;
3026             query->resize_size.from_top = resizingFromTop;
3028             if ([self.queue query:query timeout:0.1])
3029             {
3030                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3031                 rect = [self frameRectForContentRect:rect];
3032                 frameSize = rect.size;
3033             }
3035             macdrv_release_query(query);
3036         }
3038         return frameSize;
3039     }
3041     - (void) windowWillStartLiveResize:(NSNotification *)notification
3042     {
3043         [self endWindowDragging];
3045         if (maximized)
3046         {
3047             macdrv_event* event;
3048             NSRect frame = [self contentRectForFrameRect:self.frame];
3050             [[WineApplicationController sharedController] flipRect:&frame];
3052             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3053             event->window_restore_requested.keep_frame = TRUE;
3054             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3055             [queue postEvent:event];
3056             macdrv_release_event(event);
3057         }
3058         else
3059             [self sendResizeStartQuery];
3061         frameAtResizeStart = [self frame];
3062         resizingFromLeft = resizingFromTop = FALSE;
3063     }
3065     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3066     {
3067         macdrv_query* query;
3068         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3069         NSSize maxSize;
3071         query = macdrv_create_query();
3072         query->type = QUERY_MIN_MAX_INFO;
3073         query->window = (macdrv_window)[self retain];
3074         [self.queue query:query timeout:0.5];
3075         macdrv_release_query(query);
3077         currentContentRect = [self contentRectForFrameRect:[self frame]];
3078         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3080         maxSize = [self contentMaxSize];
3081         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3082         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3084         // Try to keep the top-left corner where it is.
3085         newContentRect.origin.x = NSMinX(currentContentRect);
3086         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3088         // If that pushes the bottom or right off the screen, pull it up and to the left.
3089         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3090         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3091             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3092         if (NSMinY(newContentRect) < NSMinY(screenRect))
3093             newContentRect.origin.y = NSMinY(screenRect);
3095         // If that pushes the top or left off the screen, push it down and the right
3096         // again.  Do this last because the top-left corner is more important than the
3097         // bottom-right.
3098         if (NSMinX(newContentRect) < NSMinX(screenRect))
3099             newContentRect.origin.x = NSMinX(screenRect);
3100         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3101             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3103         return [self frameRectForContentRect:newContentRect];
3104     }
3107     /*
3108      * ---------- NSPasteboardOwner methods ----------
3109      */
3110     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3111     {
3112         macdrv_query* query = macdrv_create_query();
3113         query->type = QUERY_PASTEBOARD_DATA;
3114         query->window = (macdrv_window)[self retain];
3115         query->pasteboard_data.type = (CFStringRef)[type copy];
3117         [self.queue query:query timeout:3];
3118         macdrv_release_query(query);
3119     }
3121     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3122     {
3123         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3124         [queue postEvent:event];
3125         macdrv_release_event(event);
3126     }
3129     /*
3130      * ---------- NSDraggingDestination methods ----------
3131      */
3132     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3133     {
3134         return [self draggingUpdated:sender];
3135     }
3137     - (void) draggingExited:(id <NSDraggingInfo>)sender
3138     {
3139         // This isn't really a query.  We don't need any response.  However, it
3140         // has to be processed in a similar manner as the other drag-and-drop
3141         // queries in order to maintain the proper order of operations.
3142         macdrv_query* query = macdrv_create_query();
3143         query->type = QUERY_DRAG_EXITED;
3144         query->window = (macdrv_window)[self retain];
3146         [self.queue query:query timeout:0.1];
3147         macdrv_release_query(query);
3148     }
3150     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3151     {
3152         NSDragOperation ret;
3153         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3154         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3155         NSPasteboard* pb = [sender draggingPasteboard];
3157         macdrv_query* query = macdrv_create_query();
3158         query->type = QUERY_DRAG_OPERATION;
3159         query->window = (macdrv_window)[self retain];
3160         query->drag_operation.x = floor(cgpt.x);
3161         query->drag_operation.y = floor(cgpt.y);
3162         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3163         query->drag_operation.accepted_op = NSDragOperationNone;
3164         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3166         [self.queue query:query timeout:3];
3167         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3168         macdrv_release_query(query);
3170         return ret;
3171     }
3173     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3174     {
3175         BOOL ret;
3176         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3177         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3178         NSPasteboard* pb = [sender draggingPasteboard];
3180         macdrv_query* query = macdrv_create_query();
3181         query->type = QUERY_DRAG_DROP;
3182         query->window = (macdrv_window)[self retain];
3183         query->drag_drop.x = floor(cgpt.x);
3184         query->drag_drop.y = floor(cgpt.y);
3185         query->drag_drop.op = [sender draggingSourceOperationMask];
3186         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3188         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3189         ret = query->status;
3190         macdrv_release_query(query);
3192         return ret;
3193     }
3195     - (BOOL) wantsPeriodicDraggingUpdates
3196     {
3197         return NO;
3198     }
3200 @end
3203 /***********************************************************************
3204  *              macdrv_create_cocoa_window
3206  * Create a Cocoa window with the given content frame and features (e.g.
3207  * title bar, close box, etc.).
3208  */
3209 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3210         CGRect frame, void* hwnd, macdrv_event_queue queue)
3212     __block WineWindow* window;
3214     OnMainThread(^{
3215         window = [[WineWindow createWindowWithFeatures:wf
3216                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3217                                                   hwnd:hwnd
3218                                                  queue:(WineEventQueue*)queue] retain];
3219     });
3221     return (macdrv_window)window;
3224 /***********************************************************************
3225  *              macdrv_destroy_cocoa_window
3227  * Destroy a Cocoa window.
3228  */
3229 void macdrv_destroy_cocoa_window(macdrv_window w)
3231     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3232     WineWindow* window = (WineWindow*)w;
3234     OnMainThread(^{
3235         [window doOrderOut];
3236         [window close];
3237     });
3238     [window.queue discardEventsMatchingMask:-1 forWindow:window];
3239     [window release];
3241     [pool release];
3244 /***********************************************************************
3245  *              macdrv_get_window_hwnd
3247  * Get the hwnd that was set for the window at creation.
3248  */
3249 void* macdrv_get_window_hwnd(macdrv_window w)
3251     WineWindow* window = (WineWindow*)w;
3252     return window.hwnd;
3255 /***********************************************************************
3256  *              macdrv_set_cocoa_window_features
3258  * Update a Cocoa window's features.
3259  */
3260 void macdrv_set_cocoa_window_features(macdrv_window w,
3261         const struct macdrv_window_features* wf)
3263     WineWindow* window = (WineWindow*)w;
3265     OnMainThread(^{
3266         [window setWindowFeatures:wf];
3267     });
3270 /***********************************************************************
3271  *              macdrv_set_cocoa_window_state
3273  * Update a Cocoa window's state.
3274  */
3275 void macdrv_set_cocoa_window_state(macdrv_window w,
3276         const struct macdrv_window_state* state)
3278     WineWindow* window = (WineWindow*)w;
3280     OnMainThread(^{
3281         [window setMacDrvState:state];
3282     });
3285 /***********************************************************************
3286  *              macdrv_set_cocoa_window_title
3288  * Set a Cocoa window's title.
3289  */
3290 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3291         size_t length)
3293     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3294     WineWindow* window = (WineWindow*)w;
3295     NSString* titleString;
3297     if (title)
3298         titleString = [NSString stringWithCharacters:title length:length];
3299     else
3300         titleString = @"";
3301     OnMainThreadAsync(^{
3302         [window setTitle:titleString];
3303         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3304             [NSApp changeWindowsItem:window title:titleString filename:NO];
3305     });
3307     [pool release];
3310 /***********************************************************************
3311  *              macdrv_order_cocoa_window
3313  * Reorder a Cocoa window relative to other windows.  If prev is
3314  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3315  * it is ordered above that window.  Otherwise, it is ordered to the
3316  * front.
3317  */
3318 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3319         macdrv_window n, int activate)
3321     WineWindow* window = (WineWindow*)w;
3322     WineWindow* prev = (WineWindow*)p;
3323     WineWindow* next = (WineWindow*)n;
3325     OnMainThreadAsync(^{
3326         [window orderBelow:prev
3327                    orAbove:next
3328                   activate:activate];
3329     });
3330     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3331                                   forWindow:window];
3332     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3333                                 forWindow:next];
3336 /***********************************************************************
3337  *              macdrv_hide_cocoa_window
3339  * Hides a Cocoa window.
3340  */
3341 void macdrv_hide_cocoa_window(macdrv_window w)
3343     WineWindow* window = (WineWindow*)w;
3345     OnMainThread(^{
3346         [window doOrderOut];
3347     });
3350 /***********************************************************************
3351  *              macdrv_set_cocoa_window_frame
3353  * Move a Cocoa window.
3354  */
3355 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3357     WineWindow* window = (WineWindow*)w;
3359     OnMainThread(^{
3360         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3361     });
3364 /***********************************************************************
3365  *              macdrv_get_cocoa_window_frame
3367  * Gets the frame of a Cocoa window.
3368  */
3369 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3371     WineWindow* window = (WineWindow*)w;
3373     OnMainThread(^{
3374         NSRect frame;
3376         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3377         [[WineApplicationController sharedController] flipRect:&frame];
3378         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3379     });
3382 /***********************************************************************
3383  *              macdrv_set_cocoa_parent_window
3385  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3386  * the parent window.
3387  */
3388 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3390     WineWindow* window = (WineWindow*)w;
3392     OnMainThread(^{
3393         [window setMacDrvParentWindow:(WineWindow*)parent];
3394     });
3397 /***********************************************************************
3398  *              macdrv_set_window_surface
3399  */
3400 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3402     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3403     WineWindow* window = (WineWindow*)w;
3405     OnMainThread(^{
3406         window.surface = surface;
3407         window.surface_mutex = mutex;
3408     });
3410     [pool release];
3413 /***********************************************************************
3414  *              macdrv_window_needs_display
3416  * Mark a window as needing display in a specified rect (in non-client
3417  * area coordinates).
3418  */
3419 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3421     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3422     WineWindow* window = (WineWindow*)w;
3424     OnMainThreadAsync(^{
3425         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3426     });
3428     [pool release];
3431 /***********************************************************************
3432  *              macdrv_set_window_shape
3434  * Sets the shape of a Cocoa window from an array of rectangles.  If
3435  * rects is NULL, resets the window's shape to its frame.
3436  */
3437 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3439     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3440     WineWindow* window = (WineWindow*)w;
3442     OnMainThread(^{
3443         if (!rects || !count)
3444         {
3445             [window setShape:NULL];
3446             [window checkEmptyShaped];
3447         }
3448         else
3449         {
3450             CGMutablePathRef path;
3451             unsigned int i;
3453             path = CGPathCreateMutable();
3454             for (i = 0; i < count; i++)
3455                 CGPathAddRect(path, NULL, cgrect_mac_from_win(rects[i]));
3456             [window setShape:path];
3457             CGPathRelease(path);
3458         }
3459     });
3461     [pool release];
3464 /***********************************************************************
3465  *              macdrv_set_window_alpha
3466  */
3467 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3469     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3470     WineWindow* window = (WineWindow*)w;
3472     [window setAlphaValue:alpha];
3474     [pool release];
3477 /***********************************************************************
3478  *              macdrv_set_window_color_key
3479  */
3480 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3481                                  CGFloat keyBlue)
3483     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3484     WineWindow* window = (WineWindow*)w;
3486     OnMainThread(^{
3487         window.colorKeyed       = TRUE;
3488         window.colorKeyRed      = keyRed;
3489         window.colorKeyGreen    = keyGreen;
3490         window.colorKeyBlue     = keyBlue;
3491         [window checkTransparency];
3492     });
3494     [pool release];
3497 /***********************************************************************
3498  *              macdrv_clear_window_color_key
3499  */
3500 void macdrv_clear_window_color_key(macdrv_window w)
3502     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3503     WineWindow* window = (WineWindow*)w;
3505     OnMainThread(^{
3506         window.colorKeyed = FALSE;
3507         [window checkTransparency];
3508     });
3510     [pool release];
3513 /***********************************************************************
3514  *              macdrv_window_use_per_pixel_alpha
3515  */
3516 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3518     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3519     WineWindow* window = (WineWindow*)w;
3521     OnMainThread(^{
3522         window.usePerPixelAlpha = use_per_pixel_alpha;
3523         [window checkTransparency];
3524     });
3526     [pool release];
3529 /***********************************************************************
3530  *              macdrv_give_cocoa_window_focus
3532  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3533  * orders it front and, if its frame was not within the desktop bounds,
3534  * Cocoa will typically move it on-screen.
3535  */
3536 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3538     WineWindow* window = (WineWindow*)w;
3540     OnMainThread(^{
3541         [window makeFocused:activate];
3542     });
3545 /***********************************************************************
3546  *              macdrv_set_window_min_max_sizes
3548  * Sets the window's minimum and maximum content sizes.
3549  */
3550 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3552     WineWindow* window = (WineWindow*)w;
3554     OnMainThread(^{
3555         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3556     });
3559 /***********************************************************************
3560  *              macdrv_create_view
3562  * Creates and returns a view with the specified frame rect.  The
3563  * caller is responsible for calling macdrv_dispose_view() on the view
3564  * when it is done with it.
3565  */
3566 macdrv_view macdrv_create_view(CGRect rect)
3568     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3569     __block WineContentView* view;
3571     if (CGRectIsNull(rect)) rect = CGRectZero;
3573     OnMainThread(^{
3574         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3576         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3577         [view setWantsLayer:YES];
3578         [view layer].minificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
3579         [view layer].magnificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
3580         [view layer].contentsScale = retina_on ? 2.0 : 1.0;
3581         [view setAutoresizesSubviews:NO];
3582         [view setAutoresizingMask:NSViewNotSizable];
3583         [view setHidden:YES];
3584         [view setWantsBestResolutionOpenGLSurface:retina_on];
3585         [nc addObserver:view
3586                selector:@selector(updateGLContexts)
3587                    name:NSViewGlobalFrameDidChangeNotification
3588                  object:view];
3589         [nc addObserver:view
3590                selector:@selector(updateGLContexts)
3591                    name:NSApplicationDidChangeScreenParametersNotification
3592                  object:NSApp];
3593     });
3595     [pool release];
3596     return (macdrv_view)view;
3599 /***********************************************************************
3600  *              macdrv_dispose_view
3602  * Destroys a view previously returned by macdrv_create_view.
3603  */
3604 void macdrv_dispose_view(macdrv_view v)
3606     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3607     WineContentView* view = (WineContentView*)v;
3609     OnMainThread(^{
3610         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3611         WineWindow* window = (WineWindow*)[view window];
3613         [nc removeObserver:view
3614                       name:NSViewGlobalFrameDidChangeNotification
3615                     object:view];
3616         [nc removeObserver:view
3617                       name:NSApplicationDidChangeScreenParametersNotification
3618                     object:NSApp];
3619         [view removeFromSuperview];
3620         [view release];
3621         [window updateForGLSubviews];
3622     });
3624     [pool release];
3627 /***********************************************************************
3628  *              macdrv_set_view_frame
3629  */
3630 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3632     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3633     WineContentView* view = (WineContentView*)v;
3635     if (CGRectIsNull(rect)) rect = CGRectZero;
3637     OnMainThreadAsync(^{
3638         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3639         NSRect oldFrame = [view frame];
3641         if (!NSEqualRects(oldFrame, newFrame))
3642         {
3643             [[view superview] setNeedsDisplayInRect:oldFrame];
3644             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3645                 [view setFrameSize:newFrame.size];
3646             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3647                 [view setFrameOrigin:newFrame.origin];
3648             else
3649                 [view setFrame:newFrame];
3650             [view setNeedsDisplay:YES];
3652             if (retina_enabled)
3653             {
3654                 int backing_size[2] = { 0 };
3655                 [view wine_setBackingSize:backing_size];
3656             }
3657             [(WineWindow*)[view window] updateForGLSubviews];
3658         }
3659     });
3661     [pool release];
3664 /***********************************************************************
3665  *              macdrv_set_view_superview
3667  * Move a view to a new superview and position it relative to its
3668  * siblings.  If p is non-NULL, the view is ordered behind it.
3669  * Otherwise, the view is ordered above n.  If s is NULL, use the
3670  * content view of w as the new superview.
3671  */
3672 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3674     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3675     WineContentView* view = (WineContentView*)v;
3676     WineContentView* superview = (WineContentView*)s;
3677     WineWindow* window = (WineWindow*)w;
3678     WineContentView* prev = (WineContentView*)p;
3679     WineContentView* next = (WineContentView*)n;
3681     if (!superview)
3682         superview = [window contentView];
3684     OnMainThreadAsync(^{
3685         if (superview == [view superview])
3686         {
3687             NSArray* subviews = [superview subviews];
3688             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3689             if (!prev && !next && index == [subviews count] - 1)
3690                 return;
3691             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3692                 return;
3693             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3694                 return;
3695         }
3697         WineWindow* oldWindow = (WineWindow*)[view window];
3698         WineWindow* newWindow = (WineWindow*)[superview window];
3700 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3701         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3702             [view removeFromSuperview];
3703 #endif
3704         if (prev)
3705             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3706         else
3707             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3709         if (oldWindow != newWindow)
3710         {
3711             [oldWindow updateForGLSubviews];
3712             [newWindow updateForGLSubviews];
3713         }
3714     });
3716     [pool release];
3719 /***********************************************************************
3720  *              macdrv_set_view_hidden
3721  */
3722 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3724     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3725     WineContentView* view = (WineContentView*)v;
3727     OnMainThreadAsync(^{
3728         [view setHidden:hidden];
3729         [(WineWindow*)view.window updateForGLSubviews];
3730     });
3732     [pool release];
3735 /***********************************************************************
3736  *              macdrv_add_view_opengl_context
3738  * Add an OpenGL context to the list being tracked for each view.
3739  */
3740 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3742     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3743     WineContentView* view = (WineContentView*)v;
3744     WineOpenGLContext *context = (WineOpenGLContext*)c;
3746     OnMainThread(^{
3747         [view addGLContext:context];
3748     });
3750     [pool release];
3753 /***********************************************************************
3754  *              macdrv_remove_view_opengl_context
3756  * Add an OpenGL context to the list being tracked for each view.
3757  */
3758 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3760     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3761     WineContentView* view = (WineContentView*)v;
3762     WineOpenGLContext *context = (WineOpenGLContext*)c;
3764     OnMainThreadAsync(^{
3765         [view removeGLContext:context];
3766     });
3768     [pool release];
3771 #ifdef HAVE_METAL_METAL_H
3772 macdrv_metal_device macdrv_create_metal_device(void)
3774     macdrv_metal_device ret;
3776 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3777     if (MTLCreateSystemDefaultDevice == NULL)
3778         return NULL;
3779 #endif
3781     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3782     ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3783     [pool release];
3784     return ret;
3787 void macdrv_release_metal_device(macdrv_metal_device d)
3789     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3790     [(id<MTLDevice>)d release];
3791     [pool release];
3794 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3796     id<MTLDevice> device = (id<MTLDevice>)d;
3797     WineContentView* view = (WineContentView*)v;
3798     __block WineMetalView *metalView;
3800     OnMainThread(^{
3801         metalView = [view newMetalViewWithDevice:device];
3802     });
3804     return (macdrv_metal_view)metalView;
3807 macdrv_metal_layer macdrv_view_get_metal_layer(macdrv_metal_view v)
3809     WineMetalView* view = (WineMetalView*)v;
3810     __block CAMetalLayer* layer;
3812     OnMainThread(^{
3813         layer = (CAMetalLayer*)view.layer;
3814     });
3816     return (macdrv_metal_layer)layer;
3819 void macdrv_view_release_metal_view(macdrv_metal_view v)
3821     WineMetalView* view = (WineMetalView*)v;
3822     OnMainThread(^{
3823         [view removeFromSuperview];
3824         [view release];
3825     });
3827 #endif
3829 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3831     WineContentView* view = (WineContentView*)v;
3833     if (![view isKindOfClass:[WineContentView class]])
3834         return FALSE;
3836     [view wine_getBackingSize:backing_size];
3837     return TRUE;
3840 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3842     WineContentView* view = (WineContentView*)v;
3844     if ([view isKindOfClass:[WineContentView class]])
3845         [view wine_setBackingSize:backing_size];
3848 /***********************************************************************
3849  *              macdrv_window_background_color
3851  * Returns the standard Mac window background color as a 32-bit value of
3852  * the form 0x00rrggbb.
3853  */
3854 uint32_t macdrv_window_background_color(void)
3856     static uint32_t result;
3857     static dispatch_once_t once;
3859     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3860     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3861     // of it is to draw with it.
3862     dispatch_once(&once, ^{
3863         OnMainThread(^{
3864             unsigned char rgbx[4];
3865             unsigned char *planes = rgbx;
3866             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3867                                                                                pixelsWide:1
3868                                                                                pixelsHigh:1
3869                                                                             bitsPerSample:8
3870                                                                           samplesPerPixel:3
3871                                                                                  hasAlpha:NO
3872                                                                                  isPlanar:NO
3873                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3874                                                                              bitmapFormat:0
3875                                                                               bytesPerRow:4
3876                                                                              bitsPerPixel:32];
3877             [NSGraphicsContext saveGraphicsState];
3878             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3879             [[NSColor windowBackgroundColor] set];
3880             NSRectFill(NSMakeRect(0, 0, 1, 1));
3881             [NSGraphicsContext restoreGraphicsState];
3882             [bitmap release];
3883             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3884         });
3885     });
3887     return result;
3890 /***********************************************************************
3891  *              macdrv_send_text_input_event
3892  */
3893 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3895     OnMainThreadAsync(^{
3896         BOOL ret;
3897         macdrv_event* event;
3898         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3899         if (![window isKindOfClass:[WineWindow class]])
3900         {
3901             window = (WineWindow*)[NSApp mainWindow];
3902             if (![window isKindOfClass:[WineWindow class]])
3903                 window = [[WineApplicationController sharedController] frontWineWindow];
3904         }
3906         if (window)
3907         {
3908             NSUInteger localFlags = flags;
3909             CGEventRef c;
3910             NSEvent* event;
3912             window.imeData = data;
3913             fix_device_modifiers_by_generic(&localFlags);
3915             // An NSEvent created with +keyEventWithType:... is internally marked
3916             // as synthetic and doesn't get sent through input methods.  But one
3917             // created from a CGEvent doesn't have that problem.
3918             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3919             CGEventSetFlags(c, localFlags);
3920             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3921             event = [NSEvent eventWithCGEvent:c];
3922             CFRelease(c);
3924             window.commandDone = FALSE;
3925             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3926         }
3927         else
3928             ret = FALSE;
3930         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3931         event->sent_text_input.handled = ret;
3932         event->sent_text_input.done = done;
3933         [[window queue] postEvent:event];
3934         macdrv_release_event(event);
3935     });
3938 void macdrv_clear_ime_text(void)
3940     OnMainThreadAsync(^{
3941         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3942         if (![window isKindOfClass:[WineWindow class]])
3943         {
3944             window = (WineWindow*)[NSApp mainWindow];
3945             if (![window isKindOfClass:[WineWindow class]])
3946                 window = [[WineApplicationController sharedController] frontWineWindow];
3947         }
3948         if (window)
3949             [[window contentView] clearMarkedText];
3950     });