appwiz.cpl: Add __WINE_ALLOC_SIZE attributes to heap_xxx() functions.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blob118bf3c2c783988d1b50559ebdc3ea7fd1c39d52
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         @synchronized(self) {
222             BOOL needsStart = !_windows.count;
223             [_windows addObject:window];
224             if (needsStart)
225                 CVDisplayLinkStart(_link);
226         }
227     }
229     - (void) removeWindow:(WineWindow*)window
230     {
231         @synchronized(self) {
232             BOOL wasRunning = _windows.count > 0;
233             [_windows removeObject:window];
234             if (wasRunning && !_windows.count)
235                 CVDisplayLinkStop(_link);
236         }
237     }
239     - (void) fire
240     {
241         NSSet* windows;
242         @synchronized(self) {
243             windows = [_windows copy];
244         }
245         dispatch_async(dispatch_get_main_queue(), ^{
246             BOOL anyDisplayed = FALSE;
247             for (WineWindow* window in windows)
248             {
249                 if ([window viewsNeedDisplay])
250                 {
251                     [window displayIfNeeded];
252                     anyDisplayed = YES;
253                 }
254             }
255             if (!anyDisplayed)
256                 CVDisplayLinkStop(_link);
257         });
258         [windows release];
259     }
261     - (NSTimeInterval) refreshPeriod
262     {
263         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
264             return _actualRefreshPeriod;
266         if (_nominalRefreshPeriod)
267             return _nominalRefreshPeriod;
269         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
270         if (time.flags & kCVTimeIsIndefinite)
271             return 1.0 / 60.0;
272         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
273         return _nominalRefreshPeriod;
274     }
276     - (void) start
277     {
278         CVDisplayLinkStart(_link);
279     }
281 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
283     WineDisplayLink* link = displayLinkContext;
284     [link fire];
285     return kCVReturnSuccess;
288 @end
291 @interface WineContentView : NSView <NSTextInputClient>
293     NSMutableArray* glContexts;
294     NSMutableArray* pendingGlContexts;
295     BOOL _cachedHasGLDescendant;
296     BOOL _cachedHasGLDescendantValid;
297     BOOL clearedGlSurface;
299     NSMutableAttributedString* markedText;
300     NSRange markedTextSelection;
302     int backingSize[2];
305     - (void) addGLContext:(WineOpenGLContext*)context;
306     - (void) removeGLContext:(WineOpenGLContext*)context;
307     - (void) updateGLContexts;
309     - (void) wine_getBackingSize:(int*)outBackingSize;
310     - (void) wine_setBackingSize:(const int*)newBackingSize;
312 @end
315 @interface WineWindow ()
317 @property (readwrite, nonatomic) BOOL disabled;
318 @property (readwrite, nonatomic) BOOL noActivate;
319 @property (readwrite, nonatomic) BOOL floating;
320 @property (readwrite, nonatomic) BOOL drawnSinceShown;
321 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
322 @property (retain, nonatomic) NSWindow* latentParentWindow;
324 @property (nonatomic) void* hwnd;
325 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
327 @property (nonatomic) void* surface;
328 @property (nonatomic) pthread_mutex_t* surface_mutex;
330 @property (copy, nonatomic) NSBezierPath* shape;
331 @property (copy, nonatomic) NSData* shapeData;
332 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
333 @property (readonly, nonatomic) BOOL needsTransparency;
335 @property (nonatomic) BOOL colorKeyed;
336 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
337 @property (nonatomic) BOOL usePerPixelAlpha;
339 @property (assign, nonatomic) void* imeData;
340 @property (nonatomic) BOOL commandDone;
342 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
344     - (void) updateColorSpace;
345     - (void) updateForGLSubviews;
347     - (BOOL) becameEligibleParentOrChild;
348     - (void) becameIneligibleChild;
350     - (void) windowDidDrawContent;
352 @end
355 @implementation WineContentView
357     - (void) dealloc
358     {
359         [markedText release];
360         [glContexts release];
361         [pendingGlContexts release];
362         [super dealloc];
363     }
365     - (BOOL) isFlipped
366     {
367         return YES;
368     }
370     - (void) drawRect:(NSRect)rect
371     {
372         WineWindow* window = (WineWindow*)[self window];
374         for (WineOpenGLContext* context in pendingGlContexts)
375         {
376             if (!clearedGlSurface)
377             {
378                 context.shouldClearToBlack = TRUE;
379                 clearedGlSurface = TRUE;
380             }
381             context.needsUpdate = TRUE;
382         }
383         [glContexts addObjectsFromArray:pendingGlContexts];
384         [pendingGlContexts removeAllObjects];
386         if ([window contentView] != self)
387             return;
389         if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
390         {
391             [[NSColor clearColor] setFill];
392             NSRectFill(rect);
394             [window.shape addClip];
396             [[NSColor windowBackgroundColor] setFill];
397             NSRectFill(rect);
398         }
400         if (window.surface && window.surface_mutex &&
401             !pthread_mutex_lock(window.surface_mutex))
402         {
403             const CGRect* rects;
404             int count;
406             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
407             {
408                 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
409                 CGContextRef context;
410                 int i;
412                 [window.shape addClip];
414                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
415                 CGContextSetBlendMode(context, kCGBlendModeCopy);
416                 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
418                 for (i = 0; i < count; i++)
419                 {
420                     CGRect imageRect;
421                     CGImageRef image;
423                     imageRect = CGRectIntersection(rects[i], dirtyRect);
424                     image = create_surface_image(window.surface, &imageRect, FALSE);
426                     if (image)
427                     {
428                         if (window.colorKeyed)
429                         {
430                             CGImageRef maskedImage;
431                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
432                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
433                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
434                             maskedImage = CGImageCreateWithMaskingColors(image, components);
435                             if (maskedImage)
436                             {
437                                 CGImageRelease(image);
438                                 image = maskedImage;
439                             }
440                         }
442                         CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
444                         CGImageRelease(image);
445                     }
446                 }
448                 [window windowDidDrawContent];
449             }
451             pthread_mutex_unlock(window.surface_mutex);
452         }
454         // If the window may be transparent, then we have to invalidate the
455         // shadow every time we draw.  Also, if this is the first time we've
456         // drawn since changing from transparent to opaque.
457         if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
458         {
459             window.shapeChangedSinceLastDraw = FALSE;
460             [window invalidateShadow];
461         }
462     }
464     - (void) addGLContext:(WineOpenGLContext*)context
465     {
466         BOOL hadContext = [self hasGLContext];
467         if (!glContexts)
468             glContexts = [[NSMutableArray alloc] init];
469         if (!pendingGlContexts)
470             pendingGlContexts = [[NSMutableArray alloc] init];
472         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
473         {
474             [glContexts addObject:context];
475             if (!clearedGlSurface)
476             {
477                 context.shouldClearToBlack = TRUE;
478                 clearedGlSurface = TRUE;
479             }
480             context.needsUpdate = TRUE;
481         }
482         else
483         {
484             [pendingGlContexts addObject:context];
485             [self setNeedsDisplay:YES];
486         }
488         if (!hadContext)
489             [self invalidateHasGLDescendant];
490         [(WineWindow*)[self window] updateForGLSubviews];
491     }
493     - (void) removeGLContext:(WineOpenGLContext*)context
494     {
495         BOOL hadContext = [self hasGLContext];
496         [glContexts removeObjectIdenticalTo:context];
497         [pendingGlContexts removeObjectIdenticalTo:context];
498         if (hadContext && ![self hasGLContext])
499             [self invalidateHasGLDescendant];
500         [(WineWindow*)[self window] updateForGLSubviews];
501     }
503     - (void) updateGLContexts:(BOOL)reattach
504     {
505         for (WineOpenGLContext* context in glContexts)
506         {
507             context.needsUpdate = TRUE;
508             if (reattach)
509                 context.needsReattach = TRUE;
510         }
511     }
513     - (void) updateGLContexts
514     {
515         [self updateGLContexts:NO];
516     }
518     - (BOOL) hasGLContext
519     {
520         return [glContexts count] || [pendingGlContexts count];
521     }
523     - (BOOL) _hasGLDescendant
524     {
525         if ([self isHidden])
526             return NO;
527         if ([self hasGLContext])
528             return YES;
529         for (WineContentView* view in [self subviews])
530         {
531             if ([view hasGLDescendant])
532                 return YES;
533         }
534         return NO;
535     }
537     - (BOOL) hasGLDescendant
538     {
539         if (!_cachedHasGLDescendantValid)
540         {
541             _cachedHasGLDescendant = [self _hasGLDescendant];
542             _cachedHasGLDescendantValid = YES;
543         }
544         return _cachedHasGLDescendant;
545     }
547     - (void) invalidateHasGLDescendant
548     {
549         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
550         _cachedHasGLDescendantValid = NO;
551         if (invalidateAncestors && self != [[self window] contentView])
552         {
553             WineContentView* superview = (WineContentView*)[self superview];
554             if ([superview isKindOfClass:[WineContentView class]])
555                 [superview invalidateHasGLDescendant];
556         }
557     }
559     - (void) wine_getBackingSize:(int*)outBackingSize
560     {
561         @synchronized(self) {
562             memcpy(outBackingSize, backingSize, sizeof(backingSize));
563         }
564     }
565     - (void) wine_setBackingSize:(const int*)newBackingSize
566     {
567         @synchronized(self) {
568             memcpy(backingSize, newBackingSize, sizeof(backingSize));
569         }
570     }
572     - (void) setRetinaMode:(int)mode
573     {
574         double scale = mode ? 0.5 : 2.0;
575         NSRect frame = self.frame;
576         frame.origin.x *= scale;
577         frame.origin.y *= scale;
578         frame.size.width *= scale;
579         frame.size.height *= scale;
580         [self setFrame:frame];
581         [self updateGLContexts];
583         for (WineContentView* subview in [self subviews])
584         {
585             if ([subview isKindOfClass:[WineContentView class]])
586                 [subview setRetinaMode:mode];
587         }
588     }
590     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
591     {
592         return YES;
593     }
595     - (BOOL) preservesContentDuringLiveResize
596     {
597         // Returning YES from this tells Cocoa to keep our view's content during
598         // a Cocoa-driven resize.  In theory, we're also supposed to override
599         // -setFrameSize: to mark exposed sections as needing redisplay, but
600         // user32 will take care of that in a roundabout way.  This way, we don't
601         // redraw until the window surface is flushed.
602         //
603         // This doesn't do anything when we resize the window ourselves.
604         return YES;
605     }
607     - (BOOL)acceptsFirstResponder
608     {
609         return [[self window] contentView] == self;
610     }
612     - (BOOL) mouseDownCanMoveWindow
613     {
614         return NO;
615     }
617     - (void) viewDidHide
618     {
619         [super viewDidHide];
620         if ([self hasGLContext])
621             [self invalidateHasGLDescendant];
622     }
624     - (void) viewDidUnhide
625     {
626         [super viewDidUnhide];
627         if ([self hasGLContext])
628         {
629             [self updateGLContexts:YES];
630             [self invalidateHasGLDescendant];
631         }
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             {
987                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
988                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
989             }
990             else
991             {
992                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
993                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
994                 if (style & NSFullScreenWindowMask)
995                     [super toggleFullScreen:nil];
996             }
997         }
999         if (behavior != [self collectionBehavior])
1000         {
1001             [self setCollectionBehavior:behavior];
1002             [self adjustFeaturesForState];
1003         }
1004     }
1006     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1007     {
1008         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1009                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1010         NSUInteger currentStyle = [self styleMask];
1011         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1013         if (newStyle != currentStyle)
1014         {
1015             NSString* title = [[[self title] copy] autorelease];
1016             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1017             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1018             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1019             {
1020                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1021                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1022                 // just NSTitledWindowMask, the window buttons should disappear rather
1023                 // than just being disabled.  But they don't.  Similarly in reverse.
1024                 // The workaround is to also toggle NSClosableWindowMask at the same time.
1025                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1026             }
1027             [self setStyleMask:newStyle];
1029             // -setStyleMask: resets the firstResponder to the window.  Set it
1030             // back to the content view.
1031             if ([[self contentView] acceptsFirstResponder])
1032                 [self makeFirstResponder:[self contentView]];
1034             [self adjustFullScreenBehavior:[self collectionBehavior]];
1036             if ([[self title] length] == 0 && [title length] > 0)
1037                 [self setTitle:title];
1038         }
1040         resizable = wf->resizable;
1041         [self adjustFeaturesForState];
1042         [self setHasShadow:wf->shadow];
1043     }
1045     // Indicates if the window would be visible if the app were not hidden.
1046     - (BOOL) wouldBeVisible
1047     {
1048         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1049     }
1051     - (BOOL) isOrderedIn
1052     {
1053         return [self wouldBeVisible] || [self isMiniaturized];
1054     }
1056     - (NSInteger) minimumLevelForActive:(BOOL)active
1057     {
1058         NSInteger level;
1060         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1061                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1062             level = NSFloatingWindowLevel;
1063         else
1064             level = NSNormalWindowLevel;
1066         if (active)
1067         {
1068             BOOL captured;
1070             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1072             if (captured || fullscreen)
1073             {
1074                 if (captured)
1075                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1076                 else
1077                     level = NSStatusWindowLevel + 1;
1079                 if (self.floating)
1080                     level++;
1081             }
1082         }
1084         return level;
1085     }
1087     - (void) postDidUnminimizeEvent
1088     {
1089         macdrv_event* event;
1091         /* Coalesce events by discarding any previous ones still in the queue. */
1092         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1093                                forWindow:self];
1095         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1096         [queue postEvent:event];
1097         macdrv_release_event(event);
1098     }
1100     - (void) sendResizeStartQuery
1101     {
1102         macdrv_query* query = macdrv_create_query();
1103         query->type = QUERY_RESIZE_START;
1104         query->window = (macdrv_window)[self retain];
1106         [self.queue query:query timeout:0.3];
1107         macdrv_release_query(query);
1108     }
1110     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1111     {
1112         NSWindowCollectionBehavior behavior;
1114         self.disabled = state->disabled;
1115         self.noActivate = state->no_activate;
1117         if (self.floating != state->floating)
1118         {
1119             self.floating = state->floating;
1120             if (state->floating)
1121             {
1122                 // Became floating.  If child of non-floating window, make that
1123                 // relationship latent.
1124                 WineWindow* parent = (WineWindow*)[self parentWindow];
1125                 if (parent && !parent.floating)
1126                     [self becameIneligibleChild];
1127             }
1128             else
1129             {
1130                 // Became non-floating.  If parent of floating children, make that
1131                 // relationship latent.
1132                 WineWindow* child;
1133                 for (child in [self childWineWindows])
1134                 {
1135                     if (child.floating)
1136                         [child becameIneligibleChild];
1137                 }
1138             }
1140             // Check our latent relationships.  If floating status was the only
1141             // reason they were latent, then make them active.
1142             if ([self isVisible])
1143                 [self becameEligibleParentOrChild];
1145             [[WineApplicationController sharedController] adjustWindowLevels];
1146         }
1148         if (state->minimized_valid)
1149         {
1150             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1152             pendingMinimize = FALSE;
1153             if (state->minimized && ![self isMiniaturized])
1154             {
1155                 if ([self wouldBeVisible])
1156                 {
1157                     if ([self styleMask] & NSFullScreenWindowMask)
1158                     {
1159                         [self postDidUnminimizeEvent];
1160                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1161                     }
1162                     else
1163                     {
1164                         [super miniaturize:nil];
1165                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1166                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1167                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1168                     }
1169                 }
1170                 else
1171                     pendingMinimize = TRUE;
1172             }
1173             else if (!state->minimized && [self isMiniaturized])
1174             {
1175                 ignore_windowDeminiaturize = TRUE;
1176                 [self deminiaturize:nil];
1177                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1178             }
1180             if (discard)
1181                 [queue discardEventsMatchingMask:discard forWindow:self];
1182         }
1184         if (state->maximized != maximized)
1185         {
1186             maximized = state->maximized;
1187             [self adjustFeaturesForState];
1189             if (!maximized && [self inLiveResize])
1190                 [self sendResizeStartQuery];
1191         }
1193         behavior = NSWindowCollectionBehaviorDefault;
1194         if (state->excluded_by_expose)
1195             behavior |= NSWindowCollectionBehaviorTransient;
1196         else
1197             behavior |= NSWindowCollectionBehaviorManaged;
1198         if (state->excluded_by_cycle)
1199         {
1200             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1201             if ([self isOrderedIn])
1202                 [NSApp removeWindowsItem:self];
1203         }
1204         else
1205         {
1206             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1207             if ([self isOrderedIn])
1208                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1209         }
1210         [self adjustFullScreenBehavior:behavior];
1211     }
1213     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1214     {
1215         BOOL reordered = FALSE;
1217         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1218         {
1219             if ([self level] > [child level])
1220                 [child setLevel:[self level]];
1221             if (![child isVisible])
1222                 [child setAutodisplay:YES];
1223             [self addChildWindow:child ordered:NSWindowAbove];
1224             [child checkWineDisplayLink];
1225             [latentChildWindows removeObjectIdenticalTo:child];
1226             child.latentParentWindow = nil;
1227             reordered = TRUE;
1228         }
1229         else
1230         {
1231             if (!latentChildWindows)
1232                 latentChildWindows = [[NSMutableArray alloc] init];
1233             if (![latentChildWindows containsObject:child])
1234                 [latentChildWindows addObject:child];
1235             child.latentParentWindow = self;
1236         }
1238         return reordered;
1239     }
1241     - (BOOL) addChildWineWindow:(WineWindow*)child
1242     {
1243         return [self addChildWineWindow:child assumeVisible:FALSE];
1244     }
1246     - (void) removeChildWineWindow:(WineWindow*)child
1247     {
1248         [self removeChildWindow:child];
1249         if (child.latentParentWindow == self)
1250             child.latentParentWindow = nil;
1251         [latentChildWindows removeObjectIdenticalTo:child];
1252     }
1254     - (BOOL) becameEligibleParentOrChild
1255     {
1256         BOOL reordered = FALSE;
1257         NSUInteger count;
1259         if (latentParentWindow.floating || !self.floating)
1260         {
1261             // If we aren't visible currently, we assume that we should be and soon
1262             // will be.  So, if the latent parent is visible that's enough to assume
1263             // we can establish the parent-child relationship in Cocoa.  That will
1264             // actually make us visible, which is fine.
1265             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1266                 reordered = TRUE;
1267         }
1269         // Here, though, we may not actually be visible yet and adding a child
1270         // won't make us visible.  The caller will have to call this method
1271         // again after actually making us visible.
1272         if ([self isVisible] && (count = [latentChildWindows count]))
1273         {
1274             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1275             NSUInteger i;
1277             for (i = 0; i < count; i++)
1278             {
1279                 WineWindow* child = [latentChildWindows objectAtIndex:i];
1280                 if ([child isVisible] && (self.floating || !child.floating))
1281                 {
1282                     if (child.latentParentWindow == self)
1283                     {
1284                         if ([self level] > [child level])
1285                             [child setLevel:[self level]];
1286                         [self addChildWindow:child ordered:NSWindowAbove];
1287                         child.latentParentWindow = nil;
1288                         reordered = TRUE;
1289                     }
1290                     else
1291                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1292                     [indexesToRemove addIndex:i];
1293                 }
1294             }
1296             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1297         }
1299         return reordered;
1300     }
1302     - (void) becameIneligibleChild
1303     {
1304         WineWindow* parent = (WineWindow*)[self parentWindow];
1305         if (parent)
1306         {
1307             if (!parent->latentChildWindows)
1308                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1309             [parent->latentChildWindows insertObject:self atIndex:0];
1310             self.latentParentWindow = parent;
1311             [parent removeChildWindow:self];
1312         }
1313     }
1315     - (void) becameIneligibleParentOrChild
1316     {
1317         NSArray* childWindows = [self childWineWindows];
1319         [self becameIneligibleChild];
1321         if ([childWindows count])
1322         {
1323             WineWindow* child;
1325             for (child in childWindows)
1326             {
1327                 child.latentParentWindow = self;
1328                 [self removeChildWindow:child];
1329             }
1331             if (latentChildWindows)
1332                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1333             else
1334                 latentChildWindows = [childWindows mutableCopy];
1335         }
1336     }
1338     // Determine if, among Wine windows, this window is directly above or below
1339     // a given other Wine window with no other Wine window intervening.
1340     // Intervening non-Wine windows are ignored.
1341     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1342     {
1343         NSNumber* windowNumber;
1344         NSNumber* otherWindowNumber;
1345         NSArray* windowNumbers;
1346         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1348         if (![self isVisible] || ![otherWindow isVisible])
1349             return FALSE;
1351         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1352         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1353         windowNumbers = [[self class] windowNumbersWithOptions:0];
1354         windowIndex = [windowNumbers indexOfObject:windowNumber];
1355         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1357         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1358             return FALSE;
1360         if (orderingMode == NSWindowAbove)
1361         {
1362             lowIndex = windowIndex;
1363             highIndex = otherWindowIndex;
1364         }
1365         else if (orderingMode == NSWindowBelow)
1366         {
1367             lowIndex = otherWindowIndex;
1368             highIndex = windowIndex;
1369         }
1370         else
1371             return FALSE;
1373         if (highIndex <= lowIndex)
1374             return FALSE;
1376         for (i = lowIndex + 1; i < highIndex; i++)
1377         {
1378             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1379             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1380             if ([interveningWindow isKindOfClass:[WineWindow class]])
1381                 return FALSE;
1382         }
1384         return TRUE;
1385     }
1387     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1388     {
1389         NSMutableArray* windowNumbers;
1390         NSNumber* childWindowNumber;
1391         NSUInteger otherIndex, limit;
1392         NSArray* origChildren;
1393         NSMutableArray* children;
1395         // Get the z-order from the window server and modify it to reflect the
1396         // requested window ordering.
1397         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1398         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1399         [windowNumbers removeObject:childWindowNumber];
1400         otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1401         [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1403         // Get our child windows and sort them in the reverse of the desired
1404         // z-order (back-to-front).
1405         origChildren = [self childWineWindows];
1406         children = [[origChildren mutableCopy] autorelease];
1407         [children sortWithOptions:NSSortStable
1408                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1409             NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1410             NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1411             NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1412             NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1413             if (index1 == NSNotFound)
1414             {
1415                 if (index2 == NSNotFound)
1416                     return NSOrderedSame;
1417                 else
1418                     return NSOrderedAscending;
1419             }
1420             else if (index2 == NSNotFound)
1421                 return NSOrderedDescending;
1422             else if (index1 < index2)
1423                 return NSOrderedDescending;
1424             else if (index2 < index1)
1425                 return NSOrderedAscending;
1427             return NSOrderedSame;
1428         }];
1430         // If the current and desired children arrays match up to a point, leave
1431         // those matching children alone.
1432         limit = MIN([origChildren count], [children count]);
1433         for (otherIndex = 0; otherIndex < limit; otherIndex++)
1434         {
1435             if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1436                 break;
1437         }
1438         [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1440         // Remove all of the child windows and re-add them back-to-front so they
1441         // are in the desired order.
1442         for (other in children)
1443             [self removeChildWindow:other];
1444         for (other in children)
1445             [self addChildWindow:other ordered:NSWindowAbove];
1446     }
1448     /* Returns whether or not the window was ordered in, which depends on if
1449        its frame intersects any screen. */
1450     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1451     {
1452         WineApplicationController* controller = [WineApplicationController sharedController];
1453         if (![self isMiniaturized])
1454         {
1455             BOOL needAdjustWindowLevels = FALSE;
1456             BOOL wasVisible;
1458             [controller transformProcessToForeground];
1459             if ([NSApp isHidden])
1460                 [NSApp unhide:nil];
1461             wasVisible = [self isVisible];
1463             if (activate)
1464                 [NSApp activateIgnoringOtherApps:YES];
1466             NSDisableScreenUpdates();
1468             if ([self becameEligibleParentOrChild])
1469                 needAdjustWindowLevels = TRUE;
1471             if (prev || next)
1472             {
1473                 WineWindow* other = [prev isVisible] ? prev : next;
1474                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1476                 if (![self isOrdered:orderingMode relativeTo:other])
1477                 {
1478                     WineWindow* parent = (WineWindow*)[self parentWindow];
1479                     WineWindow* otherParent = (WineWindow*)[other parentWindow];
1481                     // This window level may not be right for this window based
1482                     // on floating-ness, fullscreen-ness, etc.  But we set it
1483                     // temporarily to allow us to order the windows properly.
1484                     // Then the levels get fixed by -adjustWindowLevels.
1485                     if ([self level] != [other level])
1486                         [self setLevel:[other level]];
1487                     [self setAutodisplay:YES];
1488                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1489                     [self checkWineDisplayLink];
1491                     // The above call to -[NSWindow orderWindow:relativeTo:] won't
1492                     // reorder windows which are both children of the same parent
1493                     // relative to each other, so do that separately.
1494                     if (parent && parent == otherParent)
1495                         [parent order:orderingMode childWindow:self relativeTo:other];
1497                     needAdjustWindowLevels = TRUE;
1498                 }
1499             }
1500             else
1501             {
1502                 // Again, temporarily set level to make sure we can order to
1503                 // the right place.
1504                 next = [controller frontWineWindow];
1505                 if (next && [self level] < [next level])
1506                     [self setLevel:[next level]];
1507                 [self setAutodisplay:YES];
1508                 [self orderFront:nil];
1509                 [self checkWineDisplayLink];
1510                 needAdjustWindowLevels = TRUE;
1511             }
1513             if ([self becameEligibleParentOrChild])
1514                 needAdjustWindowLevels = TRUE;
1516             if (needAdjustWindowLevels)
1517             {
1518                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1519                     [controller updateFullscreenWindows];
1520                 [controller adjustWindowLevels];
1521             }
1523             if (pendingMinimize)
1524             {
1525                 [super miniaturize:nil];
1526                 pendingMinimize = FALSE;
1527             }
1529             NSEnableScreenUpdates();
1531             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1532                Generate a frame-changed event just in case.  The back end will ignore
1533                it if nothing actually changed. */
1534             [self windowDidResize:nil];
1536             if (![self isExcludedFromWindowsMenu])
1537                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1538         }
1539     }
1541     - (void) doOrderOut
1542     {
1543         WineApplicationController* controller = [WineApplicationController sharedController];
1544         BOOL wasVisible = [self isVisible];
1545         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1547         if ([self isMiniaturized])
1548             pendingMinimize = TRUE;
1550         WineWindow* parent = (WineWindow*)self.parentWindow;
1551         if ([parent isKindOfClass:[WineWindow class]])
1552             [parent grabDockIconSnapshotFromWindow:self force:NO];
1554         [self becameIneligibleParentOrChild];
1555         if ([self isMiniaturized])
1556         {
1557             fakingClose = TRUE;
1558             [self close];
1559             fakingClose = FALSE;
1560         }
1561         else
1562             [self orderOut:nil];
1563         [self checkWineDisplayLink];
1564         [self setBackgroundColor:[NSColor clearColor]];
1565         [self setOpaque:NO];
1566         drawnSinceShown = NO;
1567         savedVisibleState = FALSE;
1568         if (wasVisible && wasOnActiveSpace && fullscreen)
1569             [controller updateFullscreenWindows];
1570         [controller adjustWindowLevels];
1571         [NSApp removeWindowsItem:self];
1573         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1574                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1575                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1576                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1577                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1578                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1579                                forWindow:self];
1580     }
1582     - (void) updateFullscreen
1583     {
1584         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1585         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1587         if (nowFullscreen != fullscreen)
1588         {
1589             WineApplicationController* controller = [WineApplicationController sharedController];
1591             fullscreen = nowFullscreen;
1592             if ([self isVisible] && [self isOnActiveSpace])
1593                 [controller updateFullscreenWindows];
1595             [controller adjustWindowLevels];
1596         }
1597     }
1599     - (void) setFrameAndWineFrame:(NSRect)frame
1600     {
1601         [self setFrame:frame display:YES];
1603         wineFrame = frame;
1604         roundedWineFrame = self.frame;
1605         CGFloat junk;
1606 #if CGFLOAT_IS_DOUBLE
1607         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1608              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1609             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1610             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1611             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1612             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1613             roundedWineFrame = wineFrame;
1614 #else
1615         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1616              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1617             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1618             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1619             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1620             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1621             roundedWineFrame = wineFrame;
1622 #endif
1623     }
1625     - (void) setFrameFromWine:(NSRect)contentRect
1626     {
1627         /* Origin is (left, top) in a top-down space.  Need to convert it to
1628            (left, bottom) in a bottom-up space. */
1629         [[WineApplicationController sharedController] flipRect:&contentRect];
1631         /* The back end is establishing a new window size and position.  It's
1632            not interested in any stale events regarding those that may be sitting
1633            in the queue. */
1634         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1635                                forWindow:self];
1637         if (!NSIsEmptyRect(contentRect))
1638         {
1639             NSRect frame, oldFrame;
1641             oldFrame = self.wine_fractionalFrame;
1642             frame = [self frameRectForContentRect:contentRect];
1643             if (!NSEqualRects(frame, oldFrame))
1644             {
1645                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1646                 BOOL needEnableScreenUpdates = FALSE;
1648                 if ([self preventResizing])
1649                 {
1650                     // Allow the following calls to -setFrame:display: to work even
1651                     // if they would violate the content size constraints. This
1652                     // shouldn't be necessary since the content size constraints are
1653                     // documented to not constrain that method, but it seems to be.
1654                     [self setContentMinSize:NSZeroSize];
1655                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1656                 }
1658                 if (equalSizes && [[self childWineWindows] count])
1659                 {
1660                     // If we change the window frame such that the origin moves
1661                     // but the size doesn't change, then Cocoa moves child
1662                     // windows with the parent.  We don't want that so we fake
1663                     // a change of the size and then change it back.
1664                     NSRect bogusFrame = frame;
1665                     bogusFrame.size.width++;
1667                     NSDisableScreenUpdates();
1668                     needEnableScreenUpdates = TRUE;
1670                     ignore_windowResize = TRUE;
1671                     [self setFrame:bogusFrame display:NO];
1672                     ignore_windowResize = FALSE;
1673                 }
1675                 [self setFrameAndWineFrame:frame];
1676                 if ([self preventResizing])
1677                 {
1678                     [self setContentMinSize:contentRect.size];
1679                     [self setContentMaxSize:contentRect.size];
1680                 }
1682                 if (needEnableScreenUpdates)
1683                     NSEnableScreenUpdates();
1685                 if (!equalSizes)
1686                     [self updateColorSpace];
1688                 if (!enteringFullScreen &&
1689                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1690                     nonFullscreenFrame = frame;
1692                 [self updateFullscreen];
1694                 if ([self isOrderedIn])
1695                 {
1696                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1697                        event.  The back end will ignore it if nothing actually changed. */
1698                     [self windowDidResize:nil];
1699                 }
1700             }
1701         }
1702     }
1704     - (NSRect) wine_fractionalFrame
1705     {
1706         NSRect frame = self.frame;
1707         if (NSEqualRects(frame, roundedWineFrame))
1708             frame = wineFrame;
1709         return frame;
1710     }
1712     - (void) setMacDrvParentWindow:(WineWindow*)parent
1713     {
1714         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1715         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1716         {
1717             [oldParent removeChildWineWindow:self];
1718             [latentParentWindow removeChildWineWindow:self];
1719             if ([parent addChildWineWindow:self])
1720                 [[WineApplicationController sharedController] adjustWindowLevels];
1721         }
1722     }
1724     - (void) setDisabled:(BOOL)newValue
1725     {
1726         if (disabled != newValue)
1727         {
1728             disabled = newValue;
1729             [self adjustFeaturesForState];
1730         }
1731     }
1733     - (BOOL) needsTransparency
1734     {
1735         return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1736                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1737     }
1739     - (void) checkTransparency
1740     {
1741         if (![self isOpaque] && !self.needsTransparency)
1742         {
1743             self.shapeChangedSinceLastDraw = TRUE;
1744             [[self contentView] setNeedsDisplay:YES];
1745             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1746             [self setOpaque:YES];
1747         }
1748         else if ([self isOpaque] && self.needsTransparency)
1749         {
1750             self.shapeChangedSinceLastDraw = TRUE;
1751             [[self contentView] setNeedsDisplay:YES];
1752             [self setBackgroundColor:[NSColor clearColor]];
1753             [self setOpaque:NO];
1754         }
1755     }
1757     - (void) setShape:(NSBezierPath*)newShape
1758     {
1759         if (shape == newShape) return;
1761         if (shape)
1762         {
1763             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1764             [shape release];
1765         }
1766         if (newShape)
1767             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1769         shape = [newShape copy];
1770         self.shapeChangedSinceLastDraw = TRUE;
1772         [self checkTransparency];
1773     }
1775     - (void) makeFocused:(BOOL)activate
1776     {
1777         if (activate)
1778         {
1779             [[WineApplicationController sharedController] transformProcessToForeground];
1780             [NSApp activateIgnoringOtherApps:YES];
1781         }
1783         causing_becomeKeyWindow = self;
1784         [self makeKeyWindow];
1785         causing_becomeKeyWindow = nil;
1787         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1788                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1789                                forWindow:self];
1790     }
1792     - (void) postKey:(uint16_t)keyCode
1793              pressed:(BOOL)pressed
1794            modifiers:(NSUInteger)modifiers
1795                event:(NSEvent*)theEvent
1796     {
1797         macdrv_event* event;
1798         CGEventRef cgevent;
1799         WineApplicationController* controller = [WineApplicationController sharedController];
1801         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1802         event->key.keycode   = keyCode;
1803         event->key.modifiers = modifiers;
1804         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1806         if ((cgevent = [theEvent CGEvent]))
1807         {
1808             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1809                                                         kCGKeyboardEventKeyboardType);
1810             if (keyboardType != controller.keyboardType)
1811             {
1812                 controller.keyboardType = keyboardType;
1813                 [controller keyboardSelectionDidChange];
1814             }
1815         }
1817         [queue postEvent:event];
1819         macdrv_release_event(event);
1821         [controller noteKey:keyCode pressed:pressed];
1822     }
1824     - (void) postKeyEvent:(NSEvent *)theEvent
1825     {
1826         [self flagsChanged:theEvent];
1827         [self postKey:[theEvent keyCode]
1828               pressed:[theEvent type] == NSKeyDown
1829             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1830                 event:theEvent];
1831     }
1833     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1834     {
1835         savedContentMinSize = minSize;
1836         savedContentMaxSize = maxSize;
1837         if (![self preventResizing])
1838         {
1839             [self setContentMinSize:minSize];
1840             [self setContentMaxSize:maxSize];
1841         }
1842     }
1844     - (WineWindow*) ancestorWineWindow
1845     {
1846         WineWindow* ancestor = self;
1847         for (;;)
1848         {
1849             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1850             if ([parent isKindOfClass:[WineWindow class]])
1851                 ancestor = parent;
1852             else
1853                 break;
1854         }
1855         return ancestor;
1856     }
1858     - (void) postBroughtForwardEvent
1859     {
1860         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1861         [queue postEvent:event];
1862         macdrv_release_event(event);
1863     }
1865     - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
1866     {
1867         macdrv_event* event;
1868         NSUInteger style = self.styleMask;
1870         if (isFullscreen)
1871             style |= NSFullScreenWindowMask;
1872         else
1873             style &= ~NSFullScreenWindowMask;
1874         frame = [[self class] contentRectForFrameRect:frame styleMask:style];
1875         [[WineApplicationController sharedController] flipRect:&frame];
1877         /* Coalesce events by discarding any previous ones still in the queue. */
1878         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1879                                forWindow:self];
1881         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1882         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
1883         event->window_frame_changed.fullscreen = isFullscreen;
1884         event->window_frame_changed.in_resize = resizing;
1885         [queue postEvent:event];
1886         macdrv_release_event(event);
1887     }
1889     - (void) updateForCursorClipping
1890     {
1891         [self adjustFeaturesForState];
1892     }
1894     - (void) endWindowDragging
1895     {
1896         if (draggingPhase)
1897         {
1898             if (draggingPhase == 3)
1899             {
1900                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
1901                 [queue postEvent:event];
1902                 macdrv_release_event(event);
1903             }
1905             draggingPhase = 0;
1906             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
1907         }
1908     }
1910     - (NSMutableDictionary*) displayIDToDisplayLinkMap
1911     {
1912         static NSMutableDictionary* displayIDToDisplayLinkMap;
1913         if (!displayIDToDisplayLinkMap)
1914         {
1915             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
1917             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
1918                                                               object:NSApp
1919                                                                queue:nil
1920                                                           usingBlock:^(NSNotification *note){
1921                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
1922                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
1923                 [badDisplayIDs minusSet:validDisplayIDs];
1924                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
1925             }];
1926         }
1927         return displayIDToDisplayLinkMap;
1928     }
1930     - (WineDisplayLink*) wineDisplayLink
1931     {
1932         if (!_lastDisplayID)
1933             return nil;
1935         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
1936         return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
1937     }
1939     - (void) checkWineDisplayLink
1940     {
1941         NSScreen* screen = self.screen;
1942         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
1943             screen = nil;
1944 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
1945         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
1946             screen = nil;
1947 #endif
1949         NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
1950         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
1951         if (displayID == _lastDisplayID)
1952             return;
1954         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
1956         if (_lastDisplayID)
1957         {
1958             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
1959             [link removeWindow:self];
1960         }
1961         if (displayID)
1962         {
1963             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
1964             if (!link)
1965             {
1966                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
1967                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
1968             }
1969             [link addWindow:self];
1970             [self displayIfNeeded];
1971         }
1972         _lastDisplayID = displayID;
1973     }
1975     - (BOOL) isEmptyShaped
1976     {
1977         return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
1978     }
1980     - (BOOL) canProvideSnapshot
1981     {
1982         return (self.windowNumber > 0 && ![self isEmptyShaped]);
1983     }
1985     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
1986     {
1987         if (![self isEmptyShaped])
1988             return;
1990         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
1991         if (!force && now < lastDockIconSnapshot + 1)
1992             return;
1994         if (window)
1995         {
1996             if (![window canProvideSnapshot])
1997                 return;
1998         }
1999         else
2000         {
2001             CGFloat bestArea;
2002             for (WineWindow* childWindow in self.childWindows)
2003             {
2004                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2005                     continue;
2007                 NSSize size = childWindow.frame.size;
2008                 CGFloat area = size.width * size.height;
2009                 if (!window || area > bestArea)
2010                 {
2011                     window = childWindow;
2012                     bestArea = area;
2013                 }
2014             }
2016             if (!window)
2017                 return;
2018         }
2020         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2021         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2022         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2023         CFRelease(windowIDs);
2024         if (!windowImage)
2025             return;
2027         NSImage* appImage = [NSApp applicationIconImage];
2028         if (!appImage)
2029             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2031         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2032         [dockIcon lockFocus];
2034         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2036         CGRect rect = CGRectMake(8, 8, 240, 240);
2037         size_t width = CGImageGetWidth(windowImage);
2038         size_t height = CGImageGetHeight(windowImage);
2039         if (width > height)
2040         {
2041             rect.size.height *= height / (double)width;
2042             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2043         }
2044         else if (width != height)
2045         {
2046             rect.size.width *= width / (double)height;
2047             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2048         }
2050         CGContextDrawImage(cgcontext, rect, windowImage);
2051         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2052                     fromRect:NSZeroRect
2053                    operation:NSCompositeSourceOver
2054                     fraction:1
2055               respectFlipped:YES
2056                        hints:nil];
2058         [dockIcon unlockFocus];
2060         CGImageRelease(windowImage);
2062         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2063         if (![imageView isKindOfClass:[NSImageView class]])
2064         {
2065             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2066             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2067             self.dockTile.contentView = imageView;
2068         }
2069         imageView.image = dockIcon;
2070         [self.dockTile display];
2071         lastDockIconSnapshot = now;
2072     }
2074     - (void) checkEmptyShaped
2075     {
2076         if (self.dockTile.contentView && ![self isEmptyShaped])
2077         {
2078             self.dockTile.contentView = nil;
2079             lastDockIconSnapshot = 0;
2080         }
2081         [self checkWineDisplayLink];
2082     }
2085     /*
2086      * ---------- NSWindow method overrides ----------
2087      */
2088     - (BOOL) canBecomeKeyWindow
2089     {
2090         if (causing_becomeKeyWindow == self) return YES;
2091         if (self.disabled || self.noActivate) return NO;
2092         return [self isKeyWindow];
2093     }
2095     - (BOOL) canBecomeMainWindow
2096     {
2097         return [self canBecomeKeyWindow];
2098     }
2100     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2101     {
2102         // If a window is sized to completely cover a screen, then it's in
2103         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2104         // it.
2105         NSArray* screens = [NSScreen screens];
2106         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2107         if (!screen_covered_by_rect(contentRect, screens) &&
2108             frame_intersects_screens(frameRect, screens))
2109             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2110         return frameRect;
2111     }
2113     // This private method of NSWindow is called as Cocoa reacts to the display
2114     // configuration changing.  Among other things, it adjusts the window's
2115     // frame based on how the screen(s) changed size.  That tells Wine that the
2116     // window has been moved.  We don't want that.  Rather, we want to make
2117     // sure that the WinAPI notion of the window position is maintained/
2118     // restored, possibly undoing or overriding Cocoa's adjustment.
2119     //
2120     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2121     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2122     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2123     // reassert its notion of the position.  That call won't get processed
2124     // until after this method returns, so it will override whatever this
2125     // method does to the window position.  It will also discard any pending
2126     // WINDOW_FRAME_CHANGED events.
2127     //
2128     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2129     // adjust the window's position due to a display change is to hook into
2130     // this private method.  This private method has remained stable from 10.6
2131     // through 10.11.  If it does change, the most likely thing is that it
2132     // will be removed and no longer called and this fix will simply stop
2133     // working.  The only real danger would be if Apple changed the return type
2134     // to a struct or floating-point type, which would change the calling
2135     // convention.
2136     - (id) _displayChanged
2137     {
2138         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2139         [queue postEvent:event];
2140         macdrv_release_event(event);
2142         return [super _displayChanged];
2143     }
2145     - (BOOL) isExcludedFromWindowsMenu
2146     {
2147         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2148     }
2150     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2151     {
2152         BOOL ret = [super validateMenuItem:menuItem];
2154         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2155             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2156         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2157             ret = NO;
2159         return ret;
2160     }
2162     /* We don't call this.  It's the action method of the items in the Window menu. */
2163     - (void) makeKeyAndOrderFront:(id)sender
2164     {
2165         if ([self isMiniaturized])
2166             [self deminiaturize:nil];
2167         [self orderBelow:nil orAbove:nil activate:NO];
2168         [[self ancestorWineWindow] postBroughtForwardEvent];
2170         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2171             [[WineApplicationController sharedController] windowGotFocus:self];
2172     }
2174     - (void) sendEvent:(NSEvent*)event
2175     {
2176         NSEventType type = event.type;
2178         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2179            interface control.  For example, Control-Tab switches focus among
2180            views.  We want to bypass that feature, so directly route key-down
2181            events to -keyDown:. */
2182         if (type == NSKeyDown)
2183             [[self firstResponder] keyDown:event];
2184         else
2185         {
2186             if (!draggingPhase && maximized && ![self isMovable] &&
2187                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2188                 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2189             {
2190                 NSRect titleBar = self.frame;
2191                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2192                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2193                 titleBar.origin.y = NSMaxY(contentRect);
2195                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2197                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2198                 {
2199                     static const NSWindowButton buttons[] = {
2200                         NSWindowCloseButton,
2201                         NSWindowMiniaturizeButton,
2202                         NSWindowZoomButton,
2203                         NSWindowFullScreenButton,
2204                     };
2205                     BOOL hitButton = NO;
2206                     int i;
2208                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2209                     {
2210                         NSButton* button;
2212                         if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2213                             continue;
2215                         button = [self standardWindowButton:buttons[i]];
2216                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2217                         {
2218                             hitButton = YES;
2219                             break;
2220                         }
2221                     }
2223                     if (!hitButton)
2224                     {
2225                         draggingPhase = 1;
2226                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2227                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2228                     }
2229                 }
2230             }
2231             else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2232             {
2233                 if ([self isMovable])
2234                 {
2235                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2236                     NSPoint newTopLeft = dragWindowStartPosition;
2238                     newTopLeft.x += point.x - dragStartPosition.x;
2239                     newTopLeft.y += point.y - dragStartPosition.y;
2241                     if (draggingPhase == 2)
2242                     {
2243                         macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2244                         [queue postEvent:event];
2245                         macdrv_release_event(event);
2247                         draggingPhase = 3;
2248                     }
2250                     [self setFrameTopLeftPoint:newTopLeft];
2251                 }
2252                 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2253                 {
2254                     macdrv_event* event;
2255                     NSRect frame = [self contentRectForFrameRect:self.frame];
2257                     [[WineApplicationController sharedController] flipRect:&frame];
2259                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2260                     event->window_restore_requested.keep_frame = TRUE;
2261                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2262                     [queue postEvent:event];
2263                     macdrv_release_event(event);
2265                     draggingPhase = 2;
2266                 }
2268                 if (type == NSLeftMouseUp)
2269                     [self endWindowDragging];
2270             }
2272             [super sendEvent:event];
2273         }
2274     }
2276     - (void) miniaturize:(id)sender
2277     {
2278         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2279         [queue postEvent:event];
2280         macdrv_release_event(event);
2282         WineWindow* parent = (WineWindow*)self.parentWindow;
2283         if ([parent isKindOfClass:[WineWindow class]])
2284             [parent grabDockIconSnapshotFromWindow:self force:YES];
2285     }
2287     - (void) toggleFullScreen:(id)sender
2288     {
2289         if (!self.disabled && !maximized)
2290             [super toggleFullScreen:sender];
2291     }
2293     - (void) setViewsNeedDisplay:(BOOL)value
2294     {
2295         if (value && ![self viewsNeedDisplay])
2296         {
2297             WineDisplayLink* link = [self wineDisplayLink];
2298             if (link)
2299             {
2300                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2301                 if (_lastDisplayTime + [link refreshPeriod] < now)
2302                     [self setAutodisplay:YES];
2303                 else
2304                 {
2305                     [link start];
2306                     _lastDisplayTime = now;
2307                 }
2308             }
2309         }
2310         [super setViewsNeedDisplay:value];
2311     }
2313     - (void) display
2314     {
2315         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2316         [super display];
2317         [self setAutodisplay:NO];
2318     }
2320     - (void) displayIfNeeded
2321     {
2322         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2323         [super displayIfNeeded];
2324         [self setAutodisplay:NO];
2325     }
2327     - (void) windowDidDrawContent
2328     {
2329         if (!drawnSinceShown)
2330         {
2331             drawnSinceShown = YES;
2332             dispatch_async(dispatch_get_main_queue(), ^{
2333                 [self checkTransparency];
2334             });
2335         }
2336     }
2338     - (NSArray*) childWineWindows
2339     {
2340         NSArray* childWindows = self.childWindows;
2341         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2342             return [child isKindOfClass:[WineWindow class]];
2343         }];
2344         return [childWindows objectsAtIndexes:indexes];
2345     }
2347     // We normally use the generic/calibrated RGB color space for the window,
2348     // rather than the device color space, to avoid expensive color conversion
2349     // which slows down drawing.  However, for windows displaying OpenGL, having
2350     // a different color space than the screen greatly reduces frame rates, often
2351     // limiting it to the display refresh rate.
2352     //
2353     // To avoid this, we switch back to the screen color space whenever the
2354     // window is covered by a view with an attached OpenGL context.
2355     - (void) updateColorSpace
2356     {
2357         NSRect contentRect = [[self contentView] frame];
2358         BOOL coveredByGLView = FALSE;
2359         WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2360         if ([view isKindOfClass:[WineContentView class]] && [view hasGLContext])
2361         {
2362             NSRect frame = [view convertRect:[view bounds] toView:nil];
2363             if (NSContainsRect(frame, contentRect))
2364                 coveredByGLView = TRUE;
2365         }
2367         if (coveredByGLView)
2368             [self setColorSpace:nil];
2369         else
2370             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2371     }
2373     - (void) updateForGLSubviews
2374     {
2375         [self updateColorSpace];
2376         if (gl_surface_mode == GL_SURFACE_BEHIND)
2377             [self checkTransparency];
2378     }
2380     - (void) setRetinaMode:(int)mode
2381     {
2382         NSRect frame;
2383         double scale = mode ? 0.5 : 2.0;
2384         NSAffineTransform* transform = [NSAffineTransform transform];
2386         [transform scaleBy:scale];
2388         if (shape)
2389             [shape transformUsingAffineTransform:transform];
2391         for (WineContentView* subview in [self.contentView subviews])
2392         {
2393             if ([subview isKindOfClass:[WineContentView class]])
2394                 [subview setRetinaMode:mode];
2395         }
2397         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2398         frame.origin.x *= scale;
2399         frame.origin.y *= scale;
2400         frame.size.width *= scale;
2401         frame.size.height *= scale;
2402         frame = [self frameRectForContentRect:frame];
2404         savedContentMinSize = [transform transformSize:savedContentMinSize];
2405         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2406             savedContentMaxSize.width *= scale;
2407         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2408             savedContentMaxSize.height *= scale;
2410         self.contentMinSize = [transform transformSize:self.contentMinSize];
2411         NSSize temp = self.contentMaxSize;
2412         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2413             temp.width *= scale;
2414         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2415             temp.height *= scale;
2416         self.contentMaxSize = temp;
2418         ignore_windowResize = TRUE;
2419         [self setFrameAndWineFrame:frame];
2420         ignore_windowResize = FALSE;
2421     }
2424     /*
2425      * ---------- NSResponder method overrides ----------
2426      */
2427     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
2429     - (void) flagsChanged:(NSEvent *)theEvent
2430     {
2431         static const struct {
2432             NSUInteger  mask;
2433             uint16_t    keycode;
2434         } modifiers[] = {
2435             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2436             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2437             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2438             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2439             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2440             { NX_DEVICELALTKEYMASK,     kVK_Option },
2441             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2442             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2443             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2444         };
2446         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2447         NSUInteger changed;
2448         int i, last_changed;
2450         fix_device_modifiers_by_generic(&modifierFlags);
2451         changed = modifierFlags ^ lastModifierFlags;
2453         last_changed = -1;
2454         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2455             if (changed & modifiers[i].mask)
2456                 last_changed = i;
2458         for (i = 0; i <= last_changed; i++)
2459         {
2460             if (changed & modifiers[i].mask)
2461             {
2462                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2464                 if (i == last_changed)
2465                     lastModifierFlags = modifierFlags;
2466                 else
2467                 {
2468                     lastModifierFlags ^= modifiers[i].mask;
2469                     fix_generic_modifiers_by_device(&lastModifierFlags);
2470                 }
2472                 // Caps lock generates one event for each press-release action.
2473                 // We need to simulate a pair of events for each actual event.
2474                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2475                 {
2476                     [self postKey:modifiers[i].keycode
2477                           pressed:TRUE
2478                         modifiers:lastModifierFlags
2479                             event:(NSEvent*)theEvent];
2480                     pressed = FALSE;
2481                 }
2483                 [self postKey:modifiers[i].keycode
2484                       pressed:pressed
2485                     modifiers:lastModifierFlags
2486                         event:(NSEvent*)theEvent];
2487             }
2488         }
2489     }
2491     - (void) applicationWillHide
2492     {
2493         savedVisibleState = [self isVisible];
2494     }
2496     - (void) applicationDidUnhide
2497     {
2498         if ([self isVisible])
2499             [self becameEligibleParentOrChild];
2500     }
2503     /*
2504      * ---------- NSWindowDelegate methods ----------
2505      */
2506     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2507     {
2508         macdrv_query* query;
2509         NSSize size;
2511         query = macdrv_create_query();
2512         query->type = QUERY_MIN_MAX_INFO;
2513         query->window = (macdrv_window)[self retain];
2514         [self.queue query:query timeout:0.5];
2515         macdrv_release_query(query);
2517         size = [self contentMaxSize];
2518         if (proposedSize.width < size.width)
2519             size.width = proposedSize.width;
2520         if (proposedSize.height < size.height)
2521             size.height = proposedSize.height;
2522         return size;
2523     }
2525     - (void)windowDidBecomeKey:(NSNotification *)notification
2526     {
2527         WineApplicationController* controller = [WineApplicationController sharedController];
2528         NSEvent* event = [controller lastFlagsChanged];
2529         if (event)
2530             [self flagsChanged:event];
2532         if (causing_becomeKeyWindow == self) return;
2534         [controller windowGotFocus:self];
2535     }
2537     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2538     {
2539         [self checkWineDisplayLink];
2540     }
2542     - (void) windowDidChangeScreen:(NSNotification*)notification
2543     {
2544         [self checkWineDisplayLink];
2545     }
2547     - (void)windowDidDeminiaturize:(NSNotification *)notification
2548     {
2549         WineApplicationController* controller = [WineApplicationController sharedController];
2551         if (!ignore_windowDeminiaturize)
2552             [self postDidUnminimizeEvent];
2553         ignore_windowDeminiaturize = FALSE;
2555         [self becameEligibleParentOrChild];
2557         if (fullscreen && [self isOnActiveSpace])
2558             [controller updateFullscreenWindows];
2559         [controller adjustWindowLevels];
2561         if (![self parentWindow])
2562             [self postBroughtForwardEvent];
2564         if (!self.disabled && !self.noActivate)
2565         {
2566             causing_becomeKeyWindow = self;
2567             [self makeKeyWindow];
2568             causing_becomeKeyWindow = nil;
2569             [controller windowGotFocus:self];
2570         }
2572         [self windowDidResize:notification];
2573         [self checkWineDisplayLink];
2574     }
2576     - (void) windowDidEndLiveResize:(NSNotification *)notification
2577     {
2578         if (!maximized)
2579         {
2580             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2581             [queue postEvent:event];
2582             macdrv_release_event(event);
2583         }
2584     }
2586     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2587     {
2588         enteringFullScreen = FALSE;
2589         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2590     }
2592     - (void) windowDidExitFullScreen:(NSNotification*)notification
2593     {
2594         exitingFullScreen = FALSE;
2595         [self setFrameAndWineFrame:nonFullscreenFrame];
2596         [self windowDidResize:nil];
2597     }
2599     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2600     {
2601         enteringFullScreen = FALSE;
2602         enteredFullScreenTime = 0;
2603     }
2605     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2606     {
2607         exitingFullScreen = FALSE;
2608         [self windowDidResize:nil];
2609     }
2611     - (void)windowDidMiniaturize:(NSNotification *)notification
2612     {
2613         if (fullscreen && [self isOnActiveSpace])
2614             [[WineApplicationController sharedController] updateFullscreenWindows];
2615         [self checkWineDisplayLink];
2616     }
2618     - (void)windowDidMove:(NSNotification *)notification
2619     {
2620         [self windowDidResize:notification];
2621     }
2623     - (void)windowDidResignKey:(NSNotification *)notification
2624     {
2625         macdrv_event* event;
2627         if (causing_becomeKeyWindow) return;
2629         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2630         [queue postEvent:event];
2631         macdrv_release_event(event);
2632     }
2634     - (void)windowDidResize:(NSNotification *)notification
2635     {
2636         NSRect frame = self.wine_fractionalFrame;
2638         if ([self inLiveResize])
2639         {
2640             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2641                 resizingFromLeft = TRUE;
2642             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2643                 resizingFromTop = TRUE;
2644         }
2646         if (ignore_windowResize || exitingFullScreen) return;
2648         if ([self preventResizing])
2649         {
2650             NSRect contentRect = [self contentRectForFrameRect:frame];
2651             [self setContentMinSize:contentRect.size];
2652             [self setContentMaxSize:contentRect.size];
2653         }
2655         [self postWindowFrameChanged:frame
2656                           fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2657                             resizing:[self inLiveResize]];
2659         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2660         [self updateFullscreen];
2661     }
2663     - (BOOL)windowShouldClose:(id)sender
2664     {
2665         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2666         [queue postEvent:event];
2667         macdrv_release_event(event);
2668         return NO;
2669     }
2671     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2672     {
2673         if (maximized)
2674         {
2675             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2676             [queue postEvent:event];
2677             macdrv_release_event(event);
2678             return NO;
2679         }
2680         else if (!resizable)
2681         {
2682             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2683             [queue postEvent:event];
2684             macdrv_release_event(event);
2685             return NO;
2686         }
2688         return YES;
2689     }
2691     - (void) windowWillClose:(NSNotification*)notification
2692     {
2693         WineWindow* child;
2695         if (fakingClose) return;
2696         if (latentParentWindow)
2697         {
2698             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2699             self.latentParentWindow = nil;
2700         }
2702         for (child in latentChildWindows)
2703         {
2704             if (child.latentParentWindow == self)
2705                 child.latentParentWindow = nil;
2706         }
2707         [latentChildWindows removeAllObjects];
2708     }
2710     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2711     {
2712         enteringFullScreen = TRUE;
2713         nonFullscreenFrame = self.wine_fractionalFrame;
2714     }
2716     - (void) windowWillExitFullScreen:(NSNotification*)notification
2717     {
2718         exitingFullScreen = TRUE;
2719         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2720     }
2722     - (void)windowWillMiniaturize:(NSNotification *)notification
2723     {
2724         [self becameIneligibleParentOrChild];
2725         [self grabDockIconSnapshotFromWindow:nil force:NO];
2726     }
2728     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2729     {
2730         if ([self inLiveResize])
2731         {
2732             if (maximized)
2733                 return self.wine_fractionalFrame.size;
2735             NSRect rect;
2736             macdrv_query* query;
2738             rect = [self frame];
2739             if (resizingFromLeft)
2740                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2741             if (!resizingFromTop)
2742                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2743             rect.size = frameSize;
2744             rect = [self contentRectForFrameRect:rect];
2745             [[WineApplicationController sharedController] flipRect:&rect];
2747             query = macdrv_create_query();
2748             query->type = QUERY_RESIZE_SIZE;
2749             query->window = (macdrv_window)[self retain];
2750             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2751             query->resize_size.from_left = resizingFromLeft;
2752             query->resize_size.from_top = resizingFromTop;
2754             if ([self.queue query:query timeout:0.1])
2755             {
2756                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2757                 rect = [self frameRectForContentRect:rect];
2758                 frameSize = rect.size;
2759             }
2761             macdrv_release_query(query);
2762         }
2764         return frameSize;
2765     }
2767     - (void) windowWillStartLiveResize:(NSNotification *)notification
2768     {
2769         [self endWindowDragging];
2771         if (maximized)
2772         {
2773             macdrv_event* event;
2774             NSRect frame = [self contentRectForFrameRect:self.frame];
2776             [[WineApplicationController sharedController] flipRect:&frame];
2778             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2779             event->window_restore_requested.keep_frame = TRUE;
2780             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2781             [queue postEvent:event];
2782             macdrv_release_event(event);
2783         }
2784         else
2785             [self sendResizeStartQuery];
2787         frameAtResizeStart = [self frame];
2788         resizingFromLeft = resizingFromTop = FALSE;
2789     }
2791     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2792     {
2793         macdrv_query* query;
2794         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2795         NSSize maxSize;
2797         query = macdrv_create_query();
2798         query->type = QUERY_MIN_MAX_INFO;
2799         query->window = (macdrv_window)[self retain];
2800         [self.queue query:query timeout:0.5];
2801         macdrv_release_query(query);
2803         currentContentRect = [self contentRectForFrameRect:[self frame]];
2804         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2806         maxSize = [self contentMaxSize];
2807         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2808         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2810         // Try to keep the top-left corner where it is.
2811         newContentRect.origin.x = NSMinX(currentContentRect);
2812         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2814         // If that pushes the bottom or right off the screen, pull it up and to the left.
2815         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2816         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2817             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2818         if (NSMinY(newContentRect) < NSMinY(screenRect))
2819             newContentRect.origin.y = NSMinY(screenRect);
2821         // If that pushes the top or left off the screen, push it down and the right
2822         // again.  Do this last because the top-left corner is more important than the
2823         // bottom-right.
2824         if (NSMinX(newContentRect) < NSMinX(screenRect))
2825             newContentRect.origin.x = NSMinX(screenRect);
2826         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2827             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2829         return [self frameRectForContentRect:newContentRect];
2830     }
2833     /*
2834      * ---------- NSPasteboardOwner methods ----------
2835      */
2836     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2837     {
2838         macdrv_query* query = macdrv_create_query();
2839         query->type = QUERY_PASTEBOARD_DATA;
2840         query->window = (macdrv_window)[self retain];
2841         query->pasteboard_data.type = (CFStringRef)[type copy];
2843         [self.queue query:query timeout:3];
2844         macdrv_release_query(query);
2845     }
2847     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
2848     {
2849         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
2850         [queue postEvent:event];
2851         macdrv_release_event(event);
2852     }
2855     /*
2856      * ---------- NSDraggingDestination methods ----------
2857      */
2858     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2859     {
2860         return [self draggingUpdated:sender];
2861     }
2863     - (void) draggingExited:(id <NSDraggingInfo>)sender
2864     {
2865         // This isn't really a query.  We don't need any response.  However, it
2866         // has to be processed in a similar manner as the other drag-and-drop
2867         // queries in order to maintain the proper order of operations.
2868         macdrv_query* query = macdrv_create_query();
2869         query->type = QUERY_DRAG_EXITED;
2870         query->window = (macdrv_window)[self retain];
2872         [self.queue query:query timeout:0.1];
2873         macdrv_release_query(query);
2874     }
2876     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2877     {
2878         NSDragOperation ret;
2879         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2880         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
2881         NSPasteboard* pb = [sender draggingPasteboard];
2883         macdrv_query* query = macdrv_create_query();
2884         query->type = QUERY_DRAG_OPERATION;
2885         query->window = (macdrv_window)[self retain];
2886         query->drag_operation.x = floor(cgpt.x);
2887         query->drag_operation.y = floor(cgpt.y);
2888         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2889         query->drag_operation.accepted_op = NSDragOperationNone;
2890         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2892         [self.queue query:query timeout:3];
2893         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2894         macdrv_release_query(query);
2896         return ret;
2897     }
2899     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2900     {
2901         BOOL ret;
2902         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2903         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
2904         NSPasteboard* pb = [sender draggingPasteboard];
2906         macdrv_query* query = macdrv_create_query();
2907         query->type = QUERY_DRAG_DROP;
2908         query->window = (macdrv_window)[self retain];
2909         query->drag_drop.x = floor(cgpt.x);
2910         query->drag_drop.y = floor(cgpt.y);
2911         query->drag_drop.op = [sender draggingSourceOperationMask];
2912         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2914         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
2915         ret = query->status;
2916         macdrv_release_query(query);
2918         return ret;
2919     }
2921     - (BOOL) wantsPeriodicDraggingUpdates
2922     {
2923         return NO;
2924     }
2926 @end
2929 /***********************************************************************
2930  *              macdrv_create_cocoa_window
2932  * Create a Cocoa window with the given content frame and features (e.g.
2933  * title bar, close box, etc.).
2934  */
2935 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2936         CGRect frame, void* hwnd, macdrv_event_queue queue)
2938     __block WineWindow* window;
2940     OnMainThread(^{
2941         window = [[WineWindow createWindowWithFeatures:wf
2942                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
2943                                                   hwnd:hwnd
2944                                                  queue:(WineEventQueue*)queue] retain];
2945     });
2947     return (macdrv_window)window;
2950 /***********************************************************************
2951  *              macdrv_destroy_cocoa_window
2953  * Destroy a Cocoa window.
2954  */
2955 void macdrv_destroy_cocoa_window(macdrv_window w)
2957     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2958     WineWindow* window = (WineWindow*)w;
2960     OnMainThread(^{
2961         [window doOrderOut];
2962         [window close];
2963     });
2964     [window.queue discardEventsMatchingMask:-1 forWindow:window];
2965     [window release];
2967     [pool release];
2970 /***********************************************************************
2971  *              macdrv_get_window_hwnd
2973  * Get the hwnd that was set for the window at creation.
2974  */
2975 void* macdrv_get_window_hwnd(macdrv_window w)
2977     WineWindow* window = (WineWindow*)w;
2978     return window.hwnd;
2981 /***********************************************************************
2982  *              macdrv_set_cocoa_window_features
2984  * Update a Cocoa window's features.
2985  */
2986 void macdrv_set_cocoa_window_features(macdrv_window w,
2987         const struct macdrv_window_features* wf)
2989     WineWindow* window = (WineWindow*)w;
2991     OnMainThread(^{
2992         [window setWindowFeatures:wf];
2993     });
2996 /***********************************************************************
2997  *              macdrv_set_cocoa_window_state
2999  * Update a Cocoa window's state.
3000  */
3001 void macdrv_set_cocoa_window_state(macdrv_window w,
3002         const struct macdrv_window_state* state)
3004     WineWindow* window = (WineWindow*)w;
3006     OnMainThread(^{
3007         [window setMacDrvState:state];
3008     });
3011 /***********************************************************************
3012  *              macdrv_set_cocoa_window_title
3014  * Set a Cocoa window's title.
3015  */
3016 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3017         size_t length)
3019     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3020     WineWindow* window = (WineWindow*)w;
3021     NSString* titleString;
3023     if (title)
3024         titleString = [NSString stringWithCharacters:title length:length];
3025     else
3026         titleString = @"";
3027     OnMainThreadAsync(^{
3028         [window setTitle:titleString];
3029         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3030             [NSApp changeWindowsItem:window title:titleString filename:NO];
3031     });
3033     [pool release];
3036 /***********************************************************************
3037  *              macdrv_order_cocoa_window
3039  * Reorder a Cocoa window relative to other windows.  If prev is
3040  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3041  * it is ordered above that window.  Otherwise, it is ordered to the
3042  * front.
3043  */
3044 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3045         macdrv_window n, int activate)
3047     WineWindow* window = (WineWindow*)w;
3048     WineWindow* prev = (WineWindow*)p;
3049     WineWindow* next = (WineWindow*)n;
3051     OnMainThreadAsync(^{
3052         [window orderBelow:prev
3053                    orAbove:next
3054                   activate:activate];
3055     });
3056     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3057                                   forWindow:window];
3058     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3059                                 forWindow:next];
3062 /***********************************************************************
3063  *              macdrv_hide_cocoa_window
3065  * Hides a Cocoa window.
3066  */
3067 void macdrv_hide_cocoa_window(macdrv_window w)
3069     WineWindow* window = (WineWindow*)w;
3071     OnMainThread(^{
3072         [window doOrderOut];
3073     });
3076 /***********************************************************************
3077  *              macdrv_set_cocoa_window_frame
3079  * Move a Cocoa window.
3080  */
3081 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3083     WineWindow* window = (WineWindow*)w;
3085     OnMainThread(^{
3086         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3087     });
3090 /***********************************************************************
3091  *              macdrv_get_cocoa_window_frame
3093  * Gets the frame of a Cocoa window.
3094  */
3095 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3097     WineWindow* window = (WineWindow*)w;
3099     OnMainThread(^{
3100         NSRect frame;
3102         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3103         [[WineApplicationController sharedController] flipRect:&frame];
3104         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3105     });
3108 /***********************************************************************
3109  *              macdrv_set_cocoa_parent_window
3111  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3112  * the parent window.
3113  */
3114 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3116     WineWindow* window = (WineWindow*)w;
3118     OnMainThread(^{
3119         [window setMacDrvParentWindow:(WineWindow*)parent];
3120     });
3123 /***********************************************************************
3124  *              macdrv_set_window_surface
3125  */
3126 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3128     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3129     WineWindow* window = (WineWindow*)w;
3131     OnMainThread(^{
3132         window.surface = surface;
3133         window.surface_mutex = mutex;
3134     });
3136     [pool release];
3139 /***********************************************************************
3140  *              macdrv_window_needs_display
3142  * Mark a window as needing display in a specified rect (in non-client
3143  * area coordinates).
3144  */
3145 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3147     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3148     WineWindow* window = (WineWindow*)w;
3150     OnMainThreadAsync(^{
3151         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3152     });
3154     [pool release];
3157 /***********************************************************************
3158  *              macdrv_set_window_shape
3160  * Sets the shape of a Cocoa window from an array of rectangles.  If
3161  * rects is NULL, resets the window's shape to its frame.
3162  */
3163 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3165     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3166     WineWindow* window = (WineWindow*)w;
3168     OnMainThread(^{
3169         if (!rects || !count)
3170         {
3171             window.shape = nil;
3172             window.shapeData = nil;
3173             [window checkEmptyShaped];
3174         }
3175         else
3176         {
3177             size_t length = sizeof(*rects) * count;
3178             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3179             {
3180                 NSBezierPath* path;
3181                 unsigned int i;
3183                 path = [NSBezierPath bezierPath];
3184                 for (i = 0; i < count; i++)
3185                     [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3186                 window.shape = path;
3187                 window.shapeData = [NSData dataWithBytes:rects length:length];
3188                 [window checkEmptyShaped];
3189             }
3190         }
3191     });
3193     [pool release];
3196 /***********************************************************************
3197  *              macdrv_set_window_alpha
3198  */
3199 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3201     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3202     WineWindow* window = (WineWindow*)w;
3204     [window setAlphaValue:alpha];
3206     [pool release];
3209 /***********************************************************************
3210  *              macdrv_set_window_color_key
3211  */
3212 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3213                                  CGFloat keyBlue)
3215     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3216     WineWindow* window = (WineWindow*)w;
3218     OnMainThread(^{
3219         window.colorKeyed       = TRUE;
3220         window.colorKeyRed      = keyRed;
3221         window.colorKeyGreen    = keyGreen;
3222         window.colorKeyBlue     = keyBlue;
3223         [window checkTransparency];
3224     });
3226     [pool release];
3229 /***********************************************************************
3230  *              macdrv_clear_window_color_key
3231  */
3232 void macdrv_clear_window_color_key(macdrv_window w)
3234     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3235     WineWindow* window = (WineWindow*)w;
3237     OnMainThread(^{
3238         window.colorKeyed = FALSE;
3239         [window checkTransparency];
3240     });
3242     [pool release];
3245 /***********************************************************************
3246  *              macdrv_window_use_per_pixel_alpha
3247  */
3248 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3250     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3251     WineWindow* window = (WineWindow*)w;
3253     OnMainThread(^{
3254         window.usePerPixelAlpha = use_per_pixel_alpha;
3255         [window checkTransparency];
3256     });
3258     [pool release];
3261 /***********************************************************************
3262  *              macdrv_give_cocoa_window_focus
3264  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3265  * orders it front and, if its frame was not within the desktop bounds,
3266  * Cocoa will typically move it on-screen.
3267  */
3268 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3270     WineWindow* window = (WineWindow*)w;
3272     OnMainThread(^{
3273         [window makeFocused:activate];
3274     });
3277 /***********************************************************************
3278  *              macdrv_set_window_min_max_sizes
3280  * Sets the window's minimum and maximum content sizes.
3281  */
3282 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3284     WineWindow* window = (WineWindow*)w;
3286     OnMainThread(^{
3287         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3288     });
3291 /***********************************************************************
3292  *              macdrv_create_view
3294  * Creates and returns a view with the specified frame rect.  The
3295  * caller is responsible for calling macdrv_dispose_view() on the view
3296  * when it is done with it.
3297  */
3298 macdrv_view macdrv_create_view(CGRect rect)
3300     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3301     __block WineContentView* view;
3303     if (CGRectIsNull(rect)) rect = CGRectZero;
3305     OnMainThread(^{
3306         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3308         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3309         [view setAutoresizesSubviews:NO];
3310         [view setHidden:YES];
3311         [nc addObserver:view
3312                selector:@selector(updateGLContexts)
3313                    name:NSViewGlobalFrameDidChangeNotification
3314                  object:view];
3315         [nc addObserver:view
3316                selector:@selector(updateGLContexts)
3317                    name:NSApplicationDidChangeScreenParametersNotification
3318                  object:NSApp];
3319     });
3321     [pool release];
3322     return (macdrv_view)view;
3325 /***********************************************************************
3326  *              macdrv_dispose_view
3328  * Destroys a view previously returned by macdrv_create_view.
3329  */
3330 void macdrv_dispose_view(macdrv_view v)
3332     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3333     WineContentView* view = (WineContentView*)v;
3335     OnMainThread(^{
3336         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3337         WineWindow* window = (WineWindow*)[view window];
3339         [nc removeObserver:view
3340                       name:NSViewGlobalFrameDidChangeNotification
3341                     object:view];
3342         [nc removeObserver:view
3343                       name:NSApplicationDidChangeScreenParametersNotification
3344                     object:NSApp];
3345         [view removeFromSuperview];
3346         [view release];
3347         [window updateForGLSubviews];
3348     });
3350     [pool release];
3353 /***********************************************************************
3354  *              macdrv_set_view_frame
3355  */
3356 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3358     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3359     WineContentView* view = (WineContentView*)v;
3361     if (CGRectIsNull(rect)) rect = CGRectZero;
3363     OnMainThreadAsync(^{
3364         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3365         NSRect oldFrame = [view frame];
3367         if (!NSEqualRects(oldFrame, newFrame))
3368         {
3369             [[view superview] setNeedsDisplayInRect:oldFrame];
3370             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3371                 [view setFrameSize:newFrame.size];
3372             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3373                 [view setFrameOrigin:newFrame.origin];
3374             else
3375                 [view setFrame:newFrame];
3376             [view setNeedsDisplay:YES];
3378             if (retina_enabled)
3379             {
3380                 int backing_size[2] = { 0 };
3381                 [view wine_setBackingSize:backing_size];
3382             }
3383             [(WineWindow*)[view window] updateForGLSubviews];
3384         }
3385     });
3387     [pool release];
3390 /***********************************************************************
3391  *              macdrv_set_view_superview
3393  * Move a view to a new superview and position it relative to its
3394  * siblings.  If p is non-NULL, the view is ordered behind it.
3395  * Otherwise, the view is ordered above n.  If s is NULL, use the
3396  * content view of w as the new superview.
3397  */
3398 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3400     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3401     WineContentView* view = (WineContentView*)v;
3402     WineContentView* superview = (WineContentView*)s;
3403     WineWindow* window = (WineWindow*)w;
3404     WineContentView* prev = (WineContentView*)p;
3405     WineContentView* next = (WineContentView*)n;
3407     if (!superview)
3408         superview = [window contentView];
3410     OnMainThreadAsync(^{
3411         if (superview == [view superview])
3412         {
3413             NSArray* subviews = [superview subviews];
3414             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3415             if (!prev && !next && index == [subviews count] - 1)
3416                 return;
3417             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3418                 return;
3419             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3420                 return;
3421         }
3423         WineWindow* oldWindow = (WineWindow*)[view window];
3424         WineWindow* newWindow = (WineWindow*)[superview window];
3426 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3427         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3428             [view removeFromSuperview];
3429 #endif
3430         if (prev)
3431             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3432         else
3433             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3435         if (oldWindow != newWindow)
3436         {
3437             [oldWindow updateForGLSubviews];
3438             [newWindow updateForGLSubviews];
3439         }
3440     });
3442     [pool release];
3445 /***********************************************************************
3446  *              macdrv_set_view_hidden
3447  */
3448 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3450     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3451     WineContentView* view = (WineContentView*)v;
3453     OnMainThreadAsync(^{
3454         [view setHidden:hidden];
3455         [(WineWindow*)view.window updateForGLSubviews];
3456     });
3458     [pool release];
3461 /***********************************************************************
3462  *              macdrv_add_view_opengl_context
3464  * Add an OpenGL context to the list being tracked for each view.
3465  */
3466 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3468     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3469     WineContentView* view = (WineContentView*)v;
3470     WineOpenGLContext *context = (WineOpenGLContext*)c;
3472     OnMainThread(^{
3473         [view addGLContext:context];
3474     });
3476     [pool release];
3479 /***********************************************************************
3480  *              macdrv_remove_view_opengl_context
3482  * Add an OpenGL context to the list being tracked for each view.
3483  */
3484 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3486     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3487     WineContentView* view = (WineContentView*)v;
3488     WineOpenGLContext *context = (WineOpenGLContext*)c;
3490     OnMainThreadAsync(^{
3491         [view removeGLContext:context];
3492     });
3494     [pool release];
3497 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3499     WineContentView* view = (WineContentView*)v;
3501     if (![view isKindOfClass:[WineContentView class]])
3502         return FALSE;
3504     [view wine_getBackingSize:backing_size];
3505     return TRUE;
3508 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3510     WineContentView* view = (WineContentView*)v;
3512     if ([view isKindOfClass:[WineContentView class]])
3513         [view wine_setBackingSize:backing_size];
3516 /***********************************************************************
3517  *              macdrv_window_background_color
3519  * Returns the standard Mac window background color as a 32-bit value of
3520  * the form 0x00rrggbb.
3521  */
3522 uint32_t macdrv_window_background_color(void)
3524     static uint32_t result;
3525     static dispatch_once_t once;
3527     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3528     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3529     // of it is to draw with it.
3530     dispatch_once(&once, ^{
3531         OnMainThread(^{
3532             unsigned char rgbx[4];
3533             unsigned char *planes = rgbx;
3534             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3535                                                                                pixelsWide:1
3536                                                                                pixelsHigh:1
3537                                                                             bitsPerSample:8
3538                                                                           samplesPerPixel:3
3539                                                                                  hasAlpha:NO
3540                                                                                  isPlanar:NO
3541                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3542                                                                              bitmapFormat:0
3543                                                                               bytesPerRow:4
3544                                                                              bitsPerPixel:32];
3545             [NSGraphicsContext saveGraphicsState];
3546             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3547             [[NSColor windowBackgroundColor] set];
3548             NSRectFill(NSMakeRect(0, 0, 1, 1));
3549             [NSGraphicsContext restoreGraphicsState];
3550             [bitmap release];
3551             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3552         });
3553     });
3555     return result;
3558 /***********************************************************************
3559  *              macdrv_send_text_input_event
3560  */
3561 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3563     OnMainThreadAsync(^{
3564         BOOL ret;
3565         macdrv_event* event;
3566         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3567         if (![window isKindOfClass:[WineWindow class]])
3568         {
3569             window = (WineWindow*)[NSApp mainWindow];
3570             if (![window isKindOfClass:[WineWindow class]])
3571                 window = [[WineApplicationController sharedController] frontWineWindow];
3572         }
3574         if (window)
3575         {
3576             NSUInteger localFlags = flags;
3577             CGEventRef c;
3578             NSEvent* event;
3580             window.imeData = data;
3581             fix_device_modifiers_by_generic(&localFlags);
3583             // An NSEvent created with +keyEventWithType:... is internally marked
3584             // as synthetic and doesn't get sent through input methods.  But one
3585             // created from a CGEvent doesn't have that problem.
3586             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3587             CGEventSetFlags(c, localFlags);
3588             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3589             event = [NSEvent eventWithCGEvent:c];
3590             CFRelease(c);
3592             window.commandDone = FALSE;
3593             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3594         }
3595         else
3596             ret = FALSE;
3598         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3599         event->sent_text_input.handled = ret;
3600         event->sent_text_input.done = done;
3601         [[window queue] postEvent:event];
3602         macdrv_release_event(event);
3603     });