winemac: Move CVDisplayLink operations out of @synchronized blocks to avoid potential...
[wine.git] / dlls / winemac.drv / cocoa_window.m
blob7cb3459efb5296ab24c9800f22033465ec7576d9
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 #import <Carbon/Carbon.h>
22 #import <CoreVideo/CoreVideo.h>
24 #import "cocoa_window.h"
26 #include "macdrv_cocoa.h"
27 #import "cocoa_app.h"
28 #import "cocoa_event.h"
29 #import "cocoa_opengl.h"
32 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
33 enum {
34     NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
35     NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
36     NSWindowFullScreenButton = 7,
37     NSFullScreenWindowMask = 1 << 14,
40 @interface NSWindow (WineFullScreenExtensions)
41     - (void) toggleFullScreen:(id)sender;
42 @end
43 #endif
46 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
47 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
48 enum {
49     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
51 #endif
54 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
56     NSUInteger style_mask;
58     if (wf->title_bar)
59     {
60         style_mask = NSTitledWindowMask;
61         if (wf->close_button) style_mask |= NSClosableWindowMask;
62         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
63         if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
64         if (wf->utility) style_mask |= NSUtilityWindowMask;
65     }
66     else style_mask = NSBorderlessWindowMask;
68     return style_mask;
72 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
74     NSScreen* screen;
75     for (screen in screens)
76     {
77         if (NSIntersectsRect(frame, [screen frame]))
78             return TRUE;
79     }
80     return FALSE;
84 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
86     for (NSScreen* screen in screens)
87     {
88         if (NSContainsRect(rect, [screen frame]))
89             return screen;
90     }
91     return nil;
95 /* We rely on the supposedly device-dependent modifier flags to distinguish the
96    keys on the left side of the keyboard from those on the right.  Some event
97    sources don't set those device-depdendent flags.  If we see a device-independent
98    flag for a modifier without either corresponding device-dependent flag, assume
99    the left one. */
100 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
102     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
103         *modifiers |= NX_DEVICELCMDKEYMASK;
104     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
105         *modifiers |= NX_DEVICELSHIFTKEYMASK;
106     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
107         *modifiers |= NX_DEVICELCTLKEYMASK;
108     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
109         *modifiers |= NX_DEVICELALTKEYMASK;
112 /* As we manipulate individual bits of a modifier mask, we can end up with
113    inconsistent sets of flags.  In particular, we might set or clear one of the
114    left/right-specific bits, but not the corresponding non-side-specific bit.
115    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
116    otherwise clear it. */
117 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
119     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
120         *modifiers |= NX_COMMANDMASK;
121     else
122         *modifiers &= ~NX_COMMANDMASK;
123     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
124         *modifiers |= NX_SHIFTMASK;
125     else
126         *modifiers &= ~NX_SHIFTMASK;
127     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
128         *modifiers |= NX_CONTROLMASK;
129     else
130         *modifiers &= ~NX_CONTROLMASK;
131     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
132         *modifiers |= NX_ALTERNATEMASK;
133     else
134         *modifiers &= ~NX_ALTERNATEMASK;
137 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
139     fix_device_modifiers_by_generic(&modifiers);
140     if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
141     {
142         modifiers |= NX_DEVICELCMDKEYMASK;
143         modifiers &= ~NX_DEVICELALTKEYMASK;
144     }
145     if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
146     {
147         modifiers |= NX_DEVICERCMDKEYMASK;
148         modifiers &= ~NX_DEVICERALTKEYMASK;
149     }
150     fix_generic_modifiers_by_device(&modifiers);
152     return modifiers;
156 @interface NSWindow (WineAccessPrivateMethods)
157     - (id) _displayChanged;
158 @end
161 @interface WineDisplayLink : NSObject
163     CGDirectDisplayID _displayID;
164     CVDisplayLinkRef _link;
165     NSMutableSet* _windows;
167     NSTimeInterval _actualRefreshPeriod;
168     NSTimeInterval _nominalRefreshPeriod;
171     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
173     - (void) addWindow:(WineWindow*)window;
174     - (void) removeWindow:(WineWindow*)window;
176     - (NSTimeInterval) refreshPeriod;
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 needsStart;
222         @synchronized(self) {
223             needsStart = !_windows.count;
224             [_windows addObject:window];
225         }
226         if (needsStart)
227             CVDisplayLinkStart(_link);
228     }
230     - (void) removeWindow:(WineWindow*)window
231     {
232         BOOL shouldStop = FALSE;
233         @synchronized(self) {
234             BOOL wasRunning = _windows.count > 0;
235             [_windows removeObject:window];
236             if (wasRunning && !_windows.count)
237                 shouldStop = TRUE;
238         }
239         if (shouldStop)
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             }
259             if (!anyDisplayed)
260                 CVDisplayLinkStop(_link);
261         });
262         [windows release];
263     }
265     - (NSTimeInterval) refreshPeriod
266     {
267         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
268             return _actualRefreshPeriod;
270         if (_nominalRefreshPeriod)
271             return _nominalRefreshPeriod;
273         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
274         if (time.flags & kCVTimeIsIndefinite)
275             return 1.0 / 60.0;
276         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
277         return _nominalRefreshPeriod;
278     }
280     - (void) start
281     {
282         CVDisplayLinkStart(_link);
283     }
285 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
287     WineDisplayLink* link = displayLinkContext;
288     [link fire];
289     return kCVReturnSuccess;
292 @end
295 @interface WineContentView : NSView <NSTextInputClient>
297     NSMutableArray* glContexts;
298     NSMutableArray* pendingGlContexts;
299     BOOL _cachedHasGLDescendant;
300     BOOL _cachedHasGLDescendantValid;
301     BOOL clearedGlSurface;
303     NSMutableAttributedString* markedText;
304     NSRange markedTextSelection;
306     int backingSize[2];
309     - (void) addGLContext:(WineOpenGLContext*)context;
310     - (void) removeGLContext:(WineOpenGLContext*)context;
311     - (void) updateGLContexts;
313     - (void) wine_getBackingSize:(int*)outBackingSize;
314     - (void) wine_setBackingSize:(const int*)newBackingSize;
316 @end
319 @interface WineWindow ()
321 @property (readwrite, nonatomic) BOOL disabled;
322 @property (readwrite, nonatomic) BOOL noActivate;
323 @property (readwrite, nonatomic) BOOL floating;
324 @property (readwrite, nonatomic) BOOL drawnSinceShown;
325 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
326 @property (retain, nonatomic) NSWindow* latentParentWindow;
328 @property (nonatomic) void* hwnd;
329 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
331 @property (nonatomic) void* surface;
332 @property (nonatomic) pthread_mutex_t* surface_mutex;
334 @property (copy, nonatomic) NSBezierPath* shape;
335 @property (copy, nonatomic) NSData* shapeData;
336 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
337 @property (readonly, nonatomic) BOOL needsTransparency;
339 @property (nonatomic) BOOL colorKeyed;
340 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
341 @property (nonatomic) BOOL usePerPixelAlpha;
343 @property (assign, nonatomic) void* imeData;
344 @property (nonatomic) BOOL commandDone;
346 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
348     - (void) updateColorSpace;
349     - (void) updateForGLSubviews;
351     - (BOOL) becameEligibleParentOrChild;
352     - (void) becameIneligibleChild;
354     - (void) windowDidDrawContent;
356 @end
359 @implementation WineContentView
361     - (void) dealloc
362     {
363         [markedText release];
364         [glContexts release];
365         [pendingGlContexts release];
366         [super dealloc];
367     }
369     - (BOOL) isFlipped
370     {
371         return YES;
372     }
374     - (void) drawRect:(NSRect)rect
375     {
376         WineWindow* window = (WineWindow*)[self window];
378         for (WineOpenGLContext* context in pendingGlContexts)
379         {
380             if (!clearedGlSurface)
381             {
382                 context.shouldClearToBlack = TRUE;
383                 clearedGlSurface = TRUE;
384             }
385             context.needsUpdate = TRUE;
386         }
387         [glContexts addObjectsFromArray:pendingGlContexts];
388         [pendingGlContexts removeAllObjects];
390         if ([window contentView] != self)
391             return;
393         if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
394         {
395             [[NSColor clearColor] setFill];
396             NSRectFill(rect);
398             [window.shape addClip];
400             [[NSColor windowBackgroundColor] setFill];
401             NSRectFill(rect);
402         }
404         if (window.surface && window.surface_mutex &&
405             !pthread_mutex_lock(window.surface_mutex))
406         {
407             const CGRect* rects;
408             int count;
410             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
411             {
412                 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
413                 CGContextRef context;
414                 int i;
416                 [window.shape addClip];
418                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
419                 CGContextSetBlendMode(context, kCGBlendModeCopy);
420                 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
422                 for (i = 0; i < count; i++)
423                 {
424                     CGRect imageRect;
425                     CGImageRef image;
427                     imageRect = CGRectIntersection(rects[i], dirtyRect);
428                     image = create_surface_image(window.surface, &imageRect, FALSE);
430                     if (image)
431                     {
432                         if (window.colorKeyed)
433                         {
434                             CGImageRef maskedImage;
435                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
436                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
437                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
438                             maskedImage = CGImageCreateWithMaskingColors(image, components);
439                             if (maskedImage)
440                             {
441                                 CGImageRelease(image);
442                                 image = maskedImage;
443                             }
444                         }
446                         CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
448                         CGImageRelease(image);
449                     }
450                 }
452                 [window windowDidDrawContent];
453             }
455             pthread_mutex_unlock(window.surface_mutex);
456         }
458         // If the window may be transparent, then we have to invalidate the
459         // shadow every time we draw.  Also, if this is the first time we've
460         // drawn since changing from transparent to opaque.
461         if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
462         {
463             window.shapeChangedSinceLastDraw = FALSE;
464             [window invalidateShadow];
465         }
466     }
468     - (void) addGLContext:(WineOpenGLContext*)context
469     {
470         BOOL hadContext = [self hasGLContext];
471         if (!glContexts)
472             glContexts = [[NSMutableArray alloc] init];
473         if (!pendingGlContexts)
474             pendingGlContexts = [[NSMutableArray alloc] init];
476         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
477         {
478             [glContexts addObject:context];
479             if (!clearedGlSurface)
480             {
481                 context.shouldClearToBlack = TRUE;
482                 clearedGlSurface = TRUE;
483             }
484             context.needsUpdate = TRUE;
485         }
486         else
487         {
488             [pendingGlContexts addObject:context];
489             [self setNeedsDisplay:YES];
490         }
492         if (!hadContext)
493             [self invalidateHasGLDescendant];
494         [(WineWindow*)[self window] updateForGLSubviews];
495     }
497     - (void) removeGLContext:(WineOpenGLContext*)context
498     {
499         BOOL hadContext = [self hasGLContext];
500         [glContexts removeObjectIdenticalTo:context];
501         [pendingGlContexts removeObjectIdenticalTo:context];
502         if (hadContext && ![self hasGLContext])
503             [self invalidateHasGLDescendant];
504         [(WineWindow*)[self window] updateForGLSubviews];
505     }
507     - (void) updateGLContexts:(BOOL)reattach
508     {
509         for (WineOpenGLContext* context in glContexts)
510         {
511             context.needsUpdate = TRUE;
512             if (reattach)
513                 context.needsReattach = TRUE;
514         }
515     }
517     - (void) updateGLContexts
518     {
519         [self updateGLContexts:NO];
520     }
522     - (BOOL) hasGLContext
523     {
524         return [glContexts count] || [pendingGlContexts count];
525     }
527     - (BOOL) _hasGLDescendant
528     {
529         if ([self isHidden])
530             return NO;
531         if ([self hasGLContext])
532             return YES;
533         for (WineContentView* view in [self subviews])
534         {
535             if ([view hasGLDescendant])
536                 return YES;
537         }
538         return NO;
539     }
541     - (BOOL) hasGLDescendant
542     {
543         if (!_cachedHasGLDescendantValid)
544         {
545             _cachedHasGLDescendant = [self _hasGLDescendant];
546             _cachedHasGLDescendantValid = YES;
547         }
548         return _cachedHasGLDescendant;
549     }
551     - (void) invalidateHasGLDescendant
552     {
553         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
554         _cachedHasGLDescendantValid = NO;
555         if (invalidateAncestors && self != [[self window] contentView])
556         {
557             WineContentView* superview = (WineContentView*)[self superview];
558             if ([superview isKindOfClass:[WineContentView class]])
559                 [superview invalidateHasGLDescendant];
560         }
561     }
563     - (void) wine_getBackingSize:(int*)outBackingSize
564     {
565         @synchronized(self) {
566             memcpy(outBackingSize, backingSize, sizeof(backingSize));
567         }
568     }
569     - (void) wine_setBackingSize:(const int*)newBackingSize
570     {
571         @synchronized(self) {
572             memcpy(backingSize, newBackingSize, sizeof(backingSize));
573         }
574     }
576     - (void) setRetinaMode:(int)mode
577     {
578         double scale = mode ? 0.5 : 2.0;
579         NSRect frame = self.frame;
580         frame.origin.x *= scale;
581         frame.origin.y *= scale;
582         frame.size.width *= scale;
583         frame.size.height *= scale;
584         [self setFrame:frame];
585         [self updateGLContexts];
587         for (WineContentView* subview in [self subviews])
588         {
589             if ([subview isKindOfClass:[WineContentView class]])
590                 [subview setRetinaMode:mode];
591         }
592     }
594     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
595     {
596         return YES;
597     }
599     - (BOOL) preservesContentDuringLiveResize
600     {
601         // Returning YES from this tells Cocoa to keep our view's content during
602         // a Cocoa-driven resize.  In theory, we're also supposed to override
603         // -setFrameSize: to mark exposed sections as needing redisplay, but
604         // user32 will take care of that in a roundabout way.  This way, we don't
605         // redraw until the window surface is flushed.
606         //
607         // This doesn't do anything when we resize the window ourselves.
608         return YES;
609     }
611     - (BOOL)acceptsFirstResponder
612     {
613         return [[self window] contentView] == self;
614     }
616     - (BOOL) mouseDownCanMoveWindow
617     {
618         return NO;
619     }
621     - (void) viewDidHide
622     {
623         [super viewDidHide];
624         [self invalidateHasGLDescendant];
625     }
627     - (void) viewDidUnhide
628     {
629         [super viewDidUnhide];
630         [self updateGLContexts:YES];
631         [self invalidateHasGLDescendant];
632     }
634     - (void) completeText:(NSString*)text
635     {
636         macdrv_event* event;
637         WineWindow* window = (WineWindow*)[self window];
639         event = macdrv_create_event(IM_SET_TEXT, window);
640         event->im_set_text.data = [window imeData];
641         event->im_set_text.text = (CFStringRef)[text copy];
642         event->im_set_text.complete = TRUE;
644         [[window queue] postEvent:event];
646         macdrv_release_event(event);
648         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
649         markedTextSelection = NSMakeRange(0, 0);
650         [[self inputContext] discardMarkedText];
651     }
653     - (NSFocusRingType) focusRingType
654     {
655         return NSFocusRingTypeNone;
656     }
658     - (void) didAddSubview:(NSView*)subview
659     {
660         if ([subview isKindOfClass:[WineContentView class]])
661         {
662             WineContentView* view = (WineContentView*)subview;
663             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
664                 [self invalidateHasGLDescendant];
665         }
666         [super didAddSubview:subview];
667     }
669     - (void) willRemoveSubview:(NSView*)subview
670     {
671         if ([subview isKindOfClass:[WineContentView class]])
672         {
673             WineContentView* view = (WineContentView*)subview;
674             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
675                 [self invalidateHasGLDescendant];
676         }
677         [super willRemoveSubview:subview];
678     }
680     /*
681      * ---------- NSTextInputClient methods ----------
682      */
683     - (NSTextInputContext*) inputContext
684     {
685         if (!markedText)
686             markedText = [[NSMutableAttributedString alloc] init];
687         return [super inputContext];
688     }
690     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
691     {
692         if ([string isKindOfClass:[NSAttributedString class]])
693             string = [string string];
695         if ([string isKindOfClass:[NSString class]])
696             [self completeText:string];
697     }
699     - (void) doCommandBySelector:(SEL)aSelector
700     {
701         [(WineWindow*)[self window] setCommandDone:TRUE];
702     }
704     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
705     {
706         if ([string isKindOfClass:[NSAttributedString class]])
707             string = [string string];
709         if ([string isKindOfClass:[NSString class]])
710         {
711             macdrv_event* event;
712             WineWindow* window = (WineWindow*)[self window];
714             if (replacementRange.location == NSNotFound)
715                 replacementRange = NSMakeRange(0, [markedText length]);
717             [markedText replaceCharactersInRange:replacementRange withString:string];
718             markedTextSelection = selectedRange;
719             markedTextSelection.location += replacementRange.location;
721             event = macdrv_create_event(IM_SET_TEXT, window);
722             event->im_set_text.data = [window imeData];
723             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
724             event->im_set_text.complete = FALSE;
725             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
727             [[window queue] postEvent:event];
729             macdrv_release_event(event);
731             [[self inputContext] invalidateCharacterCoordinates];
732         }
733     }
735     - (void) unmarkText
736     {
737         [self completeText:nil];
738     }
740     - (NSRange) selectedRange
741     {
742         return markedTextSelection;
743     }
745     - (NSRange) markedRange
746     {
747         NSRange range = NSMakeRange(0, [markedText length]);
748         if (!range.length)
749             range.location = NSNotFound;
750         return range;
751     }
753     - (BOOL) hasMarkedText
754     {
755         return [markedText length] > 0;
756     }
758     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
759     {
760         if (aRange.location >= [markedText length])
761             return nil;
763         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
764         if (actualRange)
765             *actualRange = aRange;
766         return [markedText attributedSubstringFromRange:aRange];
767     }
769     - (NSArray*) validAttributesForMarkedText
770     {
771         return [NSArray array];
772     }
774     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
775     {
776         macdrv_query* query;
777         WineWindow* window = (WineWindow*)[self window];
778         NSRect ret;
780         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
782         query = macdrv_create_query();
783         query->type = QUERY_IME_CHAR_RECT;
784         query->window = (macdrv_window)[window retain];
785         query->ime_char_rect.data = [window imeData];
786         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
788         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
789         {
790             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
791             ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
792             [[WineApplicationController sharedController] flipRect:&ret];
793         }
794         else
795             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
797         macdrv_release_query(query);
799         if (actualRange)
800             *actualRange = aRange;
801         return ret;
802     }
804     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
805     {
806         return NSNotFound;
807     }
809     - (NSInteger) windowLevel
810     {
811         return [[self window] level];
812     }
814 @end
817 @implementation WineWindow
819     static WineWindow* causing_becomeKeyWindow;
821     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
822     @synthesize drawnSinceShown;
823     @synthesize surface, surface_mutex;
824     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
825     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
826     @synthesize usePerPixelAlpha;
827     @synthesize imeData, commandDone;
829     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
830                                  windowFrame:(NSRect)window_frame
831                                         hwnd:(void*)hwnd
832                                        queue:(WineEventQueue*)queue
833     {
834         WineWindow* window;
835         WineContentView* contentView;
836         NSTrackingArea* trackingArea;
837         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
839         [[WineApplicationController sharedController] flipRect:&window_frame];
841         window = [[[self alloc] initWithContentRect:window_frame
842                                           styleMask:style_mask_for_features(wf)
843                                             backing:NSBackingStoreBuffered
844                                               defer:YES] autorelease];
846         if (!window) return nil;
848         /* Standardize windows to eliminate differences between titled and
849            borderless windows and between NSWindow and NSPanel. */
850         [window setHidesOnDeactivate:NO];
851         [window setReleasedWhenClosed:NO];
853         [window setOneShot:YES];
854         [window disableCursorRects];
855         [window setShowsResizeIndicator:NO];
856         [window setHasShadow:wf->shadow];
857         [window setAcceptsMouseMovedEvents:YES];
858         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
859         [window setDelegate:window];
860         [window setBackgroundColor:[NSColor clearColor]];
861         [window setOpaque:NO];
862         window.hwnd = hwnd;
863         window.queue = queue;
864         window->savedContentMinSize = NSZeroSize;
865         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
866         window->resizable = wf->resizable;
867         window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
869         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
870                                                                   (NSString*)kUTTypeContent,
871                                                                   nil]];
873         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
874         if (!contentView)
875             return nil;
876         [contentView setAutoresizesSubviews:NO];
878         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
879            because they give us mouse moves in the background. */
880         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
881                                                      options:(NSTrackingMouseMoved |
882                                                               NSTrackingActiveAlways |
883                                                               NSTrackingInVisibleRect)
884                                                        owner:window
885                                                     userInfo:nil] autorelease];
886         if (!trackingArea)
887             return nil;
888         [contentView addTrackingArea:trackingArea];
890         [window setContentView:contentView];
891         [window setInitialFirstResponder:contentView];
893         [nc addObserver:window
894                selector:@selector(updateFullscreen)
895                    name:NSApplicationDidChangeScreenParametersNotification
896                  object:NSApp];
897         [window updateFullscreen];
899         [nc addObserver:window
900                selector:@selector(applicationWillHide)
901                    name:NSApplicationWillHideNotification
902                  object:NSApp];
903         [nc addObserver:window
904                selector:@selector(applicationDidUnhide)
905                    name:NSApplicationDidUnhideNotification
906                  object:NSApp];
908         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
909                                                               selector:@selector(checkWineDisplayLink)
910                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
911                                                                 object:[NSWorkspace sharedWorkspace]];
913         [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
915         return window;
916     }
918     - (void) dealloc
919     {
920         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
921         [[NSNotificationCenter defaultCenter] removeObserver:self];
922         [queue release];
923         [latentChildWindows release];
924         [latentParentWindow release];
925         [shape release];
926         [shapeData release];
927         [super dealloc];
928     }
930     - (BOOL) preventResizing
931     {
932         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
933         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
934     }
936     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
937     {
938         if (allow_immovable_windows && (disabled || inMaximized))
939             return NO;
940         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
941             return NO;
942         else
943             return YES;
944     }
946     - (void) adjustFeaturesForState
947     {
948         NSUInteger style = [self styleMask];
950         if (style & NSClosableWindowMask)
951             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
952         if (style & NSMiniaturizableWindowMask)
953             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
954         if (style & NSResizableWindowMask)
955             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
956         if ([self respondsToSelector:@selector(toggleFullScreen:)])
957         {
958             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
959                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
960         }
962         if ([self preventResizing])
963         {
964             NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
965             [self setContentMinSize:size];
966             [self setContentMaxSize:size];
967         }
968         else
969         {
970             [self setContentMaxSize:savedContentMaxSize];
971             [self setContentMinSize:savedContentMinSize];
972         }
974         if (allow_immovable_windows || cursor_clipping_locks_windows)
975             [self setMovable:[self allowsMovingWithMaximized:maximized]];
976     }
978     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
979     {
980         if ([self respondsToSelector:@selector(toggleFullScreen:)])
981         {
982             NSUInteger style = [self styleMask];
984             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
985                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
986                 !(self.parentWindow || self.latentParentWindow))
987             {
988                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
989                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
990             }
991             else
992             {
993                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
994                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
995                 if (style & NSFullScreenWindowMask)
996                     [super toggleFullScreen:nil];
997             }
998         }
1000         if (behavior != [self collectionBehavior])
1001         {
1002             [self setCollectionBehavior:behavior];
1003             [self adjustFeaturesForState];
1004         }
1005     }
1007     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1008     {
1009         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1010                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1011         NSUInteger currentStyle = [self styleMask];
1012         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1014         if (newStyle != currentStyle)
1015         {
1016             NSString* title = [[[self title] copy] autorelease];
1017             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1018             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1019             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1020             {
1021                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1022                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1023                 // just NSTitledWindowMask, the window buttons should disappear rather
1024                 // than just being disabled.  But they don't.  Similarly in reverse.
1025                 // The workaround is to also toggle NSClosableWindowMask at the same time.
1026                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1027             }
1028             [self setStyleMask:newStyle];
1030             // -setStyleMask: resets the firstResponder to the window.  Set it
1031             // back to the content view.
1032             if ([[self contentView] acceptsFirstResponder])
1033                 [self makeFirstResponder:[self contentView]];
1035             [self adjustFullScreenBehavior:[self collectionBehavior]];
1037             if ([[self title] length] == 0 && [title length] > 0)
1038                 [self setTitle:title];
1039         }
1041         resizable = wf->resizable;
1042         [self adjustFeaturesForState];
1043         [self setHasShadow:wf->shadow];
1044     }
1046     // Indicates if the window would be visible if the app were not hidden.
1047     - (BOOL) wouldBeVisible
1048     {
1049         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1050     }
1052     - (BOOL) isOrderedIn
1053     {
1054         return [self wouldBeVisible] || [self isMiniaturized];
1055     }
1057     - (NSInteger) minimumLevelForActive:(BOOL)active
1058     {
1059         NSInteger level;
1061         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1062                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1063             level = NSFloatingWindowLevel;
1064         else
1065             level = NSNormalWindowLevel;
1067         if (active)
1068         {
1069             BOOL captured;
1071             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1073             if (captured || fullscreen)
1074             {
1075                 if (captured)
1076                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1077                 else
1078                     level = NSStatusWindowLevel + 1;
1080                 if (self.floating)
1081                     level++;
1082             }
1083         }
1085         return level;
1086     }
1088     - (void) postDidUnminimizeEvent
1089     {
1090         macdrv_event* event;
1092         /* Coalesce events by discarding any previous ones still in the queue. */
1093         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1094                                forWindow:self];
1096         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1097         [queue postEvent:event];
1098         macdrv_release_event(event);
1099     }
1101     - (void) sendResizeStartQuery
1102     {
1103         macdrv_query* query = macdrv_create_query();
1104         query->type = QUERY_RESIZE_START;
1105         query->window = (macdrv_window)[self retain];
1107         [self.queue query:query timeout:0.3];
1108         macdrv_release_query(query);
1109     }
1111     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1112     {
1113         NSWindowCollectionBehavior behavior;
1115         self.disabled = state->disabled;
1116         self.noActivate = state->no_activate;
1118         if (self.floating != state->floating)
1119         {
1120             self.floating = state->floating;
1121             if (state->floating)
1122             {
1123                 // Became floating.  If child of non-floating window, make that
1124                 // relationship latent.
1125                 WineWindow* parent = (WineWindow*)[self parentWindow];
1126                 if (parent && !parent.floating)
1127                     [self becameIneligibleChild];
1128             }
1129             else
1130             {
1131                 // Became non-floating.  If parent of floating children, make that
1132                 // relationship latent.
1133                 WineWindow* child;
1134                 for (child in [self childWineWindows])
1135                 {
1136                     if (child.floating)
1137                         [child becameIneligibleChild];
1138                 }
1139             }
1141             // Check our latent relationships.  If floating status was the only
1142             // reason they were latent, then make them active.
1143             if ([self isVisible])
1144                 [self becameEligibleParentOrChild];
1146             [[WineApplicationController sharedController] adjustWindowLevels];
1147         }
1149         if (state->minimized_valid)
1150         {
1151             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1153             pendingMinimize = FALSE;
1154             if (state->minimized && ![self isMiniaturized])
1155             {
1156                 if ([self wouldBeVisible])
1157                 {
1158                     if ([self styleMask] & NSFullScreenWindowMask)
1159                     {
1160                         [self postDidUnminimizeEvent];
1161                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1162                     }
1163                     else
1164                     {
1165                         [super miniaturize:nil];
1166                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1167                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1168                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1169                     }
1170                 }
1171                 else
1172                     pendingMinimize = TRUE;
1173             }
1174             else if (!state->minimized && [self isMiniaturized])
1175             {
1176                 ignore_windowDeminiaturize = TRUE;
1177                 [self deminiaturize:nil];
1178                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1179             }
1181             if (discard)
1182                 [queue discardEventsMatchingMask:discard forWindow:self];
1183         }
1185         if (state->maximized != maximized)
1186         {
1187             maximized = state->maximized;
1188             [self adjustFeaturesForState];
1190             if (!maximized && [self inLiveResize])
1191                 [self sendResizeStartQuery];
1192         }
1194         behavior = NSWindowCollectionBehaviorDefault;
1195         if (state->excluded_by_expose)
1196             behavior |= NSWindowCollectionBehaviorTransient;
1197         else
1198             behavior |= NSWindowCollectionBehaviorManaged;
1199         if (state->excluded_by_cycle)
1200         {
1201             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1202             if ([self isOrderedIn])
1203                 [NSApp removeWindowsItem:self];
1204         }
1205         else
1206         {
1207             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1208             if ([self isOrderedIn])
1209                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1210         }
1211         [self adjustFullScreenBehavior:behavior];
1212     }
1214     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1215     {
1216         BOOL reordered = FALSE;
1218         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1219         {
1220             if ([self level] > [child level])
1221                 [child setLevel:[self level]];
1222             if (![child isVisible])
1223                 [child setAutodisplay:YES];
1224             [self addChildWindow:child ordered:NSWindowAbove];
1225             [child checkWineDisplayLink];
1226             [latentChildWindows removeObjectIdenticalTo:child];
1227             child.latentParentWindow = nil;
1228             reordered = TRUE;
1229         }
1230         else
1231         {
1232             if (!latentChildWindows)
1233                 latentChildWindows = [[NSMutableArray alloc] init];
1234             if (![latentChildWindows containsObject:child])
1235                 [latentChildWindows addObject:child];
1236             child.latentParentWindow = self;
1237         }
1239         return reordered;
1240     }
1242     - (BOOL) addChildWineWindow:(WineWindow*)child
1243     {
1244         return [self addChildWineWindow:child assumeVisible:FALSE];
1245     }
1247     - (void) removeChildWineWindow:(WineWindow*)child
1248     {
1249         [self removeChildWindow:child];
1250         if (child.latentParentWindow == self)
1251             child.latentParentWindow = nil;
1252         [latentChildWindows removeObjectIdenticalTo:child];
1253     }
1255     - (void) setChildWineWindows:(NSArray*)childWindows
1256     {
1257         NSArray* origChildren;
1258         NSUInteger count, start, limit, i;
1260         origChildren = self.childWineWindows;
1262         // If the current and desired children arrays match up to a point, leave
1263         // those matching children alone.
1264         count = childWindows.count;
1265         limit = MIN(origChildren.count, count);
1266         for (start = 0; start < limit; start++)
1267         {
1268             if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1269                 break;
1270         }
1272         // Remove all of the child windows and re-add them back-to-front so they
1273         // are in the desired order.
1274         for (i = start; i < count; i++)
1275         {
1276             WineWindow* child = [childWindows objectAtIndex:i];
1277             [self removeChildWindow:child];
1278         }
1279         for (i = start; i < count; i++)
1280         {
1281             WineWindow* child = [childWindows objectAtIndex:i];
1282             [self addChildWindow:child ordered:NSWindowAbove];
1283         }
1284     }
1286     static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1287     {
1288         NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1289         NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1290         NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1291         NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1292         if (index1 == NSNotFound)
1293         {
1294             if (index2 == NSNotFound)
1295                 return NSOrderedSame;
1296             else
1297                 return NSOrderedAscending;
1298         }
1299         else if (index2 == NSNotFound)
1300             return NSOrderedDescending;
1301         else if (index1 < index2)
1302             return NSOrderedDescending;
1303         else if (index2 < index1)
1304             return NSOrderedAscending;
1306         return NSOrderedSame;
1307     }
1309     - (BOOL) becameEligibleParentOrChild
1310     {
1311         BOOL reordered = FALSE;
1312         NSUInteger count;
1314         if (latentParentWindow.floating || !self.floating)
1315         {
1316             // If we aren't visible currently, we assume that we should be and soon
1317             // will be.  So, if the latent parent is visible that's enough to assume
1318             // we can establish the parent-child relationship in Cocoa.  That will
1319             // actually make us visible, which is fine.
1320             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1321                 reordered = TRUE;
1322         }
1324         // Here, though, we may not actually be visible yet and adding a child
1325         // won't make us visible.  The caller will have to call this method
1326         // again after actually making us visible.
1327         if ([self isVisible] && (count = [latentChildWindows count]))
1328         {
1329             NSMutableArray* windowNumbers;
1330             NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1331             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1332             NSUInteger i;
1334             windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1336             for (i = 0; i < count; i++)
1337             {
1338                 WineWindow* child = [latentChildWindows objectAtIndex:i];
1339                 if ([child isVisible] && (self.floating || !child.floating))
1340                 {
1341                     if (child.latentParentWindow == self)
1342                     {
1343                         if ([self level] > [child level])
1344                             [child setLevel:[self level]];
1345                         [childWindows addObject:child];
1346                         child.latentParentWindow = nil;
1347                         reordered = TRUE;
1348                     }
1349                     else
1350                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1351                     [indexesToRemove addIndex:i];
1352                 }
1353             }
1355             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1357             [childWindows sortWithOptions:NSSortStable
1358                           usingComparator:^NSComparisonResult(id obj1, id obj2){
1359                 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1360             }];
1361             [self setChildWineWindows:childWindows];
1362         }
1364         return reordered;
1365     }
1367     - (void) becameIneligibleChild
1368     {
1369         WineWindow* parent = (WineWindow*)[self parentWindow];
1370         if (parent)
1371         {
1372             if (!parent->latentChildWindows)
1373                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1374             [parent->latentChildWindows insertObject:self atIndex:0];
1375             self.latentParentWindow = parent;
1376             [parent removeChildWindow:self];
1377         }
1378     }
1380     - (void) becameIneligibleParentOrChild
1381     {
1382         NSArray* childWindows = [self childWineWindows];
1384         [self becameIneligibleChild];
1386         if ([childWindows count])
1387         {
1388             WineWindow* child;
1390             for (child in childWindows)
1391             {
1392                 child.latentParentWindow = self;
1393                 [self removeChildWindow:child];
1394             }
1396             if (latentChildWindows)
1397                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1398             else
1399                 latentChildWindows = [childWindows mutableCopy];
1400         }
1401     }
1403     // Determine if, among Wine windows, this window is directly above or below
1404     // a given other Wine window with no other Wine window intervening.
1405     // Intervening non-Wine windows are ignored.
1406     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1407     {
1408         NSNumber* windowNumber;
1409         NSNumber* otherWindowNumber;
1410         NSArray* windowNumbers;
1411         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1413         if (![self isVisible] || ![otherWindow isVisible])
1414             return FALSE;
1416         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1417         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1418         windowNumbers = [[self class] windowNumbersWithOptions:0];
1419         windowIndex = [windowNumbers indexOfObject:windowNumber];
1420         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1422         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1423             return FALSE;
1425         if (orderingMode == NSWindowAbove)
1426         {
1427             lowIndex = windowIndex;
1428             highIndex = otherWindowIndex;
1429         }
1430         else if (orderingMode == NSWindowBelow)
1431         {
1432             lowIndex = otherWindowIndex;
1433             highIndex = windowIndex;
1434         }
1435         else
1436             return FALSE;
1438         if (highIndex <= lowIndex)
1439             return FALSE;
1441         for (i = lowIndex + 1; i < highIndex; i++)
1442         {
1443             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1444             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1445             if ([interveningWindow isKindOfClass:[WineWindow class]])
1446                 return FALSE;
1447         }
1449         return TRUE;
1450     }
1452     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1453     {
1454         NSMutableArray* windowNumbers;
1455         NSNumber* childWindowNumber;
1456         NSUInteger otherIndex;
1457         NSArray* origChildren;
1458         NSMutableArray* children;
1460         // Get the z-order from the window server and modify it to reflect the
1461         // requested window ordering.
1462         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1463         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1464         [windowNumbers removeObject:childWindowNumber];
1465         if (other)
1466         {
1467             otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1468             [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1469         }
1470         else if (mode == NSWindowAbove)
1471             [windowNumbers insertObject:childWindowNumber atIndex:0];
1472         else
1473             [windowNumbers addObject:childWindowNumber];
1475         // Get our child windows and sort them in the reverse of the desired
1476         // z-order (back-to-front).
1477         origChildren = [self childWineWindows];
1478         children = [[origChildren mutableCopy] autorelease];
1479         [children sortWithOptions:NSSortStable
1480                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1481             return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1482         }];
1484         [self setChildWineWindows:children];
1485     }
1487     // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1488     // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1489     //      (non-nil, non-nil)  there is a level in the window tree where the two windows have sibling ancestors
1490     //                          if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1491     //                          otherwise, the two ancestors are each roots of disjoint window trees
1492     //      (nil, non-nil)      the other window is a descendent of self and *ancestorOfOther is the direct child
1493     //      (non-nil, nil)      self is a descendent of other and *ancestor is the direct child
1494     - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1495     {
1496         NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1497         WineWindow* child;
1498         WineWindow* parent;
1499         for (child = other;
1500              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1501              child = parent)
1502         {
1503             if (parent == self)
1504             {
1505                 *ancestor = nil;
1506                 *ancestorOfOther = child;
1507                 return;
1508             }
1510             [otherAncestors addObject:parent];
1511         }
1513         for (child = self;
1514              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1515              child = parent)
1516         {
1517             NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1518             if (index != NSNotFound)
1519             {
1520                 *ancestor = child;
1521                 if (index == 0)
1522                     *ancestorOfOther = nil;
1523                 else
1524                     *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1525                 return;
1526             }
1527         }
1529         *ancestor = child;
1530         *ancestorOfOther = otherAncestors.lastObject;;
1531     }
1533     /* Returns whether or not the window was ordered in, which depends on if
1534        its frame intersects any screen. */
1535     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1536     {
1537         WineApplicationController* controller = [WineApplicationController sharedController];
1538         if (![self isMiniaturized])
1539         {
1540             BOOL needAdjustWindowLevels = FALSE;
1541             BOOL wasVisible;
1542             WineWindow* parent;
1543             WineWindow* child;
1545             [controller transformProcessToForeground];
1546             if ([NSApp isHidden])
1547                 [NSApp unhide:nil];
1548             wasVisible = [self isVisible];
1550             if (activate)
1551                 [NSApp activateIgnoringOtherApps:YES];
1553             NSDisableScreenUpdates();
1555             if ([self becameEligibleParentOrChild])
1556                 needAdjustWindowLevels = TRUE;
1558             if (prev || next)
1559             {
1560                 WineWindow* other = [prev isVisible] ? prev : next;
1561                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1563                 if (![self isOrdered:orderingMode relativeTo:other])
1564                 {
1565                     WineWindow* ancestor;
1566                     WineWindow* ancestorOfOther;
1568                     [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1569                     if (ancestor)
1570                     {
1571                         [self setAutodisplay:YES];
1572                         if (ancestorOfOther)
1573                         {
1574                             // This window level may not be right for this window based
1575                             // on floating-ness, fullscreen-ness, etc.  But we set it
1576                             // temporarily to allow us to order the windows properly.
1577                             // Then the levels get fixed by -adjustWindowLevels.
1578                             if ([ancestor level] != [ancestorOfOther level])
1579                                 [ancestor setLevel:[ancestorOfOther level]];
1581                             parent = (WineWindow*)ancestor.parentWindow;
1582                             if ([parent isKindOfClass:[WineWindow class]])
1583                                 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1584                             else
1585                                 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1586                         }
1588                         for (child = self;
1589                              (parent = (WineWindow*)child.parentWindow);
1590                              child = parent)
1591                         {
1592                             if ([parent isKindOfClass:[WineWindow class]])
1593                                 [parent order:-orderingMode childWindow:child relativeTo:nil];
1594                             if (parent == ancestor)
1595                                 break;
1596                         }
1598                         [self checkWineDisplayLink];
1599                         needAdjustWindowLevels = TRUE;
1600                     }
1601                 }
1602             }
1603             else
1604             {
1605                 for (child = self;
1606                      (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1607                      child = parent)
1608                 {
1609                     [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1610                 }
1612                 // Again, temporarily set level to make sure we can order to
1613                 // the right place.
1614                 next = [controller frontWineWindow];
1615                 if (next && [self level] < [next level])
1616                     [self setLevel:[next level]];
1617                 [self setAutodisplay:YES];
1618                 [self orderFront:nil];
1619                 [self checkWineDisplayLink];
1620                 needAdjustWindowLevels = TRUE;
1621             }
1622             pendingOrderOut = FALSE;
1624             if ([self becameEligibleParentOrChild])
1625                 needAdjustWindowLevels = TRUE;
1627             if (needAdjustWindowLevels)
1628             {
1629                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1630                     [controller updateFullscreenWindows];
1631                 [controller adjustWindowLevels];
1632             }
1634             if (pendingMinimize)
1635             {
1636                 [super miniaturize:nil];
1637                 pendingMinimize = FALSE;
1638             }
1640             NSEnableScreenUpdates();
1642             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1643                Generate a frame-changed event just in case.  The back end will ignore
1644                it if nothing actually changed. */
1645             [self windowDidResize:nil];
1647             if (![self isExcludedFromWindowsMenu])
1648                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1649         }
1650     }
1652     - (void) doOrderOut
1653     {
1654         WineApplicationController* controller = [WineApplicationController sharedController];
1655         BOOL wasVisible = [self isVisible];
1656         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1658         if (enteringFullScreen || exitingFullScreen)
1659         {
1660             pendingOrderOut = TRUE;
1661             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1662                                              event_mask_for_type(WINDOW_GOT_FOCUS) |
1663                                              event_mask_for_type(WINDOW_LOST_FOCUS) |
1664                                              event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1665                                              event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1666                                              event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1667                                    forWindow:self];
1668             return;
1669         }
1671         pendingOrderOut = FALSE;
1673         if ([self isMiniaturized])
1674             pendingMinimize = TRUE;
1676         WineWindow* parent = (WineWindow*)self.parentWindow;
1677         if ([parent isKindOfClass:[WineWindow class]])
1678             [parent grabDockIconSnapshotFromWindow:self force:NO];
1680         [self becameIneligibleParentOrChild];
1681         if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1682         {
1683             fakingClose = TRUE;
1684             [self close];
1685             fakingClose = FALSE;
1686         }
1687         else
1688             [self orderOut:nil];
1689         [self checkWineDisplayLink];
1690         [self setBackgroundColor:[NSColor clearColor]];
1691         [self setOpaque:NO];
1692         drawnSinceShown = NO;
1693         savedVisibleState = FALSE;
1694         if (wasVisible && wasOnActiveSpace && fullscreen)
1695             [controller updateFullscreenWindows];
1696         [controller adjustWindowLevels];
1697         [NSApp removeWindowsItem:self];
1699         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1700                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1701                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1702                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1703                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1704                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1705                                forWindow:self];
1706     }
1708     - (void) updateFullscreen
1709     {
1710         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1711         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1713         if (nowFullscreen != fullscreen)
1714         {
1715             WineApplicationController* controller = [WineApplicationController sharedController];
1717             fullscreen = nowFullscreen;
1718             if ([self isVisible] && [self isOnActiveSpace])
1719                 [controller updateFullscreenWindows];
1721             [controller adjustWindowLevels];
1722         }
1723     }
1725     - (void) setFrameAndWineFrame:(NSRect)frame
1726     {
1727         [self setFrame:frame display:YES];
1729         wineFrame = frame;
1730         roundedWineFrame = self.frame;
1731         CGFloat junk;
1732 #if CGFLOAT_IS_DOUBLE
1733         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1734              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1735             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1736             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1737             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1738             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1739             roundedWineFrame = wineFrame;
1740 #else
1741         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1742              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1743             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1744             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1745             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1746             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1747             roundedWineFrame = wineFrame;
1748 #endif
1749     }
1751     - (void) setFrameFromWine:(NSRect)contentRect
1752     {
1753         /* Origin is (left, top) in a top-down space.  Need to convert it to
1754            (left, bottom) in a bottom-up space. */
1755         [[WineApplicationController sharedController] flipRect:&contentRect];
1757         /* The back end is establishing a new window size and position.  It's
1758            not interested in any stale events regarding those that may be sitting
1759            in the queue. */
1760         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1761                                forWindow:self];
1763         if (!NSIsEmptyRect(contentRect))
1764         {
1765             NSRect frame, oldFrame;
1767             oldFrame = self.wine_fractionalFrame;
1768             frame = [self frameRectForContentRect:contentRect];
1769             if (!NSEqualRects(frame, oldFrame))
1770             {
1771                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1772                 BOOL needEnableScreenUpdates = FALSE;
1774                 if ([self preventResizing])
1775                 {
1776                     // Allow the following calls to -setFrame:display: to work even
1777                     // if they would violate the content size constraints. This
1778                     // shouldn't be necessary since the content size constraints are
1779                     // documented to not constrain that method, but it seems to be.
1780                     [self setContentMinSize:NSZeroSize];
1781                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1782                 }
1784                 if (equalSizes && [[self childWineWindows] count])
1785                 {
1786                     // If we change the window frame such that the origin moves
1787                     // but the size doesn't change, then Cocoa moves child
1788                     // windows with the parent.  We don't want that so we fake
1789                     // a change of the size and then change it back.
1790                     NSRect bogusFrame = frame;
1791                     bogusFrame.size.width++;
1793                     NSDisableScreenUpdates();
1794                     needEnableScreenUpdates = TRUE;
1796                     ignore_windowResize = TRUE;
1797                     [self setFrame:bogusFrame display:NO];
1798                     ignore_windowResize = FALSE;
1799                 }
1801                 [self setFrameAndWineFrame:frame];
1802                 if ([self preventResizing])
1803                 {
1804                     [self setContentMinSize:contentRect.size];
1805                     [self setContentMaxSize:contentRect.size];
1806                 }
1808                 if (needEnableScreenUpdates)
1809                     NSEnableScreenUpdates();
1811                 if (!equalSizes)
1812                     [self updateColorSpace];
1814                 if (!enteringFullScreen &&
1815                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1816                     nonFullscreenFrame = frame;
1818                 [self updateFullscreen];
1820                 if ([self isOrderedIn])
1821                 {
1822                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1823                        event.  The back end will ignore it if nothing actually changed. */
1824                     [self windowDidResize:nil];
1825                 }
1826             }
1827         }
1828     }
1830     - (NSRect) wine_fractionalFrame
1831     {
1832         NSRect frame = self.frame;
1833         if (NSEqualRects(frame, roundedWineFrame))
1834             frame = wineFrame;
1835         return frame;
1836     }
1838     - (void) setMacDrvParentWindow:(WineWindow*)parent
1839     {
1840         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1841         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1842         {
1843             [oldParent removeChildWineWindow:self];
1844             [latentParentWindow removeChildWineWindow:self];
1845             if ([parent addChildWineWindow:self])
1846                 [[WineApplicationController sharedController] adjustWindowLevels];
1847             [self adjustFullScreenBehavior:[self collectionBehavior]];
1848         }
1849     }
1851     - (void) setDisabled:(BOOL)newValue
1852     {
1853         if (disabled != newValue)
1854         {
1855             disabled = newValue;
1856             [self adjustFeaturesForState];
1857         }
1858     }
1860     - (BOOL) needsTransparency
1861     {
1862         return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1863                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1864     }
1866     - (void) checkTransparency
1867     {
1868         if (![self isOpaque] && !self.needsTransparency)
1869         {
1870             self.shapeChangedSinceLastDraw = TRUE;
1871             [[self contentView] setNeedsDisplay:YES];
1872             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1873             [self setOpaque:YES];
1874         }
1875         else if ([self isOpaque] && self.needsTransparency)
1876         {
1877             self.shapeChangedSinceLastDraw = TRUE;
1878             [[self contentView] setNeedsDisplay:YES];
1879             [self setBackgroundColor:[NSColor clearColor]];
1880             [self setOpaque:NO];
1881         }
1882     }
1884     - (void) setShape:(NSBezierPath*)newShape
1885     {
1886         if (shape == newShape) return;
1888         if (shape)
1889         {
1890             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1891             [shape release];
1892         }
1893         if (newShape)
1894             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1896         shape = [newShape copy];
1897         self.shapeChangedSinceLastDraw = TRUE;
1899         [self checkTransparency];
1900     }
1902     - (void) makeFocused:(BOOL)activate
1903     {
1904         if (activate)
1905         {
1906             [[WineApplicationController sharedController] transformProcessToForeground];
1907             [NSApp activateIgnoringOtherApps:YES];
1908         }
1910         causing_becomeKeyWindow = self;
1911         [self makeKeyWindow];
1912         causing_becomeKeyWindow = nil;
1914         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1915                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1916                                forWindow:self];
1917     }
1919     - (void) postKey:(uint16_t)keyCode
1920              pressed:(BOOL)pressed
1921            modifiers:(NSUInteger)modifiers
1922                event:(NSEvent*)theEvent
1923     {
1924         macdrv_event* event;
1925         CGEventRef cgevent;
1926         WineApplicationController* controller = [WineApplicationController sharedController];
1928         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1929         event->key.keycode   = keyCode;
1930         event->key.modifiers = modifiers;
1931         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1933         if ((cgevent = [theEvent CGEvent]))
1934         {
1935             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1936                                                         kCGKeyboardEventKeyboardType);
1937             if (keyboardType != controller.keyboardType)
1938             {
1939                 controller.keyboardType = keyboardType;
1940                 [controller keyboardSelectionDidChange];
1941             }
1942         }
1944         [queue postEvent:event];
1946         macdrv_release_event(event);
1948         [controller noteKey:keyCode pressed:pressed];
1949     }
1951     - (void) postKeyEvent:(NSEvent *)theEvent
1952     {
1953         [self flagsChanged:theEvent];
1954         [self postKey:[theEvent keyCode]
1955               pressed:[theEvent type] == NSKeyDown
1956             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1957                 event:theEvent];
1958     }
1960     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1961     {
1962         savedContentMinSize = minSize;
1963         savedContentMaxSize = maxSize;
1964         if (![self preventResizing])
1965         {
1966             [self setContentMinSize:minSize];
1967             [self setContentMaxSize:maxSize];
1968         }
1969     }
1971     - (WineWindow*) ancestorWineWindow
1972     {
1973         WineWindow* ancestor = self;
1974         for (;;)
1975         {
1976             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1977             if ([parent isKindOfClass:[WineWindow class]])
1978                 ancestor = parent;
1979             else
1980                 break;
1981         }
1982         return ancestor;
1983     }
1985     - (void) postBroughtForwardEvent
1986     {
1987         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1988         [queue postEvent:event];
1989         macdrv_release_event(event);
1990     }
1992     - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
1993     {
1994         macdrv_event* event;
1995         NSUInteger style = self.styleMask;
1997         if (isFullscreen)
1998             style |= NSFullScreenWindowMask;
1999         else
2000             style &= ~NSFullScreenWindowMask;
2001         frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2002         [[WineApplicationController sharedController] flipRect:&frame];
2004         /* Coalesce events by discarding any previous ones still in the queue. */
2005         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2006                                forWindow:self];
2008         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2009         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2010         event->window_frame_changed.fullscreen = isFullscreen;
2011         event->window_frame_changed.in_resize = resizing;
2012         [queue postEvent:event];
2013         macdrv_release_event(event);
2014     }
2016     - (void) updateForCursorClipping
2017     {
2018         [self adjustFeaturesForState];
2019     }
2021     - (void) endWindowDragging
2022     {
2023         if (draggingPhase)
2024         {
2025             if (draggingPhase == 3)
2026             {
2027                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2028                 [queue postEvent:event];
2029                 macdrv_release_event(event);
2030             }
2032             draggingPhase = 0;
2033             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2034         }
2035     }
2037     - (NSMutableDictionary*) displayIDToDisplayLinkMap
2038     {
2039         static NSMutableDictionary* displayIDToDisplayLinkMap;
2040         if (!displayIDToDisplayLinkMap)
2041         {
2042             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2044             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2045                                                               object:NSApp
2046                                                                queue:nil
2047                                                           usingBlock:^(NSNotification *note){
2048                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2049                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2050                 [badDisplayIDs minusSet:validDisplayIDs];
2051                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2052             }];
2053         }
2054         return displayIDToDisplayLinkMap;
2055     }
2057     - (WineDisplayLink*) wineDisplayLink
2058     {
2059         if (!_lastDisplayID)
2060             return nil;
2062         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2063         return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2064     }
2066     - (void) checkWineDisplayLink
2067     {
2068         NSScreen* screen = self.screen;
2069         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2070             screen = nil;
2071 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2072         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2073             screen = nil;
2074 #endif
2076         NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2077         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2078         if (displayID == _lastDisplayID)
2079             return;
2081         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2083         if (_lastDisplayID)
2084         {
2085             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2086             [link removeWindow:self];
2087         }
2088         if (displayID)
2089         {
2090             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2091             if (!link)
2092             {
2093                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2094                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2095             }
2096             [link addWindow:self];
2097             [self displayIfNeeded];
2098         }
2099         _lastDisplayID = displayID;
2100     }
2102     - (BOOL) isEmptyShaped
2103     {
2104         return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2105     }
2107     - (BOOL) canProvideSnapshot
2108     {
2109         return (self.windowNumber > 0 && ![self isEmptyShaped]);
2110     }
2112     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2113     {
2114         if (![self isEmptyShaped])
2115             return;
2117         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2118         if (!force && now < lastDockIconSnapshot + 1)
2119             return;
2121         if (window)
2122         {
2123             if (![window canProvideSnapshot])
2124                 return;
2125         }
2126         else
2127         {
2128             CGFloat bestArea;
2129             for (WineWindow* childWindow in self.childWindows)
2130             {
2131                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2132                     continue;
2134                 NSSize size = childWindow.frame.size;
2135                 CGFloat area = size.width * size.height;
2136                 if (!window || area > bestArea)
2137                 {
2138                     window = childWindow;
2139                     bestArea = area;
2140                 }
2141             }
2143             if (!window)
2144                 return;
2145         }
2147         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2148         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2149         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2150         CFRelease(windowIDs);
2151         if (!windowImage)
2152             return;
2154         NSImage* appImage = [NSApp applicationIconImage];
2155         if (!appImage)
2156             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2158         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2159         [dockIcon lockFocus];
2161         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2163         CGRect rect = CGRectMake(8, 8, 240, 240);
2164         size_t width = CGImageGetWidth(windowImage);
2165         size_t height = CGImageGetHeight(windowImage);
2166         if (width > height)
2167         {
2168             rect.size.height *= height / (double)width;
2169             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2170         }
2171         else if (width != height)
2172         {
2173             rect.size.width *= width / (double)height;
2174             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2175         }
2177         CGContextDrawImage(cgcontext, rect, windowImage);
2178         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2179                     fromRect:NSZeroRect
2180                    operation:NSCompositeSourceOver
2181                     fraction:1
2182               respectFlipped:YES
2183                        hints:nil];
2185         [dockIcon unlockFocus];
2187         CGImageRelease(windowImage);
2189         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2190         if (![imageView isKindOfClass:[NSImageView class]])
2191         {
2192             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2193             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2194             self.dockTile.contentView = imageView;
2195         }
2196         imageView.image = dockIcon;
2197         [self.dockTile display];
2198         lastDockIconSnapshot = now;
2199     }
2201     - (void) checkEmptyShaped
2202     {
2203         if (self.dockTile.contentView && ![self isEmptyShaped])
2204         {
2205             self.dockTile.contentView = nil;
2206             lastDockIconSnapshot = 0;
2207         }
2208         [self checkWineDisplayLink];
2209     }
2212     /*
2213      * ---------- NSWindow method overrides ----------
2214      */
2215     - (BOOL) canBecomeKeyWindow
2216     {
2217         if (causing_becomeKeyWindow == self) return YES;
2218         if (self.disabled || self.noActivate) return NO;
2219         return [self isKeyWindow];
2220     }
2222     - (BOOL) canBecomeMainWindow
2223     {
2224         return [self canBecomeKeyWindow];
2225     }
2227     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2228     {
2229         // If a window is sized to completely cover a screen, then it's in
2230         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2231         // it.
2232         NSArray* screens = [NSScreen screens];
2233         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2234         if (!screen_covered_by_rect(contentRect, screens) &&
2235             frame_intersects_screens(frameRect, screens))
2236             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2237         return frameRect;
2238     }
2240     // This private method of NSWindow is called as Cocoa reacts to the display
2241     // configuration changing.  Among other things, it adjusts the window's
2242     // frame based on how the screen(s) changed size.  That tells Wine that the
2243     // window has been moved.  We don't want that.  Rather, we want to make
2244     // sure that the WinAPI notion of the window position is maintained/
2245     // restored, possibly undoing or overriding Cocoa's adjustment.
2246     //
2247     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2248     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2249     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2250     // reassert its notion of the position.  That call won't get processed
2251     // until after this method returns, so it will override whatever this
2252     // method does to the window position.  It will also discard any pending
2253     // WINDOW_FRAME_CHANGED events.
2254     //
2255     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2256     // adjust the window's position due to a display change is to hook into
2257     // this private method.  This private method has remained stable from 10.6
2258     // through 10.11.  If it does change, the most likely thing is that it
2259     // will be removed and no longer called and this fix will simply stop
2260     // working.  The only real danger would be if Apple changed the return type
2261     // to a struct or floating-point type, which would change the calling
2262     // convention.
2263     - (id) _displayChanged
2264     {
2265         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2266         [queue postEvent:event];
2267         macdrv_release_event(event);
2269         return [super _displayChanged];
2270     }
2272     - (BOOL) isExcludedFromWindowsMenu
2273     {
2274         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2275     }
2277     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2278     {
2279         BOOL ret = [super validateMenuItem:menuItem];
2281         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2282             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2283         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2284             ret = NO;
2286         return ret;
2287     }
2289     /* We don't call this.  It's the action method of the items in the Window menu. */
2290     - (void) makeKeyAndOrderFront:(id)sender
2291     {
2292         if ([self isMiniaturized])
2293             [self deminiaturize:nil];
2294         [self orderBelow:nil orAbove:nil activate:NO];
2295         [[self ancestorWineWindow] postBroughtForwardEvent];
2297         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2298             [[WineApplicationController sharedController] windowGotFocus:self];
2299     }
2301     - (void) sendEvent:(NSEvent*)event
2302     {
2303         NSEventType type = event.type;
2305         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2306            interface control.  For example, Control-Tab switches focus among
2307            views.  We want to bypass that feature, so directly route key-down
2308            events to -keyDown:. */
2309         if (type == NSKeyDown)
2310             [[self firstResponder] keyDown:event];
2311         else
2312         {
2313             if (!draggingPhase && maximized && ![self isMovable] &&
2314                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2315                 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2316             {
2317                 NSRect titleBar = self.frame;
2318                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2319                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2320                 titleBar.origin.y = NSMaxY(contentRect);
2322                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2324                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2325                 {
2326                     static const NSWindowButton buttons[] = {
2327                         NSWindowCloseButton,
2328                         NSWindowMiniaturizeButton,
2329                         NSWindowZoomButton,
2330                         NSWindowFullScreenButton,
2331                     };
2332                     BOOL hitButton = NO;
2333                     int i;
2335                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2336                     {
2337                         NSButton* button;
2339                         if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2340                             continue;
2342                         button = [self standardWindowButton:buttons[i]];
2343                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2344                         {
2345                             hitButton = YES;
2346                             break;
2347                         }
2348                     }
2350                     if (!hitButton)
2351                     {
2352                         draggingPhase = 1;
2353                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2354                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2355                     }
2356                 }
2357             }
2358             else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2359             {
2360                 if ([self isMovable])
2361                 {
2362                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2363                     NSPoint newTopLeft = dragWindowStartPosition;
2365                     newTopLeft.x += point.x - dragStartPosition.x;
2366                     newTopLeft.y += point.y - dragStartPosition.y;
2368                     if (draggingPhase == 2)
2369                     {
2370                         macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2371                         mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2372                         [queue postEvent:mevent];
2373                         macdrv_release_event(mevent);
2375                         draggingPhase = 3;
2376                     }
2378                     [self setFrameTopLeftPoint:newTopLeft];
2379                 }
2380                 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2381                 {
2382                     macdrv_event* event;
2383                     NSRect frame = [self contentRectForFrameRect:self.frame];
2385                     [[WineApplicationController sharedController] flipRect:&frame];
2387                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2388                     event->window_restore_requested.keep_frame = TRUE;
2389                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2390                     [queue postEvent:event];
2391                     macdrv_release_event(event);
2393                     draggingPhase = 2;
2394                 }
2396                 if (type == NSLeftMouseUp)
2397                     [self endWindowDragging];
2398             }
2400             [super sendEvent:event];
2401         }
2402     }
2404     - (void) miniaturize:(id)sender
2405     {
2406         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2407         [queue postEvent:event];
2408         macdrv_release_event(event);
2410         WineWindow* parent = (WineWindow*)self.parentWindow;
2411         if ([parent isKindOfClass:[WineWindow class]])
2412             [parent grabDockIconSnapshotFromWindow:self force:YES];
2413     }
2415     - (void) toggleFullScreen:(id)sender
2416     {
2417         if (!self.disabled && !maximized)
2418             [super toggleFullScreen:sender];
2419     }
2421     - (void) setViewsNeedDisplay:(BOOL)value
2422     {
2423         if (value && ![self viewsNeedDisplay])
2424         {
2425             WineDisplayLink* link = [self wineDisplayLink];
2426             if (link)
2427             {
2428                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2429                 if (_lastDisplayTime + [link refreshPeriod] < now)
2430                     [self setAutodisplay:YES];
2431                 else
2432                 {
2433                     [link start];
2434                     _lastDisplayTime = now;
2435                 }
2436             }
2437         }
2438         [super setViewsNeedDisplay:value];
2439     }
2441     - (void) display
2442     {
2443         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2444         [super display];
2445         [self setAutodisplay:NO];
2446     }
2448     - (void) displayIfNeeded
2449     {
2450         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2451         [super displayIfNeeded];
2452         [self setAutodisplay:NO];
2453     }
2455     - (void) windowDidDrawContent
2456     {
2457         if (!drawnSinceShown)
2458         {
2459             drawnSinceShown = YES;
2460             dispatch_async(dispatch_get_main_queue(), ^{
2461                 [self checkTransparency];
2462             });
2463         }
2464     }
2466     - (NSArray*) childWineWindows
2467     {
2468         NSArray* childWindows = self.childWindows;
2469         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2470             return [child isKindOfClass:[WineWindow class]];
2471         }];
2472         return [childWindows objectsAtIndexes:indexes];
2473     }
2475     // We normally use the generic/calibrated RGB color space for the window,
2476     // rather than the device color space, to avoid expensive color conversion
2477     // which slows down drawing.  However, for windows displaying OpenGL, having
2478     // a different color space than the screen greatly reduces frame rates, often
2479     // limiting it to the display refresh rate.
2480     //
2481     // To avoid this, we switch back to the screen color space whenever the
2482     // window is covered by a view with an attached OpenGL context.
2483     - (void) updateColorSpace
2484     {
2485         NSRect contentRect = [[self contentView] frame];
2486         BOOL coveredByGLView = FALSE;
2487         WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2488         if ([view isKindOfClass:[WineContentView class]] && [view hasGLContext])
2489         {
2490             NSRect frame = [view convertRect:[view bounds] toView:nil];
2491             if (NSContainsRect(frame, contentRect))
2492                 coveredByGLView = TRUE;
2493         }
2495         if (coveredByGLView)
2496             [self setColorSpace:nil];
2497         else
2498             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2499     }
2501     - (void) updateForGLSubviews
2502     {
2503         [self updateColorSpace];
2504         if (gl_surface_mode == GL_SURFACE_BEHIND)
2505             [self checkTransparency];
2506     }
2508     - (void) setRetinaMode:(int)mode
2509     {
2510         NSRect frame;
2511         double scale = mode ? 0.5 : 2.0;
2512         NSAffineTransform* transform = [NSAffineTransform transform];
2514         [transform scaleBy:scale];
2516         if (shape)
2517             [shape transformUsingAffineTransform:transform];
2519         for (WineContentView* subview in [self.contentView subviews])
2520         {
2521             if ([subview isKindOfClass:[WineContentView class]])
2522                 [subview setRetinaMode:mode];
2523         }
2525         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2526         frame.origin.x *= scale;
2527         frame.origin.y *= scale;
2528         frame.size.width *= scale;
2529         frame.size.height *= scale;
2530         frame = [self frameRectForContentRect:frame];
2532         savedContentMinSize = [transform transformSize:savedContentMinSize];
2533         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2534             savedContentMaxSize.width *= scale;
2535         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2536             savedContentMaxSize.height *= scale;
2538         self.contentMinSize = [transform transformSize:self.contentMinSize];
2539         NSSize temp = self.contentMaxSize;
2540         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2541             temp.width *= scale;
2542         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2543             temp.height *= scale;
2544         self.contentMaxSize = temp;
2546         ignore_windowResize = TRUE;
2547         [self setFrameAndWineFrame:frame];
2548         ignore_windowResize = FALSE;
2549     }
2552     /*
2553      * ---------- NSResponder method overrides ----------
2554      */
2555     - (void) keyDown:(NSEvent *)theEvent
2556     {
2557         if ([theEvent isARepeat])
2558         {
2559             if (!allowKeyRepeats)
2560                 return;
2561         }
2562         else
2563             allowKeyRepeats = YES;
2565         [self postKeyEvent:theEvent];
2566     }
2568     - (void) flagsChanged:(NSEvent *)theEvent
2569     {
2570         static const struct {
2571             NSUInteger  mask;
2572             uint16_t    keycode;
2573         } modifiers[] = {
2574             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2575             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2576             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2577             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2578             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2579             { NX_DEVICELALTKEYMASK,     kVK_Option },
2580             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2581             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2582             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2583         };
2585         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2586         NSUInteger changed;
2587         int i, last_changed;
2589         fix_device_modifiers_by_generic(&modifierFlags);
2590         changed = modifierFlags ^ lastModifierFlags;
2592         last_changed = -1;
2593         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2594             if (changed & modifiers[i].mask)
2595                 last_changed = i;
2597         for (i = 0; i <= last_changed; i++)
2598         {
2599             if (changed & modifiers[i].mask)
2600             {
2601                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2603                 if (pressed)
2604                     allowKeyRepeats = NO;
2606                 if (i == last_changed)
2607                     lastModifierFlags = modifierFlags;
2608                 else
2609                 {
2610                     lastModifierFlags ^= modifiers[i].mask;
2611                     fix_generic_modifiers_by_device(&lastModifierFlags);
2612                 }
2614                 // Caps lock generates one event for each press-release action.
2615                 // We need to simulate a pair of events for each actual event.
2616                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2617                 {
2618                     [self postKey:modifiers[i].keycode
2619                           pressed:TRUE
2620                         modifiers:lastModifierFlags
2621                             event:(NSEvent*)theEvent];
2622                     pressed = FALSE;
2623                 }
2625                 [self postKey:modifiers[i].keycode
2626                       pressed:pressed
2627                     modifiers:lastModifierFlags
2628                         event:(NSEvent*)theEvent];
2629             }
2630         }
2631     }
2633     - (void) applicationWillHide
2634     {
2635         savedVisibleState = [self isVisible];
2636     }
2638     - (void) applicationDidUnhide
2639     {
2640         if ([self isVisible])
2641             [self becameEligibleParentOrChild];
2642     }
2645     /*
2646      * ---------- NSWindowDelegate methods ----------
2647      */
2648     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2649     {
2650         macdrv_query* query;
2651         NSSize size;
2653         query = macdrv_create_query();
2654         query->type = QUERY_MIN_MAX_INFO;
2655         query->window = (macdrv_window)[self retain];
2656         [self.queue query:query timeout:0.5];
2657         macdrv_release_query(query);
2659         size = [self contentMaxSize];
2660         if (proposedSize.width < size.width)
2661             size.width = proposedSize.width;
2662         if (proposedSize.height < size.height)
2663             size.height = proposedSize.height;
2664         return size;
2665     }
2667     - (void)windowDidBecomeKey:(NSNotification *)notification
2668     {
2669         WineApplicationController* controller = [WineApplicationController sharedController];
2670         NSEvent* event = [controller lastFlagsChanged];
2671         if (event)
2672             [self flagsChanged:event];
2674         if (causing_becomeKeyWindow == self) return;
2676         [controller windowGotFocus:self];
2677     }
2679     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2680     {
2681         [self checkWineDisplayLink];
2682     }
2684     - (void) windowDidChangeScreen:(NSNotification*)notification
2685     {
2686         [self checkWineDisplayLink];
2687     }
2689     - (void)windowDidDeminiaturize:(NSNotification *)notification
2690     {
2691         WineApplicationController* controller = [WineApplicationController sharedController];
2693         if (!ignore_windowDeminiaturize)
2694             [self postDidUnminimizeEvent];
2695         ignore_windowDeminiaturize = FALSE;
2697         [self becameEligibleParentOrChild];
2699         if (fullscreen && [self isOnActiveSpace])
2700             [controller updateFullscreenWindows];
2701         [controller adjustWindowLevels];
2703         if (![self parentWindow])
2704             [self postBroughtForwardEvent];
2706         if (!self.disabled && !self.noActivate)
2707         {
2708             causing_becomeKeyWindow = self;
2709             [self makeKeyWindow];
2710             causing_becomeKeyWindow = nil;
2711             [controller windowGotFocus:self];
2712         }
2714         [self windowDidResize:notification];
2715         [self checkWineDisplayLink];
2716     }
2718     - (void) windowDidEndLiveResize:(NSNotification *)notification
2719     {
2720         if (!maximized)
2721         {
2722             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2723             [queue postEvent:event];
2724             macdrv_release_event(event);
2725         }
2726     }
2728     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2729     {
2730         enteringFullScreen = FALSE;
2731         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2732         if (pendingOrderOut)
2733             [self doOrderOut];
2734     }
2736     - (void) windowDidExitFullScreen:(NSNotification*)notification
2737     {
2738         exitingFullScreen = FALSE;
2739         [self setFrameAndWineFrame:nonFullscreenFrame];
2740         [self windowDidResize:nil];
2741         if (pendingOrderOut)
2742             [self doOrderOut];
2743     }
2745     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2746     {
2747         enteringFullScreen = FALSE;
2748         enteredFullScreenTime = 0;
2749         if (pendingOrderOut)
2750             [self doOrderOut];
2751     }
2753     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2754     {
2755         exitingFullScreen = FALSE;
2756         [self windowDidResize:nil];
2757         if (pendingOrderOut)
2758             [self doOrderOut];
2759     }
2761     - (void)windowDidMiniaturize:(NSNotification *)notification
2762     {
2763         if (fullscreen && [self isOnActiveSpace])
2764             [[WineApplicationController sharedController] updateFullscreenWindows];
2765         [self checkWineDisplayLink];
2766     }
2768     - (void)windowDidMove:(NSNotification *)notification
2769     {
2770         [self windowDidResize:notification];
2771     }
2773     - (void)windowDidResignKey:(NSNotification *)notification
2774     {
2775         macdrv_event* event;
2777         if (causing_becomeKeyWindow) return;
2779         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2780         [queue postEvent:event];
2781         macdrv_release_event(event);
2782     }
2784     - (void)windowDidResize:(NSNotification *)notification
2785     {
2786         NSRect frame = self.wine_fractionalFrame;
2788         if ([self inLiveResize])
2789         {
2790             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2791                 resizingFromLeft = TRUE;
2792             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2793                 resizingFromTop = TRUE;
2794         }
2796         if (ignore_windowResize || exitingFullScreen) return;
2798         if ([self preventResizing])
2799         {
2800             NSRect contentRect = [self contentRectForFrameRect:frame];
2801             [self setContentMinSize:contentRect.size];
2802             [self setContentMaxSize:contentRect.size];
2803         }
2805         [self postWindowFrameChanged:frame
2806                           fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2807                             resizing:[self inLiveResize]];
2809         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2810         [self updateFullscreen];
2811     }
2813     - (BOOL)windowShouldClose:(id)sender
2814     {
2815         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2816         [queue postEvent:event];
2817         macdrv_release_event(event);
2818         return NO;
2819     }
2821     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2822     {
2823         if (maximized)
2824         {
2825             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2826             [queue postEvent:event];
2827             macdrv_release_event(event);
2828             return NO;
2829         }
2830         else if (!resizable)
2831         {
2832             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2833             [queue postEvent:event];
2834             macdrv_release_event(event);
2835             return NO;
2836         }
2838         return YES;
2839     }
2841     - (void) windowWillClose:(NSNotification*)notification
2842     {
2843         WineWindow* child;
2845         if (fakingClose) return;
2846         if (latentParentWindow)
2847         {
2848             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2849             self.latentParentWindow = nil;
2850         }
2852         for (child in latentChildWindows)
2853         {
2854             if (child.latentParentWindow == self)
2855                 child.latentParentWindow = nil;
2856         }
2857         [latentChildWindows removeAllObjects];
2858     }
2860     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2861     {
2862         enteringFullScreen = TRUE;
2863         nonFullscreenFrame = self.wine_fractionalFrame;
2864     }
2866     - (void) windowWillExitFullScreen:(NSNotification*)notification
2867     {
2868         exitingFullScreen = TRUE;
2869         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2870     }
2872     - (void)windowWillMiniaturize:(NSNotification *)notification
2873     {
2874         [self becameIneligibleParentOrChild];
2875         [self grabDockIconSnapshotFromWindow:nil force:NO];
2876     }
2878     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2879     {
2880         if ([self inLiveResize])
2881         {
2882             if (maximized)
2883                 return self.wine_fractionalFrame.size;
2885             NSRect rect;
2886             macdrv_query* query;
2888             rect = [self frame];
2889             if (resizingFromLeft)
2890                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2891             if (!resizingFromTop)
2892                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2893             rect.size = frameSize;
2894             rect = [self contentRectForFrameRect:rect];
2895             [[WineApplicationController sharedController] flipRect:&rect];
2897             query = macdrv_create_query();
2898             query->type = QUERY_RESIZE_SIZE;
2899             query->window = (macdrv_window)[self retain];
2900             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2901             query->resize_size.from_left = resizingFromLeft;
2902             query->resize_size.from_top = resizingFromTop;
2904             if ([self.queue query:query timeout:0.1])
2905             {
2906                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2907                 rect = [self frameRectForContentRect:rect];
2908                 frameSize = rect.size;
2909             }
2911             macdrv_release_query(query);
2912         }
2914         return frameSize;
2915     }
2917     - (void) windowWillStartLiveResize:(NSNotification *)notification
2918     {
2919         [self endWindowDragging];
2921         if (maximized)
2922         {
2923             macdrv_event* event;
2924             NSRect frame = [self contentRectForFrameRect:self.frame];
2926             [[WineApplicationController sharedController] flipRect:&frame];
2928             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2929             event->window_restore_requested.keep_frame = TRUE;
2930             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2931             [queue postEvent:event];
2932             macdrv_release_event(event);
2933         }
2934         else
2935             [self sendResizeStartQuery];
2937         frameAtResizeStart = [self frame];
2938         resizingFromLeft = resizingFromTop = FALSE;
2939     }
2941     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2942     {
2943         macdrv_query* query;
2944         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2945         NSSize maxSize;
2947         query = macdrv_create_query();
2948         query->type = QUERY_MIN_MAX_INFO;
2949         query->window = (macdrv_window)[self retain];
2950         [self.queue query:query timeout:0.5];
2951         macdrv_release_query(query);
2953         currentContentRect = [self contentRectForFrameRect:[self frame]];
2954         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2956         maxSize = [self contentMaxSize];
2957         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2958         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2960         // Try to keep the top-left corner where it is.
2961         newContentRect.origin.x = NSMinX(currentContentRect);
2962         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2964         // If that pushes the bottom or right off the screen, pull it up and to the left.
2965         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2966         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2967             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2968         if (NSMinY(newContentRect) < NSMinY(screenRect))
2969             newContentRect.origin.y = NSMinY(screenRect);
2971         // If that pushes the top or left off the screen, push it down and the right
2972         // again.  Do this last because the top-left corner is more important than the
2973         // bottom-right.
2974         if (NSMinX(newContentRect) < NSMinX(screenRect))
2975             newContentRect.origin.x = NSMinX(screenRect);
2976         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2977             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2979         return [self frameRectForContentRect:newContentRect];
2980     }
2983     /*
2984      * ---------- NSPasteboardOwner methods ----------
2985      */
2986     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2987     {
2988         macdrv_query* query = macdrv_create_query();
2989         query->type = QUERY_PASTEBOARD_DATA;
2990         query->window = (macdrv_window)[self retain];
2991         query->pasteboard_data.type = (CFStringRef)[type copy];
2993         [self.queue query:query timeout:3];
2994         macdrv_release_query(query);
2995     }
2997     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
2998     {
2999         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3000         [queue postEvent:event];
3001         macdrv_release_event(event);
3002     }
3005     /*
3006      * ---------- NSDraggingDestination methods ----------
3007      */
3008     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3009     {
3010         return [self draggingUpdated:sender];
3011     }
3013     - (void) draggingExited:(id <NSDraggingInfo>)sender
3014     {
3015         // This isn't really a query.  We don't need any response.  However, it
3016         // has to be processed in a similar manner as the other drag-and-drop
3017         // queries in order to maintain the proper order of operations.
3018         macdrv_query* query = macdrv_create_query();
3019         query->type = QUERY_DRAG_EXITED;
3020         query->window = (macdrv_window)[self retain];
3022         [self.queue query:query timeout:0.1];
3023         macdrv_release_query(query);
3024     }
3026     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3027     {
3028         NSDragOperation ret;
3029         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3030         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3031         NSPasteboard* pb = [sender draggingPasteboard];
3033         macdrv_query* query = macdrv_create_query();
3034         query->type = QUERY_DRAG_OPERATION;
3035         query->window = (macdrv_window)[self retain];
3036         query->drag_operation.x = floor(cgpt.x);
3037         query->drag_operation.y = floor(cgpt.y);
3038         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3039         query->drag_operation.accepted_op = NSDragOperationNone;
3040         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3042         [self.queue query:query timeout:3];
3043         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3044         macdrv_release_query(query);
3046         return ret;
3047     }
3049     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3050     {
3051         BOOL ret;
3052         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3053         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3054         NSPasteboard* pb = [sender draggingPasteboard];
3056         macdrv_query* query = macdrv_create_query();
3057         query->type = QUERY_DRAG_DROP;
3058         query->window = (macdrv_window)[self retain];
3059         query->drag_drop.x = floor(cgpt.x);
3060         query->drag_drop.y = floor(cgpt.y);
3061         query->drag_drop.op = [sender draggingSourceOperationMask];
3062         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3064         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3065         ret = query->status;
3066         macdrv_release_query(query);
3068         return ret;
3069     }
3071     - (BOOL) wantsPeriodicDraggingUpdates
3072     {
3073         return NO;
3074     }
3076 @end
3079 /***********************************************************************
3080  *              macdrv_create_cocoa_window
3082  * Create a Cocoa window with the given content frame and features (e.g.
3083  * title bar, close box, etc.).
3084  */
3085 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3086         CGRect frame, void* hwnd, macdrv_event_queue queue)
3088     __block WineWindow* window;
3090     OnMainThread(^{
3091         window = [[WineWindow createWindowWithFeatures:wf
3092                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3093                                                   hwnd:hwnd
3094                                                  queue:(WineEventQueue*)queue] retain];
3095     });
3097     return (macdrv_window)window;
3100 /***********************************************************************
3101  *              macdrv_destroy_cocoa_window
3103  * Destroy a Cocoa window.
3104  */
3105 void macdrv_destroy_cocoa_window(macdrv_window w)
3107     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3108     WineWindow* window = (WineWindow*)w;
3110     OnMainThread(^{
3111         [window doOrderOut];
3112         [window close];
3113     });
3114     [window.queue discardEventsMatchingMask:-1 forWindow:window];
3115     [window release];
3117     [pool release];
3120 /***********************************************************************
3121  *              macdrv_get_window_hwnd
3123  * Get the hwnd that was set for the window at creation.
3124  */
3125 void* macdrv_get_window_hwnd(macdrv_window w)
3127     WineWindow* window = (WineWindow*)w;
3128     return window.hwnd;
3131 /***********************************************************************
3132  *              macdrv_set_cocoa_window_features
3134  * Update a Cocoa window's features.
3135  */
3136 void macdrv_set_cocoa_window_features(macdrv_window w,
3137         const struct macdrv_window_features* wf)
3139     WineWindow* window = (WineWindow*)w;
3141     OnMainThread(^{
3142         [window setWindowFeatures:wf];
3143     });
3146 /***********************************************************************
3147  *              macdrv_set_cocoa_window_state
3149  * Update a Cocoa window's state.
3150  */
3151 void macdrv_set_cocoa_window_state(macdrv_window w,
3152         const struct macdrv_window_state* state)
3154     WineWindow* window = (WineWindow*)w;
3156     OnMainThread(^{
3157         [window setMacDrvState:state];
3158     });
3161 /***********************************************************************
3162  *              macdrv_set_cocoa_window_title
3164  * Set a Cocoa window's title.
3165  */
3166 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3167         size_t length)
3169     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3170     WineWindow* window = (WineWindow*)w;
3171     NSString* titleString;
3173     if (title)
3174         titleString = [NSString stringWithCharacters:title length:length];
3175     else
3176         titleString = @"";
3177     OnMainThreadAsync(^{
3178         [window setTitle:titleString];
3179         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3180             [NSApp changeWindowsItem:window title:titleString filename:NO];
3181     });
3183     [pool release];
3186 /***********************************************************************
3187  *              macdrv_order_cocoa_window
3189  * Reorder a Cocoa window relative to other windows.  If prev is
3190  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3191  * it is ordered above that window.  Otherwise, it is ordered to the
3192  * front.
3193  */
3194 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3195         macdrv_window n, int activate)
3197     WineWindow* window = (WineWindow*)w;
3198     WineWindow* prev = (WineWindow*)p;
3199     WineWindow* next = (WineWindow*)n;
3201     OnMainThreadAsync(^{
3202         [window orderBelow:prev
3203                    orAbove:next
3204                   activate:activate];
3205     });
3206     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3207                                   forWindow:window];
3208     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3209                                 forWindow:next];
3212 /***********************************************************************
3213  *              macdrv_hide_cocoa_window
3215  * Hides a Cocoa window.
3216  */
3217 void macdrv_hide_cocoa_window(macdrv_window w)
3219     WineWindow* window = (WineWindow*)w;
3221     OnMainThread(^{
3222         [window doOrderOut];
3223     });
3226 /***********************************************************************
3227  *              macdrv_set_cocoa_window_frame
3229  * Move a Cocoa window.
3230  */
3231 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3233     WineWindow* window = (WineWindow*)w;
3235     OnMainThread(^{
3236         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3237     });
3240 /***********************************************************************
3241  *              macdrv_get_cocoa_window_frame
3243  * Gets the frame of a Cocoa window.
3244  */
3245 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3247     WineWindow* window = (WineWindow*)w;
3249     OnMainThread(^{
3250         NSRect frame;
3252         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3253         [[WineApplicationController sharedController] flipRect:&frame];
3254         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3255     });
3258 /***********************************************************************
3259  *              macdrv_set_cocoa_parent_window
3261  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3262  * the parent window.
3263  */
3264 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3266     WineWindow* window = (WineWindow*)w;
3268     OnMainThread(^{
3269         [window setMacDrvParentWindow:(WineWindow*)parent];
3270     });
3273 /***********************************************************************
3274  *              macdrv_set_window_surface
3275  */
3276 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3278     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3279     WineWindow* window = (WineWindow*)w;
3281     OnMainThread(^{
3282         window.surface = surface;
3283         window.surface_mutex = mutex;
3284     });
3286     [pool release];
3289 /***********************************************************************
3290  *              macdrv_window_needs_display
3292  * Mark a window as needing display in a specified rect (in non-client
3293  * area coordinates).
3294  */
3295 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3297     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3298     WineWindow* window = (WineWindow*)w;
3300     OnMainThreadAsync(^{
3301         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3302     });
3304     [pool release];
3307 /***********************************************************************
3308  *              macdrv_set_window_shape
3310  * Sets the shape of a Cocoa window from an array of rectangles.  If
3311  * rects is NULL, resets the window's shape to its frame.
3312  */
3313 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3315     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3316     WineWindow* window = (WineWindow*)w;
3318     OnMainThread(^{
3319         if (!rects || !count)
3320         {
3321             window.shape = nil;
3322             window.shapeData = nil;
3323             [window checkEmptyShaped];
3324         }
3325         else
3326         {
3327             size_t length = sizeof(*rects) * count;
3328             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3329             {
3330                 NSBezierPath* path;
3331                 unsigned int i;
3333                 path = [NSBezierPath bezierPath];
3334                 for (i = 0; i < count; i++)
3335                     [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3336                 window.shape = path;
3337                 window.shapeData = [NSData dataWithBytes:rects length:length];
3338                 [window checkEmptyShaped];
3339             }
3340         }
3341     });
3343     [pool release];
3346 /***********************************************************************
3347  *              macdrv_set_window_alpha
3348  */
3349 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3351     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3352     WineWindow* window = (WineWindow*)w;
3354     [window setAlphaValue:alpha];
3356     [pool release];
3359 /***********************************************************************
3360  *              macdrv_set_window_color_key
3361  */
3362 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3363                                  CGFloat keyBlue)
3365     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3366     WineWindow* window = (WineWindow*)w;
3368     OnMainThread(^{
3369         window.colorKeyed       = TRUE;
3370         window.colorKeyRed      = keyRed;
3371         window.colorKeyGreen    = keyGreen;
3372         window.colorKeyBlue     = keyBlue;
3373         [window checkTransparency];
3374     });
3376     [pool release];
3379 /***********************************************************************
3380  *              macdrv_clear_window_color_key
3381  */
3382 void macdrv_clear_window_color_key(macdrv_window w)
3384     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3385     WineWindow* window = (WineWindow*)w;
3387     OnMainThread(^{
3388         window.colorKeyed = FALSE;
3389         [window checkTransparency];
3390     });
3392     [pool release];
3395 /***********************************************************************
3396  *              macdrv_window_use_per_pixel_alpha
3397  */
3398 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3400     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3401     WineWindow* window = (WineWindow*)w;
3403     OnMainThread(^{
3404         window.usePerPixelAlpha = use_per_pixel_alpha;
3405         [window checkTransparency];
3406     });
3408     [pool release];
3411 /***********************************************************************
3412  *              macdrv_give_cocoa_window_focus
3414  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3415  * orders it front and, if its frame was not within the desktop bounds,
3416  * Cocoa will typically move it on-screen.
3417  */
3418 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3420     WineWindow* window = (WineWindow*)w;
3422     OnMainThread(^{
3423         [window makeFocused:activate];
3424     });
3427 /***********************************************************************
3428  *              macdrv_set_window_min_max_sizes
3430  * Sets the window's minimum and maximum content sizes.
3431  */
3432 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3434     WineWindow* window = (WineWindow*)w;
3436     OnMainThread(^{
3437         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3438     });
3441 /***********************************************************************
3442  *              macdrv_create_view
3444  * Creates and returns a view with the specified frame rect.  The
3445  * caller is responsible for calling macdrv_dispose_view() on the view
3446  * when it is done with it.
3447  */
3448 macdrv_view macdrv_create_view(CGRect rect)
3450     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3451     __block WineContentView* view;
3453     if (CGRectIsNull(rect)) rect = CGRectZero;
3455     OnMainThread(^{
3456         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3458         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3459         [view setAutoresizesSubviews:NO];
3460         [view setHidden:YES];
3461         [nc addObserver:view
3462                selector:@selector(updateGLContexts)
3463                    name:NSViewGlobalFrameDidChangeNotification
3464                  object:view];
3465         [nc addObserver:view
3466                selector:@selector(updateGLContexts)
3467                    name:NSApplicationDidChangeScreenParametersNotification
3468                  object:NSApp];
3469     });
3471     [pool release];
3472     return (macdrv_view)view;
3475 /***********************************************************************
3476  *              macdrv_dispose_view
3478  * Destroys a view previously returned by macdrv_create_view.
3479  */
3480 void macdrv_dispose_view(macdrv_view v)
3482     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3483     WineContentView* view = (WineContentView*)v;
3485     OnMainThread(^{
3486         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3487         WineWindow* window = (WineWindow*)[view window];
3489         [nc removeObserver:view
3490                       name:NSViewGlobalFrameDidChangeNotification
3491                     object:view];
3492         [nc removeObserver:view
3493                       name:NSApplicationDidChangeScreenParametersNotification
3494                     object:NSApp];
3495         [view removeFromSuperview];
3496         [view release];
3497         [window updateForGLSubviews];
3498     });
3500     [pool release];
3503 /***********************************************************************
3504  *              macdrv_set_view_frame
3505  */
3506 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3508     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3509     WineContentView* view = (WineContentView*)v;
3511     if (CGRectIsNull(rect)) rect = CGRectZero;
3513     OnMainThreadAsync(^{
3514         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3515         NSRect oldFrame = [view frame];
3517         if (!NSEqualRects(oldFrame, newFrame))
3518         {
3519             [[view superview] setNeedsDisplayInRect:oldFrame];
3520             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3521                 [view setFrameSize:newFrame.size];
3522             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3523                 [view setFrameOrigin:newFrame.origin];
3524             else
3525                 [view setFrame:newFrame];
3526             [view setNeedsDisplay:YES];
3528             if (retina_enabled)
3529             {
3530                 int backing_size[2] = { 0 };
3531                 [view wine_setBackingSize:backing_size];
3532             }
3533             [(WineWindow*)[view window] updateForGLSubviews];
3534         }
3535     });
3537     [pool release];
3540 /***********************************************************************
3541  *              macdrv_set_view_superview
3543  * Move a view to a new superview and position it relative to its
3544  * siblings.  If p is non-NULL, the view is ordered behind it.
3545  * Otherwise, the view is ordered above n.  If s is NULL, use the
3546  * content view of w as the new superview.
3547  */
3548 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3550     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3551     WineContentView* view = (WineContentView*)v;
3552     WineContentView* superview = (WineContentView*)s;
3553     WineWindow* window = (WineWindow*)w;
3554     WineContentView* prev = (WineContentView*)p;
3555     WineContentView* next = (WineContentView*)n;
3557     if (!superview)
3558         superview = [window contentView];
3560     OnMainThreadAsync(^{
3561         if (superview == [view superview])
3562         {
3563             NSArray* subviews = [superview subviews];
3564             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3565             if (!prev && !next && index == [subviews count] - 1)
3566                 return;
3567             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3568                 return;
3569             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3570                 return;
3571         }
3573         WineWindow* oldWindow = (WineWindow*)[view window];
3574         WineWindow* newWindow = (WineWindow*)[superview window];
3576 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3577         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3578             [view removeFromSuperview];
3579 #endif
3580         if (prev)
3581             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3582         else
3583             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3585         if (oldWindow != newWindow)
3586         {
3587             [oldWindow updateForGLSubviews];
3588             [newWindow updateForGLSubviews];
3589         }
3590     });
3592     [pool release];
3595 /***********************************************************************
3596  *              macdrv_set_view_hidden
3597  */
3598 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3600     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3601     WineContentView* view = (WineContentView*)v;
3603     OnMainThreadAsync(^{
3604         [view setHidden:hidden];
3605         [(WineWindow*)view.window updateForGLSubviews];
3606     });
3608     [pool release];
3611 /***********************************************************************
3612  *              macdrv_add_view_opengl_context
3614  * Add an OpenGL context to the list being tracked for each view.
3615  */
3616 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3618     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3619     WineContentView* view = (WineContentView*)v;
3620     WineOpenGLContext *context = (WineOpenGLContext*)c;
3622     OnMainThread(^{
3623         [view addGLContext:context];
3624     });
3626     [pool release];
3629 /***********************************************************************
3630  *              macdrv_remove_view_opengl_context
3632  * Add an OpenGL context to the list being tracked for each view.
3633  */
3634 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3636     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3637     WineContentView* view = (WineContentView*)v;
3638     WineOpenGLContext *context = (WineOpenGLContext*)c;
3640     OnMainThreadAsync(^{
3641         [view removeGLContext:context];
3642     });
3644     [pool release];
3647 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3649     WineContentView* view = (WineContentView*)v;
3651     if (![view isKindOfClass:[WineContentView class]])
3652         return FALSE;
3654     [view wine_getBackingSize:backing_size];
3655     return TRUE;
3658 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3660     WineContentView* view = (WineContentView*)v;
3662     if ([view isKindOfClass:[WineContentView class]])
3663         [view wine_setBackingSize:backing_size];
3666 /***********************************************************************
3667  *              macdrv_window_background_color
3669  * Returns the standard Mac window background color as a 32-bit value of
3670  * the form 0x00rrggbb.
3671  */
3672 uint32_t macdrv_window_background_color(void)
3674     static uint32_t result;
3675     static dispatch_once_t once;
3677     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3678     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3679     // of it is to draw with it.
3680     dispatch_once(&once, ^{
3681         OnMainThread(^{
3682             unsigned char rgbx[4];
3683             unsigned char *planes = rgbx;
3684             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3685                                                                                pixelsWide:1
3686                                                                                pixelsHigh:1
3687                                                                             bitsPerSample:8
3688                                                                           samplesPerPixel:3
3689                                                                                  hasAlpha:NO
3690                                                                                  isPlanar:NO
3691                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3692                                                                              bitmapFormat:0
3693                                                                               bytesPerRow:4
3694                                                                              bitsPerPixel:32];
3695             [NSGraphicsContext saveGraphicsState];
3696             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3697             [[NSColor windowBackgroundColor] set];
3698             NSRectFill(NSMakeRect(0, 0, 1, 1));
3699             [NSGraphicsContext restoreGraphicsState];
3700             [bitmap release];
3701             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3702         });
3703     });
3705     return result;
3708 /***********************************************************************
3709  *              macdrv_send_text_input_event
3710  */
3711 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3713     OnMainThreadAsync(^{
3714         BOOL ret;
3715         macdrv_event* event;
3716         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3717         if (![window isKindOfClass:[WineWindow class]])
3718         {
3719             window = (WineWindow*)[NSApp mainWindow];
3720             if (![window isKindOfClass:[WineWindow class]])
3721                 window = [[WineApplicationController sharedController] frontWineWindow];
3722         }
3724         if (window)
3725         {
3726             NSUInteger localFlags = flags;
3727             CGEventRef c;
3728             NSEvent* event;
3730             window.imeData = data;
3731             fix_device_modifiers_by_generic(&localFlags);
3733             // An NSEvent created with +keyEventWithType:... is internally marked
3734             // as synthetic and doesn't get sent through input methods.  But one
3735             // created from a CGEvent doesn't have that problem.
3736             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3737             CGEventSetFlags(c, localFlags);
3738             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3739             event = [NSEvent eventWithCGEvent:c];
3740             CFRelease(c);
3742             window.commandDone = FALSE;
3743             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3744         }
3745         else
3746             ret = FALSE;
3748         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3749         event->sent_text_input.handled = ret;
3750         event->sent_text_input.done = done;
3751         [[window queue] postEvent:event];
3752         macdrv_release_event(event);
3753     });