comctl32: Fix a typo in comment.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blob991db57f4f6175e8d951a7a83fc1821e14f41c9f
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;
170     NSTimeInterval _lastDisplayTime;
173     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
175     - (void) addWindow:(WineWindow*)window;
176     - (void) removeWindow:(WineWindow*)window;
178     - (NSTimeInterval) refreshPeriod;
180     - (void) start;
182 @end
184 @implementation WineDisplayLink
186 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
188     - (id) initWithDisplayID:(CGDirectDisplayID)displayID
189     {
190         self = [super init];
191         if (self)
192         {
193             CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
194             if (status == kCVReturnSuccess && !_link)
195                 status = kCVReturnError;
196             if (status == kCVReturnSuccess)
197                 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
198             if (status != kCVReturnSuccess)
199             {
200                 [self release];
201                 return nil;
202             }
204             _displayID = displayID;
205             _windows = [[NSMutableSet alloc] init];
206         }
207         return self;
208     }
210     - (void) dealloc
211     {
212         if (_link)
213         {
214             CVDisplayLinkStop(_link);
215             CVDisplayLinkRelease(_link);
216         }
217         [_windows release];
218         [super dealloc];
219     }
221     - (void) addWindow:(WineWindow*)window
222     {
223         BOOL firstWindow;
224         @synchronized(self) {
225             firstWindow = !_windows.count;
226             [_windows addObject:window];
227         }
228         if (firstWindow || !CVDisplayLinkIsRunning(_link))
229             [self start];
230     }
232     - (void) removeWindow:(WineWindow*)window
233     {
234         BOOL lastWindow = FALSE;
235         @synchronized(self) {
236             BOOL hadWindows = _windows.count > 0;
237             [_windows removeObject:window];
238             if (hadWindows && !_windows.count)
239                 lastWindow = TRUE;
240         }
241         if (lastWindow && CVDisplayLinkIsRunning(_link))
242             CVDisplayLinkStop(_link);
243     }
245     - (void) fire
246     {
247         NSSet* windows;
248         @synchronized(self) {
249             windows = [_windows copy];
250         }
251         dispatch_async(dispatch_get_main_queue(), ^{
252             BOOL anyDisplayed = FALSE;
253             for (WineWindow* window in windows)
254             {
255                 if ([window viewsNeedDisplay])
256                 {
257                     [window displayIfNeeded];
258                     anyDisplayed = YES;
259                 }
260             }
262             NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
263             if (anyDisplayed)
264                 _lastDisplayTime = now;
265             else if (_lastDisplayTime + 2.0 < now)
266                 CVDisplayLinkStop(_link);
267         });
268         [windows release];
269     }
271     - (NSTimeInterval) refreshPeriod
272     {
273         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
274             return _actualRefreshPeriod;
276         if (_nominalRefreshPeriod)
277             return _nominalRefreshPeriod;
279         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
280         if (time.flags & kCVTimeIsIndefinite)
281             return 1.0 / 60.0;
282         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
283         return _nominalRefreshPeriod;
284     }
286     - (void) start
287     {
288         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
289         CVDisplayLinkStart(_link);
290     }
292 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
294     WineDisplayLink* link = displayLinkContext;
295     [link fire];
296     return kCVReturnSuccess;
299 @end
302 @interface WineContentView : NSView <NSTextInputClient>
304     NSMutableArray* glContexts;
305     NSMutableArray* pendingGlContexts;
306     BOOL _everHadGLContext;
307     BOOL _cachedHasGLDescendant;
308     BOOL _cachedHasGLDescendantValid;
309     BOOL clearedGlSurface;
311     NSMutableAttributedString* markedText;
312     NSRange markedTextSelection;
314     int backingSize[2];
317 @property (readonly, nonatomic) BOOL everHadGLContext;
319     - (void) addGLContext:(WineOpenGLContext*)context;
320     - (void) removeGLContext:(WineOpenGLContext*)context;
321     - (void) updateGLContexts;
323     - (void) wine_getBackingSize:(int*)outBackingSize;
324     - (void) wine_setBackingSize:(const int*)newBackingSize;
326 @end
329 @interface WineWindow ()
331 @property (readwrite, nonatomic) BOOL disabled;
332 @property (readwrite, nonatomic) BOOL noActivate;
333 @property (readwrite, nonatomic) BOOL floating;
334 @property (readwrite, nonatomic) BOOL drawnSinceShown;
335 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
336 @property (retain, nonatomic) NSWindow* latentParentWindow;
338 @property (nonatomic) void* hwnd;
339 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
341 @property (nonatomic) void* surface;
342 @property (nonatomic) pthread_mutex_t* surface_mutex;
344 @property (copy, nonatomic) NSBezierPath* shape;
345 @property (copy, nonatomic) NSData* shapeData;
346 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
347 @property (readonly, nonatomic) BOOL needsTransparency;
349 @property (nonatomic) BOOL colorKeyed;
350 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
351 @property (nonatomic) BOOL usePerPixelAlpha;
353 @property (assign, nonatomic) void* imeData;
354 @property (nonatomic) BOOL commandDone;
356 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
358     - (void) updateColorSpace;
359     - (void) updateForGLSubviews;
361     - (BOOL) becameEligibleParentOrChild;
362     - (void) becameIneligibleChild;
364     - (void) windowDidDrawContent;
366 @end
369 @implementation WineContentView
371 @synthesize everHadGLContext = _everHadGLContext;
373     - (void) dealloc
374     {
375         [markedText release];
376         [glContexts release];
377         [pendingGlContexts release];
378         [super dealloc];
379     }
381     - (BOOL) isFlipped
382     {
383         return YES;
384     }
386     - (void) drawRect:(NSRect)rect
387     {
388         WineWindow* window = (WineWindow*)[self window];
390         for (WineOpenGLContext* context in pendingGlContexts)
391         {
392             if (!clearedGlSurface)
393             {
394                 context.shouldClearToBlack = TRUE;
395                 clearedGlSurface = TRUE;
396             }
397             context.needsUpdate = TRUE;
398         }
399         [glContexts addObjectsFromArray:pendingGlContexts];
400         [pendingGlContexts removeAllObjects];
402         if ([window contentView] != self)
403             return;
405         if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
406         {
407             [[NSColor clearColor] setFill];
408             NSRectFill(rect);
410             [window.shape addClip];
412             [[NSColor windowBackgroundColor] setFill];
413             NSRectFill(rect);
414         }
416         if (window.surface && window.surface_mutex &&
417             !pthread_mutex_lock(window.surface_mutex))
418         {
419             const CGRect* rects;
420             int count;
422             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
423             {
424                 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
425                 CGContextRef context;
426                 int i;
428                 [window.shape addClip];
430                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
431                 CGContextSetBlendMode(context, kCGBlendModeCopy);
432                 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
434                 for (i = 0; i < count; i++)
435                 {
436                     CGRect imageRect;
437                     CGImageRef image;
439                     imageRect = CGRectIntersection(rects[i], dirtyRect);
440                     image = create_surface_image(window.surface, &imageRect, FALSE);
442                     if (image)
443                     {
444                         if (window.colorKeyed)
445                         {
446                             CGImageRef maskedImage;
447                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
448                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
449                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
450                             maskedImage = CGImageCreateWithMaskingColors(image, components);
451                             if (maskedImage)
452                             {
453                                 CGImageRelease(image);
454                                 image = maskedImage;
455                             }
456                         }
458                         CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
460                         CGImageRelease(image);
461                     }
462                 }
464                 [window windowDidDrawContent];
465             }
467             pthread_mutex_unlock(window.surface_mutex);
468         }
470         // If the window may be transparent, then we have to invalidate the
471         // shadow every time we draw.  Also, if this is the first time we've
472         // drawn since changing from transparent to opaque.
473         if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
474         {
475             window.shapeChangedSinceLastDraw = FALSE;
476             [window invalidateShadow];
477         }
478     }
480     - (void) addGLContext:(WineOpenGLContext*)context
481     {
482         BOOL hadContext = _everHadGLContext;
483         if (!glContexts)
484             glContexts = [[NSMutableArray alloc] init];
485         if (!pendingGlContexts)
486             pendingGlContexts = [[NSMutableArray alloc] init];
488         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
489         {
490             [glContexts addObject:context];
491             if (!clearedGlSurface)
492             {
493                 context.shouldClearToBlack = TRUE;
494                 clearedGlSurface = TRUE;
495             }
496             context.needsUpdate = TRUE;
497         }
498         else
499         {
500             [pendingGlContexts addObject:context];
501             [self setNeedsDisplay:YES];
502         }
504         _everHadGLContext = YES;
505         if (!hadContext)
506             [self invalidateHasGLDescendant];
507         [(WineWindow*)[self window] updateForGLSubviews];
508     }
510     - (void) removeGLContext:(WineOpenGLContext*)context
511     {
512         [glContexts removeObjectIdenticalTo:context];
513         [pendingGlContexts removeObjectIdenticalTo:context];
514         [(WineWindow*)[self window] updateForGLSubviews];
515     }
517     - (void) updateGLContexts:(BOOL)reattach
518     {
519         for (WineOpenGLContext* context in glContexts)
520         {
521             context.needsUpdate = TRUE;
522             if (reattach)
523                 context.needsReattach = TRUE;
524         }
525     }
527     - (void) updateGLContexts
528     {
529         [self updateGLContexts:NO];
530     }
532     - (BOOL) _hasGLDescendant
533     {
534         if ([self isHidden])
535             return NO;
536         if (_everHadGLContext)
537             return YES;
538         for (WineContentView* view in [self subviews])
539         {
540             if ([view hasGLDescendant])
541                 return YES;
542         }
543         return NO;
544     }
546     - (BOOL) hasGLDescendant
547     {
548         if (!_cachedHasGLDescendantValid)
549         {
550             _cachedHasGLDescendant = [self _hasGLDescendant];
551             _cachedHasGLDescendantValid = YES;
552         }
553         return _cachedHasGLDescendant;
554     }
556     - (void) invalidateHasGLDescendant
557     {
558         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
559         _cachedHasGLDescendantValid = NO;
560         if (invalidateAncestors && self != [[self window] contentView])
561         {
562             WineContentView* superview = (WineContentView*)[self superview];
563             if ([superview isKindOfClass:[WineContentView class]])
564                 [superview invalidateHasGLDescendant];
565         }
566     }
568     - (void) wine_getBackingSize:(int*)outBackingSize
569     {
570         @synchronized(self) {
571             memcpy(outBackingSize, backingSize, sizeof(backingSize));
572         }
573     }
574     - (void) wine_setBackingSize:(const int*)newBackingSize
575     {
576         @synchronized(self) {
577             memcpy(backingSize, newBackingSize, sizeof(backingSize));
578         }
579     }
581     - (void) setRetinaMode:(int)mode
582     {
583         double scale = mode ? 0.5 : 2.0;
584         NSRect frame = self.frame;
585         frame.origin.x *= scale;
586         frame.origin.y *= scale;
587         frame.size.width *= scale;
588         frame.size.height *= scale;
589         [self setFrame:frame];
590         [self updateGLContexts];
592         for (WineContentView* subview in [self subviews])
593         {
594             if ([subview isKindOfClass:[WineContentView class]])
595                 [subview setRetinaMode:mode];
596         }
597     }
599     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
600     {
601         return YES;
602     }
604     - (BOOL) preservesContentDuringLiveResize
605     {
606         // Returning YES from this tells Cocoa to keep our view's content during
607         // a Cocoa-driven resize.  In theory, we're also supposed to override
608         // -setFrameSize: to mark exposed sections as needing redisplay, but
609         // user32 will take care of that in a roundabout way.  This way, we don't
610         // redraw until the window surface is flushed.
611         //
612         // This doesn't do anything when we resize the window ourselves.
613         return YES;
614     }
616     - (BOOL)acceptsFirstResponder
617     {
618         return [[self window] contentView] == self;
619     }
621     - (BOOL) mouseDownCanMoveWindow
622     {
623         return NO;
624     }
626     - (void) viewDidHide
627     {
628         [super viewDidHide];
629         [self invalidateHasGLDescendant];
630     }
632     - (void) viewDidUnhide
633     {
634         [super viewDidUnhide];
635         [self updateGLContexts:YES];
636         [self invalidateHasGLDescendant];
637     }
639     - (void) completeText:(NSString*)text
640     {
641         macdrv_event* event;
642         WineWindow* window = (WineWindow*)[self window];
644         event = macdrv_create_event(IM_SET_TEXT, window);
645         event->im_set_text.data = [window imeData];
646         event->im_set_text.text = (CFStringRef)[text copy];
647         event->im_set_text.complete = TRUE;
649         [[window queue] postEvent:event];
651         macdrv_release_event(event);
653         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
654         markedTextSelection = NSMakeRange(0, 0);
655         [[self inputContext] discardMarkedText];
656     }
658     - (NSFocusRingType) focusRingType
659     {
660         return NSFocusRingTypeNone;
661     }
663     - (void) didAddSubview:(NSView*)subview
664     {
665         if ([subview isKindOfClass:[WineContentView class]])
666         {
667             WineContentView* view = (WineContentView*)subview;
668             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
669                 [self invalidateHasGLDescendant];
670         }
671         [super didAddSubview:subview];
672     }
674     - (void) willRemoveSubview:(NSView*)subview
675     {
676         if ([subview isKindOfClass:[WineContentView class]])
677         {
678             WineContentView* view = (WineContentView*)subview;
679             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
680                 [self invalidateHasGLDescendant];
681         }
682         [super willRemoveSubview:subview];
683     }
685     /*
686      * ---------- NSTextInputClient methods ----------
687      */
688     - (NSTextInputContext*) inputContext
689     {
690         if (!markedText)
691             markedText = [[NSMutableAttributedString alloc] init];
692         return [super inputContext];
693     }
695     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
696     {
697         if ([string isKindOfClass:[NSAttributedString class]])
698             string = [string string];
700         if ([string isKindOfClass:[NSString class]])
701             [self completeText:string];
702     }
704     - (void) doCommandBySelector:(SEL)aSelector
705     {
706         [(WineWindow*)[self window] setCommandDone:TRUE];
707     }
709     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
710     {
711         if ([string isKindOfClass:[NSAttributedString class]])
712             string = [string string];
714         if ([string isKindOfClass:[NSString class]])
715         {
716             macdrv_event* event;
717             WineWindow* window = (WineWindow*)[self window];
719             if (replacementRange.location == NSNotFound)
720                 replacementRange = NSMakeRange(0, [markedText length]);
722             [markedText replaceCharactersInRange:replacementRange withString:string];
723             markedTextSelection = selectedRange;
724             markedTextSelection.location += replacementRange.location;
726             event = macdrv_create_event(IM_SET_TEXT, window);
727             event->im_set_text.data = [window imeData];
728             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
729             event->im_set_text.complete = FALSE;
730             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
732             [[window queue] postEvent:event];
734             macdrv_release_event(event);
736             [[self inputContext] invalidateCharacterCoordinates];
737         }
738     }
740     - (void) unmarkText
741     {
742         [self completeText:nil];
743     }
745     - (NSRange) selectedRange
746     {
747         return markedTextSelection;
748     }
750     - (NSRange) markedRange
751     {
752         NSRange range = NSMakeRange(0, [markedText length]);
753         if (!range.length)
754             range.location = NSNotFound;
755         return range;
756     }
758     - (BOOL) hasMarkedText
759     {
760         return [markedText length] > 0;
761     }
763     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
764     {
765         if (aRange.location >= [markedText length])
766             return nil;
768         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
769         if (actualRange)
770             *actualRange = aRange;
771         return [markedText attributedSubstringFromRange:aRange];
772     }
774     - (NSArray*) validAttributesForMarkedText
775     {
776         return [NSArray array];
777     }
779     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
780     {
781         macdrv_query* query;
782         WineWindow* window = (WineWindow*)[self window];
783         NSRect ret;
785         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
787         query = macdrv_create_query();
788         query->type = QUERY_IME_CHAR_RECT;
789         query->window = (macdrv_window)[window retain];
790         query->ime_char_rect.data = [window imeData];
791         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
793         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
794         {
795             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
796             ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
797             [[WineApplicationController sharedController] flipRect:&ret];
798         }
799         else
800             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
802         macdrv_release_query(query);
804         if (actualRange)
805             *actualRange = aRange;
806         return ret;
807     }
809     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
810     {
811         return NSNotFound;
812     }
814     - (NSInteger) windowLevel
815     {
816         return [[self window] level];
817     }
819 @end
822 @implementation WineWindow
824     static WineWindow* causing_becomeKeyWindow;
826     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
827     @synthesize drawnSinceShown;
828     @synthesize surface, surface_mutex;
829     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
830     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
831     @synthesize usePerPixelAlpha;
832     @synthesize imeData, commandDone;
834     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
835                                  windowFrame:(NSRect)window_frame
836                                         hwnd:(void*)hwnd
837                                        queue:(WineEventQueue*)queue
838     {
839         WineWindow* window;
840         WineContentView* contentView;
841         NSTrackingArea* trackingArea;
842         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
844         [[WineApplicationController sharedController] flipRect:&window_frame];
846         window = [[[self alloc] initWithContentRect:window_frame
847                                           styleMask:style_mask_for_features(wf)
848                                             backing:NSBackingStoreBuffered
849                                               defer:YES] autorelease];
851         if (!window) return nil;
853         /* Standardize windows to eliminate differences between titled and
854            borderless windows and between NSWindow and NSPanel. */
855         [window setHidesOnDeactivate:NO];
856         [window setReleasedWhenClosed:NO];
858         [window setOneShot:YES];
859         [window disableCursorRects];
860         [window setShowsResizeIndicator:NO];
861         [window setHasShadow:wf->shadow];
862         [window setAcceptsMouseMovedEvents:YES];
863         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
864         [window setDelegate:window];
865         [window setBackgroundColor:[NSColor clearColor]];
866         [window setOpaque:NO];
867         window.hwnd = hwnd;
868         window.queue = queue;
869         window->savedContentMinSize = NSZeroSize;
870         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
871         window->resizable = wf->resizable;
872         window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
874         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
875                                                                   (NSString*)kUTTypeContent,
876                                                                   nil]];
878         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
879         if (!contentView)
880             return nil;
881         [contentView setAutoresizesSubviews:NO];
883         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
884            because they give us mouse moves in the background. */
885         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
886                                                      options:(NSTrackingMouseMoved |
887                                                               NSTrackingActiveAlways |
888                                                               NSTrackingInVisibleRect)
889                                                        owner:window
890                                                     userInfo:nil] autorelease];
891         if (!trackingArea)
892             return nil;
893         [contentView addTrackingArea:trackingArea];
895         [window setContentView:contentView];
896         [window setInitialFirstResponder:contentView];
898         [nc addObserver:window
899                selector:@selector(updateFullscreen)
900                    name:NSApplicationDidChangeScreenParametersNotification
901                  object:NSApp];
902         [window updateFullscreen];
904         [nc addObserver:window
905                selector:@selector(applicationWillHide)
906                    name:NSApplicationWillHideNotification
907                  object:NSApp];
908         [nc addObserver:window
909                selector:@selector(applicationDidUnhide)
910                    name:NSApplicationDidUnhideNotification
911                  object:NSApp];
913         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
914                                                               selector:@selector(checkWineDisplayLink)
915                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
916                                                                 object:[NSWorkspace sharedWorkspace]];
918         [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
920         return window;
921     }
923     - (void) dealloc
924     {
925         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
926         [[NSNotificationCenter defaultCenter] removeObserver:self];
927         [queue release];
928         [latentChildWindows release];
929         [latentParentWindow release];
930         [shape release];
931         [shapeData release];
932         [super dealloc];
933     }
935     - (BOOL) preventResizing
936     {
937         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
938         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
939     }
941     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
942     {
943         if (allow_immovable_windows && (disabled || inMaximized))
944             return NO;
945         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
946             return NO;
947         else
948             return YES;
949     }
951     - (void) adjustFeaturesForState
952     {
953         NSUInteger style = [self styleMask];
955         if (style & NSClosableWindowMask)
956             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
957         if (style & NSMiniaturizableWindowMask)
958             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
959         if (style & NSResizableWindowMask)
960             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
961         if ([self respondsToSelector:@selector(toggleFullScreen:)])
962         {
963             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
964                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
965         }
967         if ([self preventResizing])
968         {
969             NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
970             [self setContentMinSize:size];
971             [self setContentMaxSize:size];
972         }
973         else
974         {
975             [self setContentMaxSize:savedContentMaxSize];
976             [self setContentMinSize:savedContentMinSize];
977         }
979         if (allow_immovable_windows || cursor_clipping_locks_windows)
980             [self setMovable:[self allowsMovingWithMaximized:maximized]];
981     }
983     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
984     {
985         if ([self respondsToSelector:@selector(toggleFullScreen:)])
986         {
987             NSUInteger style = [self styleMask];
989             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
990                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
991                 !(self.parentWindow || self.latentParentWindow))
992             {
993                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
994                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
995             }
996             else
997             {
998                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
999                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1000                 if (style & NSFullScreenWindowMask)
1001                     [super toggleFullScreen:nil];
1002             }
1003         }
1005         if (behavior != [self collectionBehavior])
1006         {
1007             [self setCollectionBehavior:behavior];
1008             [self adjustFeaturesForState];
1009         }
1010     }
1012     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1013     {
1014         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1015                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1016         NSUInteger currentStyle = [self styleMask];
1017         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1019         if (newStyle != currentStyle)
1020         {
1021             NSString* title = [[[self title] copy] autorelease];
1022             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1023             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1024             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1025             {
1026                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1027                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1028                 // just NSTitledWindowMask, the window buttons should disappear rather
1029                 // than just being disabled.  But they don't.  Similarly in reverse.
1030                 // The workaround is to also toggle NSClosableWindowMask at the same time.
1031                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1032             }
1033             [self setStyleMask:newStyle];
1035             // -setStyleMask: resets the firstResponder to the window.  Set it
1036             // back to the content view.
1037             if ([[self contentView] acceptsFirstResponder])
1038                 [self makeFirstResponder:[self contentView]];
1040             [self adjustFullScreenBehavior:[self collectionBehavior]];
1042             if ([[self title] length] == 0 && [title length] > 0)
1043                 [self setTitle:title];
1044         }
1046         resizable = wf->resizable;
1047         [self adjustFeaturesForState];
1048         [self setHasShadow:wf->shadow];
1049     }
1051     // Indicates if the window would be visible if the app were not hidden.
1052     - (BOOL) wouldBeVisible
1053     {
1054         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1055     }
1057     - (BOOL) isOrderedIn
1058     {
1059         return [self wouldBeVisible] || [self isMiniaturized];
1060     }
1062     - (NSInteger) minimumLevelForActive:(BOOL)active
1063     {
1064         NSInteger level;
1066         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1067                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1068             level = NSFloatingWindowLevel;
1069         else
1070             level = NSNormalWindowLevel;
1072         if (active)
1073         {
1074             BOOL captured;
1076             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1078             if (captured || fullscreen)
1079             {
1080                 if (captured)
1081                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1082                 else
1083                     level = NSStatusWindowLevel + 1;
1085                 if (self.floating)
1086                     level++;
1087             }
1088         }
1090         return level;
1091     }
1093     - (void) postDidUnminimizeEvent
1094     {
1095         macdrv_event* event;
1097         /* Coalesce events by discarding any previous ones still in the queue. */
1098         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1099                                forWindow:self];
1101         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1102         [queue postEvent:event];
1103         macdrv_release_event(event);
1104     }
1106     - (void) sendResizeStartQuery
1107     {
1108         macdrv_query* query = macdrv_create_query();
1109         query->type = QUERY_RESIZE_START;
1110         query->window = (macdrv_window)[self retain];
1112         [self.queue query:query timeout:0.3];
1113         macdrv_release_query(query);
1114     }
1116     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1117     {
1118         NSWindowCollectionBehavior behavior;
1120         self.disabled = state->disabled;
1121         self.noActivate = state->no_activate;
1123         if (self.floating != state->floating)
1124         {
1125             self.floating = state->floating;
1126             if (state->floating)
1127             {
1128                 // Became floating.  If child of non-floating window, make that
1129                 // relationship latent.
1130                 WineWindow* parent = (WineWindow*)[self parentWindow];
1131                 if (parent && !parent.floating)
1132                     [self becameIneligibleChild];
1133             }
1134             else
1135             {
1136                 // Became non-floating.  If parent of floating children, make that
1137                 // relationship latent.
1138                 WineWindow* child;
1139                 for (child in [self childWineWindows])
1140                 {
1141                     if (child.floating)
1142                         [child becameIneligibleChild];
1143                 }
1144             }
1146             // Check our latent relationships.  If floating status was the only
1147             // reason they were latent, then make them active.
1148             if ([self isVisible])
1149                 [self becameEligibleParentOrChild];
1151             [[WineApplicationController sharedController] adjustWindowLevels];
1152         }
1154         if (state->minimized_valid)
1155         {
1156             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1158             pendingMinimize = FALSE;
1159             if (state->minimized && ![self isMiniaturized])
1160             {
1161                 if ([self wouldBeVisible])
1162                 {
1163                     if ([self styleMask] & NSFullScreenWindowMask)
1164                     {
1165                         [self postDidUnminimizeEvent];
1166                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1167                     }
1168                     else
1169                     {
1170                         [super miniaturize:nil];
1171                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1172                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1173                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1174                     }
1175                 }
1176                 else
1177                     pendingMinimize = TRUE;
1178             }
1179             else if (!state->minimized && [self isMiniaturized])
1180             {
1181                 ignore_windowDeminiaturize = TRUE;
1182                 [self deminiaturize:nil];
1183                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1184             }
1186             if (discard)
1187                 [queue discardEventsMatchingMask:discard forWindow:self];
1188         }
1190         if (state->maximized != maximized)
1191         {
1192             maximized = state->maximized;
1193             [self adjustFeaturesForState];
1195             if (!maximized && [self inLiveResize])
1196                 [self sendResizeStartQuery];
1197         }
1199         behavior = NSWindowCollectionBehaviorDefault;
1200         if (state->excluded_by_expose)
1201             behavior |= NSWindowCollectionBehaviorTransient;
1202         else
1203             behavior |= NSWindowCollectionBehaviorManaged;
1204         if (state->excluded_by_cycle)
1205         {
1206             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1207             if ([self isOrderedIn])
1208                 [NSApp removeWindowsItem:self];
1209         }
1210         else
1211         {
1212             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1213             if ([self isOrderedIn])
1214                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1215         }
1216         [self adjustFullScreenBehavior:behavior];
1217     }
1219     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1220     {
1221         BOOL reordered = FALSE;
1223         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1224         {
1225             if ([self level] > [child level])
1226                 [child setLevel:[self level]];
1227             if (![child isVisible])
1228                 [child setAutodisplay:YES];
1229             [self addChildWindow:child ordered:NSWindowAbove];
1230             [child checkWineDisplayLink];
1231             [latentChildWindows removeObjectIdenticalTo:child];
1232             child.latentParentWindow = nil;
1233             reordered = TRUE;
1234         }
1235         else
1236         {
1237             if (!latentChildWindows)
1238                 latentChildWindows = [[NSMutableArray alloc] init];
1239             if (![latentChildWindows containsObject:child])
1240                 [latentChildWindows addObject:child];
1241             child.latentParentWindow = self;
1242         }
1244         return reordered;
1245     }
1247     - (BOOL) addChildWineWindow:(WineWindow*)child
1248     {
1249         return [self addChildWineWindow:child assumeVisible:FALSE];
1250     }
1252     - (void) removeChildWineWindow:(WineWindow*)child
1253     {
1254         [self removeChildWindow:child];
1255         if (child.latentParentWindow == self)
1256             child.latentParentWindow = nil;
1257         [latentChildWindows removeObjectIdenticalTo:child];
1258     }
1260     - (void) setChildWineWindows:(NSArray*)childWindows
1261     {
1262         NSArray* origChildren;
1263         NSUInteger count, start, limit, i;
1265         origChildren = self.childWineWindows;
1267         // If the current and desired children arrays match up to a point, leave
1268         // those matching children alone.
1269         count = childWindows.count;
1270         limit = MIN(origChildren.count, count);
1271         for (start = 0; start < limit; start++)
1272         {
1273             if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1274                 break;
1275         }
1277         // Remove all of the child windows and re-add them back-to-front so they
1278         // are in the desired order.
1279         for (i = start; i < count; i++)
1280         {
1281             WineWindow* child = [childWindows objectAtIndex:i];
1282             [self removeChildWindow:child];
1283         }
1284         for (i = start; i < count; i++)
1285         {
1286             WineWindow* child = [childWindows objectAtIndex:i];
1287             [self addChildWindow:child ordered:NSWindowAbove];
1288         }
1289     }
1291     static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1292     {
1293         NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1294         NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1295         NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1296         NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1297         if (index1 == NSNotFound)
1298         {
1299             if (index2 == NSNotFound)
1300                 return NSOrderedSame;
1301             else
1302                 return NSOrderedAscending;
1303         }
1304         else if (index2 == NSNotFound)
1305             return NSOrderedDescending;
1306         else if (index1 < index2)
1307             return NSOrderedDescending;
1308         else if (index2 < index1)
1309             return NSOrderedAscending;
1311         return NSOrderedSame;
1312     }
1314     - (BOOL) becameEligibleParentOrChild
1315     {
1316         BOOL reordered = FALSE;
1317         NSUInteger count;
1319         if (latentParentWindow.floating || !self.floating)
1320         {
1321             // If we aren't visible currently, we assume that we should be and soon
1322             // will be.  So, if the latent parent is visible that's enough to assume
1323             // we can establish the parent-child relationship in Cocoa.  That will
1324             // actually make us visible, which is fine.
1325             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1326                 reordered = TRUE;
1327         }
1329         // Here, though, we may not actually be visible yet and adding a child
1330         // won't make us visible.  The caller will have to call this method
1331         // again after actually making us visible.
1332         if ([self isVisible] && (count = [latentChildWindows count]))
1333         {
1334             NSMutableArray* windowNumbers;
1335             NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1336             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1337             NSUInteger i;
1339             windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1341             for (i = 0; i < count; i++)
1342             {
1343                 WineWindow* child = [latentChildWindows objectAtIndex:i];
1344                 if ([child isVisible] && (self.floating || !child.floating))
1345                 {
1346                     if (child.latentParentWindow == self)
1347                     {
1348                         if ([self level] > [child level])
1349                             [child setLevel:[self level]];
1350                         [childWindows addObject:child];
1351                         child.latentParentWindow = nil;
1352                         reordered = TRUE;
1353                     }
1354                     else
1355                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1356                     [indexesToRemove addIndex:i];
1357                 }
1358             }
1360             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1362             [childWindows sortWithOptions:NSSortStable
1363                           usingComparator:^NSComparisonResult(id obj1, id obj2){
1364                 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1365             }];
1366             [self setChildWineWindows:childWindows];
1367         }
1369         return reordered;
1370     }
1372     - (void) becameIneligibleChild
1373     {
1374         WineWindow* parent = (WineWindow*)[self parentWindow];
1375         if (parent)
1376         {
1377             if (!parent->latentChildWindows)
1378                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1379             [parent->latentChildWindows insertObject:self atIndex:0];
1380             self.latentParentWindow = parent;
1381             [parent removeChildWindow:self];
1382         }
1383     }
1385     - (void) becameIneligibleParentOrChild
1386     {
1387         NSArray* childWindows = [self childWineWindows];
1389         [self becameIneligibleChild];
1391         if ([childWindows count])
1392         {
1393             WineWindow* child;
1395             for (child in childWindows)
1396             {
1397                 child.latentParentWindow = self;
1398                 [self removeChildWindow:child];
1399             }
1401             if (latentChildWindows)
1402                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1403             else
1404                 latentChildWindows = [childWindows mutableCopy];
1405         }
1406     }
1408     // Determine if, among Wine windows, this window is directly above or below
1409     // a given other Wine window with no other Wine window intervening.
1410     // Intervening non-Wine windows are ignored.
1411     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1412     {
1413         NSNumber* windowNumber;
1414         NSNumber* otherWindowNumber;
1415         NSArray* windowNumbers;
1416         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1418         if (![self isVisible] || ![otherWindow isVisible])
1419             return FALSE;
1421         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1422         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1423         windowNumbers = [[self class] windowNumbersWithOptions:0];
1424         windowIndex = [windowNumbers indexOfObject:windowNumber];
1425         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1427         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1428             return FALSE;
1430         if (orderingMode == NSWindowAbove)
1431         {
1432             lowIndex = windowIndex;
1433             highIndex = otherWindowIndex;
1434         }
1435         else if (orderingMode == NSWindowBelow)
1436         {
1437             lowIndex = otherWindowIndex;
1438             highIndex = windowIndex;
1439         }
1440         else
1441             return FALSE;
1443         if (highIndex <= lowIndex)
1444             return FALSE;
1446         for (i = lowIndex + 1; i < highIndex; i++)
1447         {
1448             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1449             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1450             if ([interveningWindow isKindOfClass:[WineWindow class]])
1451                 return FALSE;
1452         }
1454         return TRUE;
1455     }
1457     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1458     {
1459         NSMutableArray* windowNumbers;
1460         NSNumber* childWindowNumber;
1461         NSUInteger otherIndex;
1462         NSArray* origChildren;
1463         NSMutableArray* children;
1465         // Get the z-order from the window server and modify it to reflect the
1466         // requested window ordering.
1467         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1468         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1469         [windowNumbers removeObject:childWindowNumber];
1470         if (other)
1471         {
1472             otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1473             [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1474         }
1475         else if (mode == NSWindowAbove)
1476             [windowNumbers insertObject:childWindowNumber atIndex:0];
1477         else
1478             [windowNumbers addObject:childWindowNumber];
1480         // Get our child windows and sort them in the reverse of the desired
1481         // z-order (back-to-front).
1482         origChildren = [self childWineWindows];
1483         children = [[origChildren mutableCopy] autorelease];
1484         [children sortWithOptions:NSSortStable
1485                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1486             return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1487         }];
1489         [self setChildWineWindows:children];
1490     }
1492     // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1493     // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1494     //      (non-nil, non-nil)  there is a level in the window tree where the two windows have sibling ancestors
1495     //                          if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1496     //                          otherwise, the two ancestors are each roots of disjoint window trees
1497     //      (nil, non-nil)      the other window is a descendent of self and *ancestorOfOther is the direct child
1498     //      (non-nil, nil)      self is a descendent of other and *ancestor is the direct child
1499     - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1500     {
1501         NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1502         WineWindow* child;
1503         WineWindow* parent;
1504         for (child = other;
1505              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1506              child = parent)
1507         {
1508             if (parent == self)
1509             {
1510                 *ancestor = nil;
1511                 *ancestorOfOther = child;
1512                 return;
1513             }
1515             [otherAncestors addObject:parent];
1516         }
1518         for (child = self;
1519              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1520              child = parent)
1521         {
1522             NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1523             if (index != NSNotFound)
1524             {
1525                 *ancestor = child;
1526                 if (index == 0)
1527                     *ancestorOfOther = nil;
1528                 else
1529                     *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1530                 return;
1531             }
1532         }
1534         *ancestor = child;
1535         *ancestorOfOther = otherAncestors.lastObject;;
1536     }
1538     /* Returns whether or not the window was ordered in, which depends on if
1539        its frame intersects any screen. */
1540     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1541     {
1542         WineApplicationController* controller = [WineApplicationController sharedController];
1543         if (![self isMiniaturized])
1544         {
1545             BOOL needAdjustWindowLevels = FALSE;
1546             BOOL wasVisible;
1547             WineWindow* parent;
1548             WineWindow* child;
1550             [controller transformProcessToForeground];
1551             if ([NSApp isHidden])
1552                 [NSApp unhide:nil];
1553             wasVisible = [self isVisible];
1555             if (activate)
1556                 [NSApp activateIgnoringOtherApps:YES];
1558             NSDisableScreenUpdates();
1560             if ([self becameEligibleParentOrChild])
1561                 needAdjustWindowLevels = TRUE;
1563             if (prev || next)
1564             {
1565                 WineWindow* other = [prev isVisible] ? prev : next;
1566                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1568                 if (![self isOrdered:orderingMode relativeTo:other])
1569                 {
1570                     WineWindow* ancestor;
1571                     WineWindow* ancestorOfOther;
1573                     [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1574                     if (ancestor)
1575                     {
1576                         [self setAutodisplay:YES];
1577                         if (ancestorOfOther)
1578                         {
1579                             // This window level may not be right for this window based
1580                             // on floating-ness, fullscreen-ness, etc.  But we set it
1581                             // temporarily to allow us to order the windows properly.
1582                             // Then the levels get fixed by -adjustWindowLevels.
1583                             if ([ancestor level] != [ancestorOfOther level])
1584                                 [ancestor setLevel:[ancestorOfOther level]];
1586                             parent = (WineWindow*)ancestor.parentWindow;
1587                             if ([parent isKindOfClass:[WineWindow class]])
1588                                 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1589                             else
1590                                 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1591                         }
1593                         for (child = self;
1594                              (parent = (WineWindow*)child.parentWindow);
1595                              child = parent)
1596                         {
1597                             if ([parent isKindOfClass:[WineWindow class]])
1598                                 [parent order:-orderingMode childWindow:child relativeTo:nil];
1599                             if (parent == ancestor)
1600                                 break;
1601                         }
1603                         [self checkWineDisplayLink];
1604                         needAdjustWindowLevels = TRUE;
1605                     }
1606                 }
1607             }
1608             else
1609             {
1610                 for (child = self;
1611                      (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1612                      child = parent)
1613                 {
1614                     [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1615                 }
1617                 // Again, temporarily set level to make sure we can order to
1618                 // the right place.
1619                 next = [controller frontWineWindow];
1620                 if (next && [self level] < [next level])
1621                     [self setLevel:[next level]];
1622                 [self setAutodisplay:YES];
1623                 [self orderFront:nil];
1624                 [self checkWineDisplayLink];
1625                 needAdjustWindowLevels = TRUE;
1626             }
1627             pendingOrderOut = FALSE;
1629             if ([self becameEligibleParentOrChild])
1630                 needAdjustWindowLevels = TRUE;
1632             if (needAdjustWindowLevels)
1633             {
1634                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1635                     [controller updateFullscreenWindows];
1636                 [controller adjustWindowLevels];
1637             }
1639             if (pendingMinimize)
1640             {
1641                 [super miniaturize:nil];
1642                 pendingMinimize = FALSE;
1643             }
1645             NSEnableScreenUpdates();
1647             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1648                Generate a frame-changed event just in case.  The back end will ignore
1649                it if nothing actually changed. */
1650             [self windowDidResize:nil];
1652             if (![self isExcludedFromWindowsMenu])
1653                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1654         }
1655     }
1657     - (void) doOrderOut
1658     {
1659         WineApplicationController* controller = [WineApplicationController sharedController];
1660         BOOL wasVisible = [self isVisible];
1661         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1663         [self endWindowDragging];
1664         [controller windowWillOrderOut:self];
1666         if (enteringFullScreen || exitingFullScreen)
1667         {
1668             pendingOrderOut = TRUE;
1669             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1670                                              event_mask_for_type(WINDOW_GOT_FOCUS) |
1671                                              event_mask_for_type(WINDOW_LOST_FOCUS) |
1672                                              event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1673                                              event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1674                                              event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1675                                    forWindow:self];
1676             return;
1677         }
1679         pendingOrderOut = FALSE;
1681         if ([self isMiniaturized])
1682             pendingMinimize = TRUE;
1684         WineWindow* parent = (WineWindow*)self.parentWindow;
1685         if ([parent isKindOfClass:[WineWindow class]])
1686             [parent grabDockIconSnapshotFromWindow:self force:NO];
1688         [self becameIneligibleParentOrChild];
1689         if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1690         {
1691             fakingClose = TRUE;
1692             [self close];
1693             fakingClose = FALSE;
1694         }
1695         else
1696             [self orderOut:nil];
1697         [self checkWineDisplayLink];
1698         [self setBackgroundColor:[NSColor clearColor]];
1699         [self setOpaque:NO];
1700         drawnSinceShown = NO;
1701         savedVisibleState = FALSE;
1702         if (wasVisible && wasOnActiveSpace && fullscreen)
1703             [controller updateFullscreenWindows];
1704         [controller adjustWindowLevels];
1705         [NSApp removeWindowsItem:self];
1707         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1708                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1709                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1710                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1711                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1712                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1713                                forWindow:self];
1714     }
1716     - (void) updateFullscreen
1717     {
1718         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1719         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1721         if (nowFullscreen != fullscreen)
1722         {
1723             WineApplicationController* controller = [WineApplicationController sharedController];
1725             fullscreen = nowFullscreen;
1726             if ([self isVisible] && [self isOnActiveSpace])
1727                 [controller updateFullscreenWindows];
1729             [controller adjustWindowLevels];
1730         }
1731     }
1733     - (void) setFrameAndWineFrame:(NSRect)frame
1734     {
1735         [self setFrame:frame display:YES];
1737         wineFrame = frame;
1738         roundedWineFrame = self.frame;
1739         CGFloat junk;
1740 #if CGFLOAT_IS_DOUBLE
1741         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1742              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1743             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1744             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1745             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1746             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1747             roundedWineFrame = wineFrame;
1748 #else
1749         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1750              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1751             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1752             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1753             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1754             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1755             roundedWineFrame = wineFrame;
1756 #endif
1757     }
1759     - (void) setFrameFromWine:(NSRect)contentRect
1760     {
1761         /* Origin is (left, top) in a top-down space.  Need to convert it to
1762            (left, bottom) in a bottom-up space. */
1763         [[WineApplicationController sharedController] flipRect:&contentRect];
1765         /* The back end is establishing a new window size and position.  It's
1766            not interested in any stale events regarding those that may be sitting
1767            in the queue. */
1768         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1769                                forWindow:self];
1771         if (!NSIsEmptyRect(contentRect))
1772         {
1773             NSRect frame, oldFrame;
1775             oldFrame = self.wine_fractionalFrame;
1776             frame = [self frameRectForContentRect:contentRect];
1777             if (!NSEqualRects(frame, oldFrame))
1778             {
1779                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1780                 BOOL needEnableScreenUpdates = FALSE;
1782                 if ([self preventResizing])
1783                 {
1784                     // Allow the following calls to -setFrame:display: to work even
1785                     // if they would violate the content size constraints. This
1786                     // shouldn't be necessary since the content size constraints are
1787                     // documented to not constrain that method, but it seems to be.
1788                     [self setContentMinSize:NSZeroSize];
1789                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1790                 }
1792                 if (equalSizes && [[self childWineWindows] count])
1793                 {
1794                     // If we change the window frame such that the origin moves
1795                     // but the size doesn't change, then Cocoa moves child
1796                     // windows with the parent.  We don't want that so we fake
1797                     // a change of the size and then change it back.
1798                     NSRect bogusFrame = frame;
1799                     bogusFrame.size.width++;
1801                     NSDisableScreenUpdates();
1802                     needEnableScreenUpdates = TRUE;
1804                     ignore_windowResize = TRUE;
1805                     [self setFrame:bogusFrame display:NO];
1806                     ignore_windowResize = FALSE;
1807                 }
1809                 [self setFrameAndWineFrame:frame];
1810                 if ([self preventResizing])
1811                 {
1812                     [self setContentMinSize:contentRect.size];
1813                     [self setContentMaxSize:contentRect.size];
1814                 }
1816                 if (needEnableScreenUpdates)
1817                     NSEnableScreenUpdates();
1819                 if (!equalSizes)
1820                     [self updateColorSpace];
1822                 if (!enteringFullScreen &&
1823                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1824                     nonFullscreenFrame = frame;
1826                 [self updateFullscreen];
1828                 if ([self isOrderedIn])
1829                 {
1830                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1831                        event.  The back end will ignore it if nothing actually changed. */
1832                     [self windowDidResize:nil];
1833                 }
1834             }
1835         }
1836     }
1838     - (NSRect) wine_fractionalFrame
1839     {
1840         NSRect frame = self.frame;
1841         if (NSEqualRects(frame, roundedWineFrame))
1842             frame = wineFrame;
1843         return frame;
1844     }
1846     - (void) setMacDrvParentWindow:(WineWindow*)parent
1847     {
1848         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1849         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1850         {
1851             [oldParent removeChildWineWindow:self];
1852             [latentParentWindow removeChildWineWindow:self];
1853             if ([parent addChildWineWindow:self])
1854                 [[WineApplicationController sharedController] adjustWindowLevels];
1855             [self adjustFullScreenBehavior:[self collectionBehavior]];
1856         }
1857     }
1859     - (void) setDisabled:(BOOL)newValue
1860     {
1861         if (disabled != newValue)
1862         {
1863             disabled = newValue;
1864             [self adjustFeaturesForState];
1865         }
1866     }
1868     - (BOOL) needsTransparency
1869     {
1870         return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1871                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1872     }
1874     - (void) checkTransparency
1875     {
1876         if (![self isOpaque] && !self.needsTransparency)
1877         {
1878             self.shapeChangedSinceLastDraw = TRUE;
1879             [[self contentView] setNeedsDisplay:YES];
1880             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1881             [self setOpaque:YES];
1882         }
1883         else if ([self isOpaque] && self.needsTransparency)
1884         {
1885             self.shapeChangedSinceLastDraw = TRUE;
1886             [[self contentView] setNeedsDisplay:YES];
1887             [self setBackgroundColor:[NSColor clearColor]];
1888             [self setOpaque:NO];
1889         }
1890     }
1892     - (void) setShape:(NSBezierPath*)newShape
1893     {
1894         if (shape == newShape) return;
1896         if (shape)
1897         {
1898             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1899             [shape release];
1900         }
1901         if (newShape)
1902             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1904         shape = [newShape copy];
1905         self.shapeChangedSinceLastDraw = TRUE;
1907         [self checkTransparency];
1908     }
1910     - (void) makeFocused:(BOOL)activate
1911     {
1912         if (activate)
1913         {
1914             [[WineApplicationController sharedController] transformProcessToForeground];
1915             [NSApp activateIgnoringOtherApps:YES];
1916         }
1918         causing_becomeKeyWindow = self;
1919         [self makeKeyWindow];
1920         causing_becomeKeyWindow = nil;
1922         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1923                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1924                                forWindow:self];
1925     }
1927     - (void) postKey:(uint16_t)keyCode
1928              pressed:(BOOL)pressed
1929            modifiers:(NSUInteger)modifiers
1930                event:(NSEvent*)theEvent
1931     {
1932         macdrv_event* event;
1933         CGEventRef cgevent;
1934         WineApplicationController* controller = [WineApplicationController sharedController];
1936         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1937         event->key.keycode   = keyCode;
1938         event->key.modifiers = modifiers;
1939         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1941         if ((cgevent = [theEvent CGEvent]))
1942             controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
1944         [queue postEvent:event];
1946         macdrv_release_event(event);
1948         [controller noteKey:keyCode pressed:pressed];
1949     }
1951     - (void) postKeyEvent:(NSEvent *)theEvent
1952     {
1953         [self flagsChanged:theEvent];
1954         [self postKey:[theEvent keyCode]
1955               pressed:[theEvent type] == NSKeyDown
1956             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1957                 event:theEvent];
1958     }
1960     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1961     {
1962         savedContentMinSize = minSize;
1963         savedContentMaxSize = maxSize;
1964         if (![self preventResizing])
1965         {
1966             [self setContentMinSize:minSize];
1967             [self setContentMaxSize:maxSize];
1968         }
1969     }
1971     - (WineWindow*) ancestorWineWindow
1972     {
1973         WineWindow* ancestor = self;
1974         for (;;)
1975         {
1976             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1977             if ([parent isKindOfClass:[WineWindow class]])
1978                 ancestor = parent;
1979             else
1980                 break;
1981         }
1982         return ancestor;
1983     }
1985     - (void) postBroughtForwardEvent
1986     {
1987         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1988         [queue postEvent:event];
1989         macdrv_release_event(event);
1990     }
1992     - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
1993     {
1994         macdrv_event* event;
1995         NSUInteger style = self.styleMask;
1997         if (isFullscreen)
1998             style |= NSFullScreenWindowMask;
1999         else
2000             style &= ~NSFullScreenWindowMask;
2001         frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2002         [[WineApplicationController sharedController] flipRect:&frame];
2004         /* Coalesce events by discarding any previous ones still in the queue. */
2005         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2006                                forWindow:self];
2008         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2009         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2010         event->window_frame_changed.fullscreen = isFullscreen;
2011         event->window_frame_changed.in_resize = resizing;
2012         [queue postEvent:event];
2013         macdrv_release_event(event);
2014     }
2016     - (void) updateForCursorClipping
2017     {
2018         [self adjustFeaturesForState];
2019     }
2021     - (void) endWindowDragging
2022     {
2023         if (draggingPhase)
2024         {
2025             if (draggingPhase == 3)
2026             {
2027                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2028                 [queue postEvent:event];
2029                 macdrv_release_event(event);
2030             }
2032             draggingPhase = 0;
2033             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2034         }
2035     }
2037     - (NSMutableDictionary*) displayIDToDisplayLinkMap
2038     {
2039         static NSMutableDictionary* displayIDToDisplayLinkMap;
2040         if (!displayIDToDisplayLinkMap)
2041         {
2042             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2044             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2045                                                               object:NSApp
2046                                                                queue:nil
2047                                                           usingBlock:^(NSNotification *note){
2048                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2049                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2050                 [badDisplayIDs minusSet:validDisplayIDs];
2051                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2052             }];
2053         }
2054         return displayIDToDisplayLinkMap;
2055     }
2057     - (WineDisplayLink*) wineDisplayLink
2058     {
2059         if (!_lastDisplayID)
2060             return nil;
2062         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2063         return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2064     }
2066     - (void) checkWineDisplayLink
2067     {
2068         NSScreen* screen = self.screen;
2069         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2070             screen = nil;
2071 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2072         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2073             screen = nil;
2074 #endif
2076         NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2077         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2078         if (displayID == _lastDisplayID)
2079             return;
2081         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2083         if (_lastDisplayID)
2084         {
2085             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2086             [link removeWindow:self];
2087         }
2088         if (displayID)
2089         {
2090             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2091             if (!link)
2092             {
2093                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2094                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2095             }
2096             [link addWindow:self];
2097             [self displayIfNeeded];
2098         }
2099         _lastDisplayID = displayID;
2100     }
2102     - (BOOL) isEmptyShaped
2103     {
2104         return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2105     }
2107     - (BOOL) canProvideSnapshot
2108     {
2109         return (self.windowNumber > 0 && ![self isEmptyShaped]);
2110     }
2112     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2113     {
2114         if (![self isEmptyShaped])
2115             return;
2117         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2118         if (!force && now < lastDockIconSnapshot + 1)
2119             return;
2121         if (window)
2122         {
2123             if (![window canProvideSnapshot])
2124                 return;
2125         }
2126         else
2127         {
2128             CGFloat bestArea;
2129             for (WineWindow* childWindow in self.childWindows)
2130             {
2131                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2132                     continue;
2134                 NSSize size = childWindow.frame.size;
2135                 CGFloat area = size.width * size.height;
2136                 if (!window || area > bestArea)
2137                 {
2138                     window = childWindow;
2139                     bestArea = area;
2140                 }
2141             }
2143             if (!window)
2144                 return;
2145         }
2147         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2148         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2149         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2150         CFRelease(windowIDs);
2151         if (!windowImage)
2152             return;
2154         NSImage* appImage = [NSApp applicationIconImage];
2155         if (!appImage)
2156             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2158         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2159         [dockIcon lockFocus];
2161         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2163         CGRect rect = CGRectMake(8, 8, 240, 240);
2164         size_t width = CGImageGetWidth(windowImage);
2165         size_t height = CGImageGetHeight(windowImage);
2166         if (width > height)
2167         {
2168             rect.size.height *= height / (double)width;
2169             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2170         }
2171         else if (width != height)
2172         {
2173             rect.size.width *= width / (double)height;
2174             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2175         }
2177         CGContextDrawImage(cgcontext, rect, windowImage);
2178         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2179                     fromRect:NSZeroRect
2180                    operation:NSCompositeSourceOver
2181                     fraction:1
2182               respectFlipped:YES
2183                        hints:nil];
2185         [dockIcon unlockFocus];
2187         CGImageRelease(windowImage);
2189         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2190         if (![imageView isKindOfClass:[NSImageView class]])
2191         {
2192             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2193             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2194             self.dockTile.contentView = imageView;
2195         }
2196         imageView.image = dockIcon;
2197         [self.dockTile display];
2198         lastDockIconSnapshot = now;
2199     }
2201     - (void) checkEmptyShaped
2202     {
2203         if (self.dockTile.contentView && ![self isEmptyShaped])
2204         {
2205             self.dockTile.contentView = nil;
2206             lastDockIconSnapshot = 0;
2207         }
2208         [self checkWineDisplayLink];
2209     }
2212     /*
2213      * ---------- NSWindow method overrides ----------
2214      */
2215     - (BOOL) canBecomeKeyWindow
2216     {
2217         if (causing_becomeKeyWindow == self) return YES;
2218         if (self.disabled || self.noActivate) return NO;
2219         return [self isKeyWindow];
2220     }
2222     - (BOOL) canBecomeMainWindow
2223     {
2224         return [self canBecomeKeyWindow];
2225     }
2227     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2228     {
2229         // If a window is sized to completely cover a screen, then it's in
2230         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2231         // it.
2232         NSArray* screens = [NSScreen screens];
2233         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2234         if (!screen_covered_by_rect(contentRect, screens) &&
2235             frame_intersects_screens(frameRect, screens))
2236             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2237         return frameRect;
2238     }
2240     // This private method of NSWindow is called as Cocoa reacts to the display
2241     // configuration changing.  Among other things, it adjusts the window's
2242     // frame based on how the screen(s) changed size.  That tells Wine that the
2243     // window has been moved.  We don't want that.  Rather, we want to make
2244     // sure that the WinAPI notion of the window position is maintained/
2245     // restored, possibly undoing or overriding Cocoa's adjustment.
2246     //
2247     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2248     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2249     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2250     // reassert its notion of the position.  That call won't get processed
2251     // until after this method returns, so it will override whatever this
2252     // method does to the window position.  It will also discard any pending
2253     // WINDOW_FRAME_CHANGED events.
2254     //
2255     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2256     // adjust the window's position due to a display change is to hook into
2257     // this private method.  This private method has remained stable from 10.6
2258     // through 10.11.  If it does change, the most likely thing is that it
2259     // will be removed and no longer called and this fix will simply stop
2260     // working.  The only real danger would be if Apple changed the return type
2261     // to a struct or floating-point type, which would change the calling
2262     // convention.
2263     - (id) _displayChanged
2264     {
2265         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2266         [queue postEvent:event];
2267         macdrv_release_event(event);
2269         return [super _displayChanged];
2270     }
2272     - (BOOL) isExcludedFromWindowsMenu
2273     {
2274         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2275     }
2277     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2278     {
2279         BOOL ret = [super validateMenuItem:menuItem];
2281         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2282             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2283         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2284             ret = NO;
2286         return ret;
2287     }
2289     /* We don't call this.  It's the action method of the items in the Window menu. */
2290     - (void) makeKeyAndOrderFront:(id)sender
2291     {
2292         if ([self isMiniaturized])
2293             [self deminiaturize:nil];
2294         [self orderBelow:nil orAbove:nil activate:NO];
2295         [[self ancestorWineWindow] postBroughtForwardEvent];
2297         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2298             [[WineApplicationController sharedController] windowGotFocus:self];
2299     }
2301     - (void) sendEvent:(NSEvent*)event
2302     {
2303         NSEventType type = event.type;
2305         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2306            interface control.  For example, Control-Tab switches focus among
2307            views.  We want to bypass that feature, so directly route key-down
2308            events to -keyDown:. */
2309         if (type == NSKeyDown)
2310             [[self firstResponder] keyDown:event];
2311         else
2312         {
2313             if (!draggingPhase && maximized && ![self isMovable] &&
2314                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2315                 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2316             {
2317                 NSRect titleBar = self.frame;
2318                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2319                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2320                 titleBar.origin.y = NSMaxY(contentRect);
2322                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2324                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2325                 {
2326                     static const NSWindowButton buttons[] = {
2327                         NSWindowCloseButton,
2328                         NSWindowMiniaturizeButton,
2329                         NSWindowZoomButton,
2330                         NSWindowFullScreenButton,
2331                     };
2332                     BOOL hitButton = NO;
2333                     int i;
2335                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2336                     {
2337                         NSButton* button;
2339                         if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2340                             continue;
2342                         button = [self standardWindowButton:buttons[i]];
2343                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2344                         {
2345                             hitButton = YES;
2346                             break;
2347                         }
2348                     }
2350                     if (!hitButton)
2351                     {
2352                         draggingPhase = 1;
2353                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2354                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2355                     }
2356                 }
2357             }
2358             else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2359             {
2360                 if ([self isMovable])
2361                 {
2362                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2363                     NSPoint newTopLeft = dragWindowStartPosition;
2365                     newTopLeft.x += point.x - dragStartPosition.x;
2366                     newTopLeft.y += point.y - dragStartPosition.y;
2368                     if (draggingPhase == 2)
2369                     {
2370                         macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2371                         mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2372                         [queue postEvent:mevent];
2373                         macdrv_release_event(mevent);
2375                         draggingPhase = 3;
2376                     }
2378                     [self setFrameTopLeftPoint:newTopLeft];
2379                 }
2380                 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2381                 {
2382                     macdrv_event* event;
2383                     NSRect frame = [self contentRectForFrameRect:self.frame];
2385                     [[WineApplicationController sharedController] flipRect:&frame];
2387                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2388                     event->window_restore_requested.keep_frame = TRUE;
2389                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2390                     [queue postEvent:event];
2391                     macdrv_release_event(event);
2393                     draggingPhase = 2;
2394                 }
2396                 if (type == NSLeftMouseUp)
2397                     [self endWindowDragging];
2398             }
2400             [super sendEvent:event];
2401         }
2402     }
2404     - (void) miniaturize:(id)sender
2405     {
2406         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2407         [queue postEvent:event];
2408         macdrv_release_event(event);
2410         WineWindow* parent = (WineWindow*)self.parentWindow;
2411         if ([parent isKindOfClass:[WineWindow class]])
2412             [parent grabDockIconSnapshotFromWindow:self force:YES];
2413     }
2415     - (void) toggleFullScreen:(id)sender
2416     {
2417         if (!self.disabled && !maximized)
2418             [super toggleFullScreen:sender];
2419     }
2421     - (void) setViewsNeedDisplay:(BOOL)value
2422     {
2423         if (value && ![self viewsNeedDisplay])
2424         {
2425             WineDisplayLink* link = [self wineDisplayLink];
2426             if (link)
2427             {
2428                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2429                 if (_lastDisplayTime + [link refreshPeriod] < now)
2430                     [self setAutodisplay:YES];
2431                 else
2432                 {
2433                     [link start];
2434                     _lastDisplayTime = now;
2435                 }
2436             }
2437             else
2438                 [self setAutodisplay:YES];
2439         }
2440         [super setViewsNeedDisplay:value];
2441     }
2443     - (void) display
2444     {
2445         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2446         [super display];
2447         if (_lastDisplayID)
2448             [self setAutodisplay:NO];
2449     }
2451     - (void) displayIfNeeded
2452     {
2453         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2454         [super displayIfNeeded];
2455         if (_lastDisplayID)
2456             [self setAutodisplay:NO];
2457     }
2459     - (void) windowDidDrawContent
2460     {
2461         if (!drawnSinceShown)
2462         {
2463             drawnSinceShown = YES;
2464             dispatch_async(dispatch_get_main_queue(), ^{
2465                 [self checkTransparency];
2466             });
2467         }
2468     }
2470     - (NSArray*) childWineWindows
2471     {
2472         NSArray* childWindows = self.childWindows;
2473         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2474             return [child isKindOfClass:[WineWindow class]];
2475         }];
2476         return [childWindows objectsAtIndexes:indexes];
2477     }
2479     // We normally use the generic/calibrated RGB color space for the window,
2480     // rather than the device color space, to avoid expensive color conversion
2481     // which slows down drawing.  However, for windows displaying OpenGL, having
2482     // a different color space than the screen greatly reduces frame rates, often
2483     // limiting it to the display refresh rate.
2484     //
2485     // To avoid this, we switch back to the screen color space whenever the
2486     // window is covered by a view with an attached OpenGL context.
2487     - (void) updateColorSpace
2488     {
2489         NSRect contentRect = [[self contentView] frame];
2490         BOOL coveredByGLView = FALSE;
2491         WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2492         if ([view isKindOfClass:[WineContentView class]] && view.everHadGLContext)
2493         {
2494             NSRect frame = [view convertRect:[view bounds] toView:nil];
2495             if (NSContainsRect(frame, contentRect))
2496                 coveredByGLView = TRUE;
2497         }
2499         if (coveredByGLView)
2500             [self setColorSpace:nil];
2501         else
2502             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2503     }
2505     - (void) updateForGLSubviews
2506     {
2507         [self updateColorSpace];
2508         if (gl_surface_mode == GL_SURFACE_BEHIND)
2509             [self checkTransparency];
2510     }
2512     - (void) setRetinaMode:(int)mode
2513     {
2514         NSRect frame;
2515         double scale = mode ? 0.5 : 2.0;
2516         NSAffineTransform* transform = [NSAffineTransform transform];
2518         [transform scaleBy:scale];
2520         if (shape)
2521             [shape transformUsingAffineTransform:transform];
2523         for (WineContentView* subview in [self.contentView subviews])
2524         {
2525             if ([subview isKindOfClass:[WineContentView class]])
2526                 [subview setRetinaMode:mode];
2527         }
2529         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2530         frame.origin.x *= scale;
2531         frame.origin.y *= scale;
2532         frame.size.width *= scale;
2533         frame.size.height *= scale;
2534         frame = [self frameRectForContentRect:frame];
2536         savedContentMinSize = [transform transformSize:savedContentMinSize];
2537         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2538             savedContentMaxSize.width *= scale;
2539         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2540             savedContentMaxSize.height *= scale;
2542         self.contentMinSize = [transform transformSize:self.contentMinSize];
2543         NSSize temp = self.contentMaxSize;
2544         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2545             temp.width *= scale;
2546         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2547             temp.height *= scale;
2548         self.contentMaxSize = temp;
2550         ignore_windowResize = TRUE;
2551         [self setFrameAndWineFrame:frame];
2552         ignore_windowResize = FALSE;
2553     }
2556     /*
2557      * ---------- NSResponder method overrides ----------
2558      */
2559     - (void) keyDown:(NSEvent *)theEvent
2560     {
2561         if ([theEvent isARepeat])
2562         {
2563             if (!allowKeyRepeats)
2564                 return;
2565         }
2566         else
2567             allowKeyRepeats = YES;
2569         [self postKeyEvent:theEvent];
2570     }
2572     - (void) flagsChanged:(NSEvent *)theEvent
2573     {
2574         static const struct {
2575             NSUInteger  mask;
2576             uint16_t    keycode;
2577         } modifiers[] = {
2578             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2579             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2580             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2581             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2582             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2583             { NX_DEVICELALTKEYMASK,     kVK_Option },
2584             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2585             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2586             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2587         };
2589         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2590         NSUInteger changed;
2591         int i, last_changed;
2593         fix_device_modifiers_by_generic(&modifierFlags);
2594         changed = modifierFlags ^ lastModifierFlags;
2596         last_changed = -1;
2597         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2598             if (changed & modifiers[i].mask)
2599                 last_changed = i;
2601         for (i = 0; i <= last_changed; i++)
2602         {
2603             if (changed & modifiers[i].mask)
2604             {
2605                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2607                 if (pressed)
2608                     allowKeyRepeats = NO;
2610                 if (i == last_changed)
2611                     lastModifierFlags = modifierFlags;
2612                 else
2613                 {
2614                     lastModifierFlags ^= modifiers[i].mask;
2615                     fix_generic_modifiers_by_device(&lastModifierFlags);
2616                 }
2618                 // Caps lock generates one event for each press-release action.
2619                 // We need to simulate a pair of events for each actual event.
2620                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2621                 {
2622                     [self postKey:modifiers[i].keycode
2623                           pressed:TRUE
2624                         modifiers:lastModifierFlags
2625                             event:(NSEvent*)theEvent];
2626                     pressed = FALSE;
2627                 }
2629                 [self postKey:modifiers[i].keycode
2630                       pressed:pressed
2631                     modifiers:lastModifierFlags
2632                         event:(NSEvent*)theEvent];
2633             }
2634         }
2635     }
2637     - (void) applicationWillHide
2638     {
2639         savedVisibleState = [self isVisible];
2640     }
2642     - (void) applicationDidUnhide
2643     {
2644         if ([self isVisible])
2645             [self becameEligibleParentOrChild];
2646     }
2649     /*
2650      * ---------- NSWindowDelegate methods ----------
2651      */
2652     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2653     {
2654         macdrv_query* query;
2655         NSSize size;
2657         query = macdrv_create_query();
2658         query->type = QUERY_MIN_MAX_INFO;
2659         query->window = (macdrv_window)[self retain];
2660         [self.queue query:query timeout:0.5];
2661         macdrv_release_query(query);
2663         size = [self contentMaxSize];
2664         if (proposedSize.width < size.width)
2665             size.width = proposedSize.width;
2666         if (proposedSize.height < size.height)
2667             size.height = proposedSize.height;
2668         return size;
2669     }
2671     - (void)windowDidBecomeKey:(NSNotification *)notification
2672     {
2673         WineApplicationController* controller = [WineApplicationController sharedController];
2674         NSEvent* event = [controller lastFlagsChanged];
2675         if (event)
2676             [self flagsChanged:event];
2678         if (causing_becomeKeyWindow == self) return;
2680         [controller windowGotFocus:self];
2681     }
2683     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2684     {
2685         [self checkWineDisplayLink];
2686     }
2688     - (void) windowDidChangeScreen:(NSNotification*)notification
2689     {
2690         [self checkWineDisplayLink];
2691     }
2693     - (void)windowDidDeminiaturize:(NSNotification *)notification
2694     {
2695         WineApplicationController* controller = [WineApplicationController sharedController];
2697         if (!ignore_windowDeminiaturize)
2698             [self postDidUnminimizeEvent];
2699         ignore_windowDeminiaturize = FALSE;
2701         [self becameEligibleParentOrChild];
2703         if (fullscreen && [self isOnActiveSpace])
2704             [controller updateFullscreenWindows];
2705         [controller adjustWindowLevels];
2707         if (![self parentWindow])
2708             [self postBroughtForwardEvent];
2710         if (!self.disabled && !self.noActivate)
2711         {
2712             causing_becomeKeyWindow = self;
2713             [self makeKeyWindow];
2714             causing_becomeKeyWindow = nil;
2715             [controller windowGotFocus:self];
2716         }
2718         [self windowDidResize:notification];
2719         [self checkWineDisplayLink];
2720     }
2722     - (void) windowDidEndLiveResize:(NSNotification *)notification
2723     {
2724         if (!maximized)
2725         {
2726             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2727             [queue postEvent:event];
2728             macdrv_release_event(event);
2729         }
2730     }
2732     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2733     {
2734         enteringFullScreen = FALSE;
2735         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2736         if (pendingOrderOut)
2737             [self doOrderOut];
2738     }
2740     - (void) windowDidExitFullScreen:(NSNotification*)notification
2741     {
2742         exitingFullScreen = FALSE;
2743         [self setFrameAndWineFrame:nonFullscreenFrame];
2744         [self windowDidResize:nil];
2745         if (pendingOrderOut)
2746             [self doOrderOut];
2747     }
2749     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2750     {
2751         enteringFullScreen = FALSE;
2752         enteredFullScreenTime = 0;
2753         if (pendingOrderOut)
2754             [self doOrderOut];
2755     }
2757     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2758     {
2759         exitingFullScreen = FALSE;
2760         [self windowDidResize:nil];
2761         if (pendingOrderOut)
2762             [self doOrderOut];
2763     }
2765     - (void)windowDidMiniaturize:(NSNotification *)notification
2766     {
2767         if (fullscreen && [self isOnActiveSpace])
2768             [[WineApplicationController sharedController] updateFullscreenWindows];
2769         [self checkWineDisplayLink];
2770     }
2772     - (void)windowDidMove:(NSNotification *)notification
2773     {
2774         [self windowDidResize:notification];
2775     }
2777     - (void)windowDidResignKey:(NSNotification *)notification
2778     {
2779         macdrv_event* event;
2781         if (causing_becomeKeyWindow) return;
2783         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2784         [queue postEvent:event];
2785         macdrv_release_event(event);
2786     }
2788     - (void)windowDidResize:(NSNotification *)notification
2789     {
2790         NSRect frame = self.wine_fractionalFrame;
2792         if ([self inLiveResize])
2793         {
2794             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2795                 resizingFromLeft = TRUE;
2796             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2797                 resizingFromTop = TRUE;
2798         }
2800         if (ignore_windowResize || exitingFullScreen) return;
2802         if ([self preventResizing])
2803         {
2804             NSRect contentRect = [self contentRectForFrameRect:frame];
2805             [self setContentMinSize:contentRect.size];
2806             [self setContentMaxSize:contentRect.size];
2807         }
2809         [self postWindowFrameChanged:frame
2810                           fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2811                             resizing:[self inLiveResize]];
2813         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2814         [self updateFullscreen];
2815     }
2817     - (BOOL)windowShouldClose:(id)sender
2818     {
2819         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2820         [queue postEvent:event];
2821         macdrv_release_event(event);
2822         return NO;
2823     }
2825     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2826     {
2827         if (maximized)
2828         {
2829             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2830             [queue postEvent:event];
2831             macdrv_release_event(event);
2832             return NO;
2833         }
2834         else if (!resizable)
2835         {
2836             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2837             [queue postEvent:event];
2838             macdrv_release_event(event);
2839             return NO;
2840         }
2842         return YES;
2843     }
2845     - (void) windowWillClose:(NSNotification*)notification
2846     {
2847         WineWindow* child;
2849         if (fakingClose) return;
2850         if (latentParentWindow)
2851         {
2852             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2853             self.latentParentWindow = nil;
2854         }
2856         for (child in latentChildWindows)
2857         {
2858             if (child.latentParentWindow == self)
2859                 child.latentParentWindow = nil;
2860         }
2861         [latentChildWindows removeAllObjects];
2862     }
2864     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2865     {
2866         enteringFullScreen = TRUE;
2867         nonFullscreenFrame = self.wine_fractionalFrame;
2868     }
2870     - (void) windowWillExitFullScreen:(NSNotification*)notification
2871     {
2872         exitingFullScreen = TRUE;
2873         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2874     }
2876     - (void)windowWillMiniaturize:(NSNotification *)notification
2877     {
2878         [self becameIneligibleParentOrChild];
2879         [self grabDockIconSnapshotFromWindow:nil force:NO];
2880     }
2882     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2883     {
2884         if ([self inLiveResize])
2885         {
2886             if (maximized)
2887                 return self.wine_fractionalFrame.size;
2889             NSRect rect;
2890             macdrv_query* query;
2892             rect = [self frame];
2893             if (resizingFromLeft)
2894                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2895             if (!resizingFromTop)
2896                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2897             rect.size = frameSize;
2898             rect = [self contentRectForFrameRect:rect];
2899             [[WineApplicationController sharedController] flipRect:&rect];
2901             query = macdrv_create_query();
2902             query->type = QUERY_RESIZE_SIZE;
2903             query->window = (macdrv_window)[self retain];
2904             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2905             query->resize_size.from_left = resizingFromLeft;
2906             query->resize_size.from_top = resizingFromTop;
2908             if ([self.queue query:query timeout:0.1])
2909             {
2910                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2911                 rect = [self frameRectForContentRect:rect];
2912                 frameSize = rect.size;
2913             }
2915             macdrv_release_query(query);
2916         }
2918         return frameSize;
2919     }
2921     - (void) windowWillStartLiveResize:(NSNotification *)notification
2922     {
2923         [self endWindowDragging];
2925         if (maximized)
2926         {
2927             macdrv_event* event;
2928             NSRect frame = [self contentRectForFrameRect:self.frame];
2930             [[WineApplicationController sharedController] flipRect:&frame];
2932             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2933             event->window_restore_requested.keep_frame = TRUE;
2934             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2935             [queue postEvent:event];
2936             macdrv_release_event(event);
2937         }
2938         else
2939             [self sendResizeStartQuery];
2941         frameAtResizeStart = [self frame];
2942         resizingFromLeft = resizingFromTop = FALSE;
2943     }
2945     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2946     {
2947         macdrv_query* query;
2948         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2949         NSSize maxSize;
2951         query = macdrv_create_query();
2952         query->type = QUERY_MIN_MAX_INFO;
2953         query->window = (macdrv_window)[self retain];
2954         [self.queue query:query timeout:0.5];
2955         macdrv_release_query(query);
2957         currentContentRect = [self contentRectForFrameRect:[self frame]];
2958         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2960         maxSize = [self contentMaxSize];
2961         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2962         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2964         // Try to keep the top-left corner where it is.
2965         newContentRect.origin.x = NSMinX(currentContentRect);
2966         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2968         // If that pushes the bottom or right off the screen, pull it up and to the left.
2969         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2970         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2971             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2972         if (NSMinY(newContentRect) < NSMinY(screenRect))
2973             newContentRect.origin.y = NSMinY(screenRect);
2975         // If that pushes the top or left off the screen, push it down and the right
2976         // again.  Do this last because the top-left corner is more important than the
2977         // bottom-right.
2978         if (NSMinX(newContentRect) < NSMinX(screenRect))
2979             newContentRect.origin.x = NSMinX(screenRect);
2980         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2981             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2983         return [self frameRectForContentRect:newContentRect];
2984     }
2987     /*
2988      * ---------- NSPasteboardOwner methods ----------
2989      */
2990     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2991     {
2992         macdrv_query* query = macdrv_create_query();
2993         query->type = QUERY_PASTEBOARD_DATA;
2994         query->window = (macdrv_window)[self retain];
2995         query->pasteboard_data.type = (CFStringRef)[type copy];
2997         [self.queue query:query timeout:3];
2998         macdrv_release_query(query);
2999     }
3001     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3002     {
3003         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3004         [queue postEvent:event];
3005         macdrv_release_event(event);
3006     }
3009     /*
3010      * ---------- NSDraggingDestination methods ----------
3011      */
3012     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3013     {
3014         return [self draggingUpdated:sender];
3015     }
3017     - (void) draggingExited:(id <NSDraggingInfo>)sender
3018     {
3019         // This isn't really a query.  We don't need any response.  However, it
3020         // has to be processed in a similar manner as the other drag-and-drop
3021         // queries in order to maintain the proper order of operations.
3022         macdrv_query* query = macdrv_create_query();
3023         query->type = QUERY_DRAG_EXITED;
3024         query->window = (macdrv_window)[self retain];
3026         [self.queue query:query timeout:0.1];
3027         macdrv_release_query(query);
3028     }
3030     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3031     {
3032         NSDragOperation ret;
3033         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3034         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3035         NSPasteboard* pb = [sender draggingPasteboard];
3037         macdrv_query* query = macdrv_create_query();
3038         query->type = QUERY_DRAG_OPERATION;
3039         query->window = (macdrv_window)[self retain];
3040         query->drag_operation.x = floor(cgpt.x);
3041         query->drag_operation.y = floor(cgpt.y);
3042         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3043         query->drag_operation.accepted_op = NSDragOperationNone;
3044         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3046         [self.queue query:query timeout:3];
3047         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3048         macdrv_release_query(query);
3050         return ret;
3051     }
3053     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3054     {
3055         BOOL ret;
3056         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3057         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3058         NSPasteboard* pb = [sender draggingPasteboard];
3060         macdrv_query* query = macdrv_create_query();
3061         query->type = QUERY_DRAG_DROP;
3062         query->window = (macdrv_window)[self retain];
3063         query->drag_drop.x = floor(cgpt.x);
3064         query->drag_drop.y = floor(cgpt.y);
3065         query->drag_drop.op = [sender draggingSourceOperationMask];
3066         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3068         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3069         ret = query->status;
3070         macdrv_release_query(query);
3072         return ret;
3073     }
3075     - (BOOL) wantsPeriodicDraggingUpdates
3076     {
3077         return NO;
3078     }
3080 @end
3083 /***********************************************************************
3084  *              macdrv_create_cocoa_window
3086  * Create a Cocoa window with the given content frame and features (e.g.
3087  * title bar, close box, etc.).
3088  */
3089 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3090         CGRect frame, void* hwnd, macdrv_event_queue queue)
3092     __block WineWindow* window;
3094     OnMainThread(^{
3095         window = [[WineWindow createWindowWithFeatures:wf
3096                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3097                                                   hwnd:hwnd
3098                                                  queue:(WineEventQueue*)queue] retain];
3099     });
3101     return (macdrv_window)window;
3104 /***********************************************************************
3105  *              macdrv_destroy_cocoa_window
3107  * Destroy a Cocoa window.
3108  */
3109 void macdrv_destroy_cocoa_window(macdrv_window w)
3111     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3112     WineWindow* window = (WineWindow*)w;
3114     OnMainThread(^{
3115         [window doOrderOut];
3116         [window close];
3117     });
3118     [window.queue discardEventsMatchingMask:-1 forWindow:window];
3119     [window release];
3121     [pool release];
3124 /***********************************************************************
3125  *              macdrv_get_window_hwnd
3127  * Get the hwnd that was set for the window at creation.
3128  */
3129 void* macdrv_get_window_hwnd(macdrv_window w)
3131     WineWindow* window = (WineWindow*)w;
3132     return window.hwnd;
3135 /***********************************************************************
3136  *              macdrv_set_cocoa_window_features
3138  * Update a Cocoa window's features.
3139  */
3140 void macdrv_set_cocoa_window_features(macdrv_window w,
3141         const struct macdrv_window_features* wf)
3143     WineWindow* window = (WineWindow*)w;
3145     OnMainThread(^{
3146         [window setWindowFeatures:wf];
3147     });
3150 /***********************************************************************
3151  *              macdrv_set_cocoa_window_state
3153  * Update a Cocoa window's state.
3154  */
3155 void macdrv_set_cocoa_window_state(macdrv_window w,
3156         const struct macdrv_window_state* state)
3158     WineWindow* window = (WineWindow*)w;
3160     OnMainThread(^{
3161         [window setMacDrvState:state];
3162     });
3165 /***********************************************************************
3166  *              macdrv_set_cocoa_window_title
3168  * Set a Cocoa window's title.
3169  */
3170 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3171         size_t length)
3173     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3174     WineWindow* window = (WineWindow*)w;
3175     NSString* titleString;
3177     if (title)
3178         titleString = [NSString stringWithCharacters:title length:length];
3179     else
3180         titleString = @"";
3181     OnMainThreadAsync(^{
3182         [window setTitle:titleString];
3183         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3184             [NSApp changeWindowsItem:window title:titleString filename:NO];
3185     });
3187     [pool release];
3190 /***********************************************************************
3191  *              macdrv_order_cocoa_window
3193  * Reorder a Cocoa window relative to other windows.  If prev is
3194  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3195  * it is ordered above that window.  Otherwise, it is ordered to the
3196  * front.
3197  */
3198 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3199         macdrv_window n, int activate)
3201     WineWindow* window = (WineWindow*)w;
3202     WineWindow* prev = (WineWindow*)p;
3203     WineWindow* next = (WineWindow*)n;
3205     OnMainThreadAsync(^{
3206         [window orderBelow:prev
3207                    orAbove:next
3208                   activate:activate];
3209     });
3210     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3211                                   forWindow:window];
3212     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3213                                 forWindow:next];
3216 /***********************************************************************
3217  *              macdrv_hide_cocoa_window
3219  * Hides a Cocoa window.
3220  */
3221 void macdrv_hide_cocoa_window(macdrv_window w)
3223     WineWindow* window = (WineWindow*)w;
3225     OnMainThread(^{
3226         [window doOrderOut];
3227     });
3230 /***********************************************************************
3231  *              macdrv_set_cocoa_window_frame
3233  * Move a Cocoa window.
3234  */
3235 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3237     WineWindow* window = (WineWindow*)w;
3239     OnMainThread(^{
3240         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3241     });
3244 /***********************************************************************
3245  *              macdrv_get_cocoa_window_frame
3247  * Gets the frame of a Cocoa window.
3248  */
3249 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3251     WineWindow* window = (WineWindow*)w;
3253     OnMainThread(^{
3254         NSRect frame;
3256         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3257         [[WineApplicationController sharedController] flipRect:&frame];
3258         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3259     });
3262 /***********************************************************************
3263  *              macdrv_set_cocoa_parent_window
3265  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3266  * the parent window.
3267  */
3268 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3270     WineWindow* window = (WineWindow*)w;
3272     OnMainThread(^{
3273         [window setMacDrvParentWindow:(WineWindow*)parent];
3274     });
3277 /***********************************************************************
3278  *              macdrv_set_window_surface
3279  */
3280 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3282     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3283     WineWindow* window = (WineWindow*)w;
3285     OnMainThread(^{
3286         window.surface = surface;
3287         window.surface_mutex = mutex;
3288     });
3290     [pool release];
3293 /***********************************************************************
3294  *              macdrv_window_needs_display
3296  * Mark a window as needing display in a specified rect (in non-client
3297  * area coordinates).
3298  */
3299 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3301     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3302     WineWindow* window = (WineWindow*)w;
3304     OnMainThreadAsync(^{
3305         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3306     });
3308     [pool release];
3311 /***********************************************************************
3312  *              macdrv_set_window_shape
3314  * Sets the shape of a Cocoa window from an array of rectangles.  If
3315  * rects is NULL, resets the window's shape to its frame.
3316  */
3317 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3319     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3320     WineWindow* window = (WineWindow*)w;
3322     OnMainThread(^{
3323         if (!rects || !count)
3324         {
3325             window.shape = nil;
3326             window.shapeData = nil;
3327             [window checkEmptyShaped];
3328         }
3329         else
3330         {
3331             size_t length = sizeof(*rects) * count;
3332             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3333             {
3334                 NSBezierPath* path;
3335                 unsigned int i;
3337                 path = [NSBezierPath bezierPath];
3338                 for (i = 0; i < count; i++)
3339                     [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3340                 window.shape = path;
3341                 window.shapeData = [NSData dataWithBytes:rects length:length];
3342                 [window checkEmptyShaped];
3343             }
3344         }
3345     });
3347     [pool release];
3350 /***********************************************************************
3351  *              macdrv_set_window_alpha
3352  */
3353 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3355     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3356     WineWindow* window = (WineWindow*)w;
3358     [window setAlphaValue:alpha];
3360     [pool release];
3363 /***********************************************************************
3364  *              macdrv_set_window_color_key
3365  */
3366 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3367                                  CGFloat keyBlue)
3369     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3370     WineWindow* window = (WineWindow*)w;
3372     OnMainThread(^{
3373         window.colorKeyed       = TRUE;
3374         window.colorKeyRed      = keyRed;
3375         window.colorKeyGreen    = keyGreen;
3376         window.colorKeyBlue     = keyBlue;
3377         [window checkTransparency];
3378     });
3380     [pool release];
3383 /***********************************************************************
3384  *              macdrv_clear_window_color_key
3385  */
3386 void macdrv_clear_window_color_key(macdrv_window w)
3388     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3389     WineWindow* window = (WineWindow*)w;
3391     OnMainThread(^{
3392         window.colorKeyed = FALSE;
3393         [window checkTransparency];
3394     });
3396     [pool release];
3399 /***********************************************************************
3400  *              macdrv_window_use_per_pixel_alpha
3401  */
3402 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3404     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3405     WineWindow* window = (WineWindow*)w;
3407     OnMainThread(^{
3408         window.usePerPixelAlpha = use_per_pixel_alpha;
3409         [window checkTransparency];
3410     });
3412     [pool release];
3415 /***********************************************************************
3416  *              macdrv_give_cocoa_window_focus
3418  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3419  * orders it front and, if its frame was not within the desktop bounds,
3420  * Cocoa will typically move it on-screen.
3421  */
3422 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3424     WineWindow* window = (WineWindow*)w;
3426     OnMainThread(^{
3427         [window makeFocused:activate];
3428     });
3431 /***********************************************************************
3432  *              macdrv_set_window_min_max_sizes
3434  * Sets the window's minimum and maximum content sizes.
3435  */
3436 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3438     WineWindow* window = (WineWindow*)w;
3440     OnMainThread(^{
3441         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3442     });
3445 /***********************************************************************
3446  *              macdrv_create_view
3448  * Creates and returns a view with the specified frame rect.  The
3449  * caller is responsible for calling macdrv_dispose_view() on the view
3450  * when it is done with it.
3451  */
3452 macdrv_view macdrv_create_view(CGRect rect)
3454     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3455     __block WineContentView* view;
3457     if (CGRectIsNull(rect)) rect = CGRectZero;
3459     OnMainThread(^{
3460         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3462         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3463         [view setAutoresizesSubviews:NO];
3464         [view setHidden:YES];
3465         [nc addObserver:view
3466                selector:@selector(updateGLContexts)
3467                    name:NSViewGlobalFrameDidChangeNotification
3468                  object:view];
3469         [nc addObserver:view
3470                selector:@selector(updateGLContexts)
3471                    name:NSApplicationDidChangeScreenParametersNotification
3472                  object:NSApp];
3473     });
3475     [pool release];
3476     return (macdrv_view)view;
3479 /***********************************************************************
3480  *              macdrv_dispose_view
3482  * Destroys a view previously returned by macdrv_create_view.
3483  */
3484 void macdrv_dispose_view(macdrv_view v)
3486     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3487     WineContentView* view = (WineContentView*)v;
3489     OnMainThread(^{
3490         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3491         WineWindow* window = (WineWindow*)[view window];
3493         [nc removeObserver:view
3494                       name:NSViewGlobalFrameDidChangeNotification
3495                     object:view];
3496         [nc removeObserver:view
3497                       name:NSApplicationDidChangeScreenParametersNotification
3498                     object:NSApp];
3499         [view removeFromSuperview];
3500         [view release];
3501         [window updateForGLSubviews];
3502     });
3504     [pool release];
3507 /***********************************************************************
3508  *              macdrv_set_view_frame
3509  */
3510 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3512     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3513     WineContentView* view = (WineContentView*)v;
3515     if (CGRectIsNull(rect)) rect = CGRectZero;
3517     OnMainThreadAsync(^{
3518         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3519         NSRect oldFrame = [view frame];
3521         if (!NSEqualRects(oldFrame, newFrame))
3522         {
3523             [[view superview] setNeedsDisplayInRect:oldFrame];
3524             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3525                 [view setFrameSize:newFrame.size];
3526             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3527                 [view setFrameOrigin:newFrame.origin];
3528             else
3529                 [view setFrame:newFrame];
3530             [view setNeedsDisplay:YES];
3532             if (retina_enabled)
3533             {
3534                 int backing_size[2] = { 0 };
3535                 [view wine_setBackingSize:backing_size];
3536             }
3537             [(WineWindow*)[view window] updateForGLSubviews];
3538         }
3539     });
3541     [pool release];
3544 /***********************************************************************
3545  *              macdrv_set_view_superview
3547  * Move a view to a new superview and position it relative to its
3548  * siblings.  If p is non-NULL, the view is ordered behind it.
3549  * Otherwise, the view is ordered above n.  If s is NULL, use the
3550  * content view of w as the new superview.
3551  */
3552 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3554     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3555     WineContentView* view = (WineContentView*)v;
3556     WineContentView* superview = (WineContentView*)s;
3557     WineWindow* window = (WineWindow*)w;
3558     WineContentView* prev = (WineContentView*)p;
3559     WineContentView* next = (WineContentView*)n;
3561     if (!superview)
3562         superview = [window contentView];
3564     OnMainThreadAsync(^{
3565         if (superview == [view superview])
3566         {
3567             NSArray* subviews = [superview subviews];
3568             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3569             if (!prev && !next && index == [subviews count] - 1)
3570                 return;
3571             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3572                 return;
3573             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3574                 return;
3575         }
3577         WineWindow* oldWindow = (WineWindow*)[view window];
3578         WineWindow* newWindow = (WineWindow*)[superview window];
3580 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3581         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3582             [view removeFromSuperview];
3583 #endif
3584         if (prev)
3585             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3586         else
3587             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3589         if (oldWindow != newWindow)
3590         {
3591             [oldWindow updateForGLSubviews];
3592             [newWindow updateForGLSubviews];
3593         }
3594     });
3596     [pool release];
3599 /***********************************************************************
3600  *              macdrv_set_view_hidden
3601  */
3602 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3604     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3605     WineContentView* view = (WineContentView*)v;
3607     OnMainThreadAsync(^{
3608         [view setHidden:hidden];
3609         [(WineWindow*)view.window updateForGLSubviews];
3610     });
3612     [pool release];
3615 /***********************************************************************
3616  *              macdrv_add_view_opengl_context
3618  * Add an OpenGL context to the list being tracked for each view.
3619  */
3620 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3622     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3623     WineContentView* view = (WineContentView*)v;
3624     WineOpenGLContext *context = (WineOpenGLContext*)c;
3626     OnMainThread(^{
3627         [view addGLContext:context];
3628     });
3630     [pool release];
3633 /***********************************************************************
3634  *              macdrv_remove_view_opengl_context
3636  * Add an OpenGL context to the list being tracked for each view.
3637  */
3638 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3640     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3641     WineContentView* view = (WineContentView*)v;
3642     WineOpenGLContext *context = (WineOpenGLContext*)c;
3644     OnMainThreadAsync(^{
3645         [view removeGLContext:context];
3646     });
3648     [pool release];
3651 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3653     WineContentView* view = (WineContentView*)v;
3655     if (![view isKindOfClass:[WineContentView class]])
3656         return FALSE;
3658     [view wine_getBackingSize:backing_size];
3659     return TRUE;
3662 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3664     WineContentView* view = (WineContentView*)v;
3666     if ([view isKindOfClass:[WineContentView class]])
3667         [view wine_setBackingSize:backing_size];
3670 /***********************************************************************
3671  *              macdrv_window_background_color
3673  * Returns the standard Mac window background color as a 32-bit value of
3674  * the form 0x00rrggbb.
3675  */
3676 uint32_t macdrv_window_background_color(void)
3678     static uint32_t result;
3679     static dispatch_once_t once;
3681     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3682     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3683     // of it is to draw with it.
3684     dispatch_once(&once, ^{
3685         OnMainThread(^{
3686             unsigned char rgbx[4];
3687             unsigned char *planes = rgbx;
3688             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3689                                                                                pixelsWide:1
3690                                                                                pixelsHigh:1
3691                                                                             bitsPerSample:8
3692                                                                           samplesPerPixel:3
3693                                                                                  hasAlpha:NO
3694                                                                                  isPlanar:NO
3695                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3696                                                                              bitmapFormat:0
3697                                                                               bytesPerRow:4
3698                                                                              bitsPerPixel:32];
3699             [NSGraphicsContext saveGraphicsState];
3700             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3701             [[NSColor windowBackgroundColor] set];
3702             NSRectFill(NSMakeRect(0, 0, 1, 1));
3703             [NSGraphicsContext restoreGraphicsState];
3704             [bitmap release];
3705             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3706         });
3707     });
3709     return result;
3712 /***********************************************************************
3713  *              macdrv_send_text_input_event
3714  */
3715 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3717     OnMainThreadAsync(^{
3718         BOOL ret;
3719         macdrv_event* event;
3720         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3721         if (![window isKindOfClass:[WineWindow class]])
3722         {
3723             window = (WineWindow*)[NSApp mainWindow];
3724             if (![window isKindOfClass:[WineWindow class]])
3725                 window = [[WineApplicationController sharedController] frontWineWindow];
3726         }
3728         if (window)
3729         {
3730             NSUInteger localFlags = flags;
3731             CGEventRef c;
3732             NSEvent* event;
3734             window.imeData = data;
3735             fix_device_modifiers_by_generic(&localFlags);
3737             // An NSEvent created with +keyEventWithType:... is internally marked
3738             // as synthetic and doesn't get sent through input methods.  But one
3739             // created from a CGEvent doesn't have that problem.
3740             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3741             CGEventSetFlags(c, localFlags);
3742             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3743             event = [NSEvent eventWithCGEvent:c];
3744             CFRelease(c);
3746             window.commandDone = FALSE;
3747             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3748         }
3749         else
3750             ret = FALSE;
3752         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3753         event->sent_text_input.handled = ret;
3754         event->sent_text_input.done = done;
3755         [[window queue] postEvent:event];
3756         macdrv_release_event(event);
3757     });