user32: Add stub for SetThreadDpiAwarenessContext.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blobb94b7748adb3b5f28d4eea8d4c4cbad289ac696b
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) setFrame:(NSRect)frameRect display:(BOOL)flag
2460     {
2461         if (flag)
2462             [self setAutodisplay:YES];
2463         [super setFrame:frameRect display:flag];
2464     }
2466     - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2467     {
2468         if (displayFlag)
2469             [self setAutodisplay:YES];
2470         [super setFrame:frameRect display:displayFlag animate:animateFlag];
2471     }
2473     - (void) windowDidDrawContent
2474     {
2475         if (!drawnSinceShown)
2476         {
2477             drawnSinceShown = YES;
2478             dispatch_async(dispatch_get_main_queue(), ^{
2479                 [self checkTransparency];
2480             });
2481         }
2482     }
2484     - (NSArray*) childWineWindows
2485     {
2486         NSArray* childWindows = self.childWindows;
2487         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2488             return [child isKindOfClass:[WineWindow class]];
2489         }];
2490         return [childWindows objectsAtIndexes:indexes];
2491     }
2493     // We normally use the generic/calibrated RGB color space for the window,
2494     // rather than the device color space, to avoid expensive color conversion
2495     // which slows down drawing.  However, for windows displaying OpenGL, having
2496     // a different color space than the screen greatly reduces frame rates, often
2497     // limiting it to the display refresh rate.
2498     //
2499     // To avoid this, we switch back to the screen color space whenever the
2500     // window is covered by a view with an attached OpenGL context.
2501     - (void) updateColorSpace
2502     {
2503         NSRect contentRect = [[self contentView] frame];
2504         BOOL coveredByGLView = FALSE;
2505         WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2506         if ([view isKindOfClass:[WineContentView class]] && view.everHadGLContext)
2507         {
2508             NSRect frame = [view convertRect:[view bounds] toView:nil];
2509             if (NSContainsRect(frame, contentRect))
2510                 coveredByGLView = TRUE;
2511         }
2513         if (coveredByGLView)
2514             [self setColorSpace:nil];
2515         else
2516             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2517     }
2519     - (void) updateForGLSubviews
2520     {
2521         [self updateColorSpace];
2522         if (gl_surface_mode == GL_SURFACE_BEHIND)
2523             [self checkTransparency];
2524     }
2526     - (void) setRetinaMode:(int)mode
2527     {
2528         NSRect frame;
2529         double scale = mode ? 0.5 : 2.0;
2530         NSAffineTransform* transform = [NSAffineTransform transform];
2532         [transform scaleBy:scale];
2534         if (shape)
2535             [shape transformUsingAffineTransform:transform];
2537         for (WineContentView* subview in [self.contentView subviews])
2538         {
2539             if ([subview isKindOfClass:[WineContentView class]])
2540                 [subview setRetinaMode:mode];
2541         }
2543         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2544         frame.origin.x *= scale;
2545         frame.origin.y *= scale;
2546         frame.size.width *= scale;
2547         frame.size.height *= scale;
2548         frame = [self frameRectForContentRect:frame];
2550         savedContentMinSize = [transform transformSize:savedContentMinSize];
2551         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2552             savedContentMaxSize.width *= scale;
2553         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2554             savedContentMaxSize.height *= scale;
2556         self.contentMinSize = [transform transformSize:self.contentMinSize];
2557         NSSize temp = self.contentMaxSize;
2558         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2559             temp.width *= scale;
2560         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2561             temp.height *= scale;
2562         self.contentMaxSize = temp;
2564         ignore_windowResize = TRUE;
2565         [self setFrameAndWineFrame:frame];
2566         ignore_windowResize = FALSE;
2567     }
2570     /*
2571      * ---------- NSResponder method overrides ----------
2572      */
2573     - (void) keyDown:(NSEvent *)theEvent
2574     {
2575         if ([theEvent isARepeat])
2576         {
2577             if (!allowKeyRepeats)
2578                 return;
2579         }
2580         else
2581             allowKeyRepeats = YES;
2583         [self postKeyEvent:theEvent];
2584     }
2586     - (void) flagsChanged:(NSEvent *)theEvent
2587     {
2588         static const struct {
2589             NSUInteger  mask;
2590             uint16_t    keycode;
2591         } modifiers[] = {
2592             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2593             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2594             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2595             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2596             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2597             { NX_DEVICELALTKEYMASK,     kVK_Option },
2598             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2599             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2600             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2601         };
2603         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2604         NSUInteger changed;
2605         int i, last_changed;
2607         fix_device_modifiers_by_generic(&modifierFlags);
2608         changed = modifierFlags ^ lastModifierFlags;
2610         last_changed = -1;
2611         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2612             if (changed & modifiers[i].mask)
2613                 last_changed = i;
2615         for (i = 0; i <= last_changed; i++)
2616         {
2617             if (changed & modifiers[i].mask)
2618             {
2619                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2621                 if (pressed)
2622                     allowKeyRepeats = NO;
2624                 if (i == last_changed)
2625                     lastModifierFlags = modifierFlags;
2626                 else
2627                 {
2628                     lastModifierFlags ^= modifiers[i].mask;
2629                     fix_generic_modifiers_by_device(&lastModifierFlags);
2630                 }
2632                 // Caps lock generates one event for each press-release action.
2633                 // We need to simulate a pair of events for each actual event.
2634                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2635                 {
2636                     [self postKey:modifiers[i].keycode
2637                           pressed:TRUE
2638                         modifiers:lastModifierFlags
2639                             event:(NSEvent*)theEvent];
2640                     pressed = FALSE;
2641                 }
2643                 [self postKey:modifiers[i].keycode
2644                       pressed:pressed
2645                     modifiers:lastModifierFlags
2646                         event:(NSEvent*)theEvent];
2647             }
2648         }
2649     }
2651     - (void) applicationWillHide
2652     {
2653         savedVisibleState = [self isVisible];
2654     }
2656     - (void) applicationDidUnhide
2657     {
2658         if ([self isVisible])
2659             [self becameEligibleParentOrChild];
2660     }
2663     /*
2664      * ---------- NSWindowDelegate methods ----------
2665      */
2666     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2667     {
2668         macdrv_query* query;
2669         NSSize size;
2671         query = macdrv_create_query();
2672         query->type = QUERY_MIN_MAX_INFO;
2673         query->window = (macdrv_window)[self retain];
2674         [self.queue query:query timeout:0.5];
2675         macdrv_release_query(query);
2677         size = [self contentMaxSize];
2678         if (proposedSize.width < size.width)
2679             size.width = proposedSize.width;
2680         if (proposedSize.height < size.height)
2681             size.height = proposedSize.height;
2682         return size;
2683     }
2685     - (void)windowDidBecomeKey:(NSNotification *)notification
2686     {
2687         WineApplicationController* controller = [WineApplicationController sharedController];
2688         NSEvent* event = [controller lastFlagsChanged];
2689         if (event)
2690             [self flagsChanged:event];
2692         if (causing_becomeKeyWindow == self) return;
2694         [controller windowGotFocus:self];
2695     }
2697     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2698     {
2699         [self checkWineDisplayLink];
2700     }
2702     - (void) windowDidChangeScreen:(NSNotification*)notification
2703     {
2704         [self checkWineDisplayLink];
2705     }
2707     - (void)windowDidDeminiaturize:(NSNotification *)notification
2708     {
2709         WineApplicationController* controller = [WineApplicationController sharedController];
2711         if (!ignore_windowDeminiaturize)
2712             [self postDidUnminimizeEvent];
2713         ignore_windowDeminiaturize = FALSE;
2715         [self becameEligibleParentOrChild];
2717         if (fullscreen && [self isOnActiveSpace])
2718             [controller updateFullscreenWindows];
2719         [controller adjustWindowLevels];
2721         if (![self parentWindow])
2722             [self postBroughtForwardEvent];
2724         if (!self.disabled && !self.noActivate)
2725         {
2726             causing_becomeKeyWindow = self;
2727             [self makeKeyWindow];
2728             causing_becomeKeyWindow = nil;
2729             [controller windowGotFocus:self];
2730         }
2732         [self windowDidResize:notification];
2733         [self checkWineDisplayLink];
2734     }
2736     - (void) windowDidEndLiveResize:(NSNotification *)notification
2737     {
2738         if (!maximized)
2739         {
2740             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2741             [queue postEvent:event];
2742             macdrv_release_event(event);
2743         }
2744     }
2746     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2747     {
2748         enteringFullScreen = FALSE;
2749         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2750         if (pendingOrderOut)
2751             [self doOrderOut];
2752     }
2754     - (void) windowDidExitFullScreen:(NSNotification*)notification
2755     {
2756         exitingFullScreen = FALSE;
2757         [self setFrameAndWineFrame:nonFullscreenFrame];
2758         [self windowDidResize:nil];
2759         if (pendingOrderOut)
2760             [self doOrderOut];
2761     }
2763     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2764     {
2765         enteringFullScreen = FALSE;
2766         enteredFullScreenTime = 0;
2767         if (pendingOrderOut)
2768             [self doOrderOut];
2769     }
2771     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2772     {
2773         exitingFullScreen = FALSE;
2774         [self windowDidResize:nil];
2775         if (pendingOrderOut)
2776             [self doOrderOut];
2777     }
2779     - (void)windowDidMiniaturize:(NSNotification *)notification
2780     {
2781         if (fullscreen && [self isOnActiveSpace])
2782             [[WineApplicationController sharedController] updateFullscreenWindows];
2783         [self checkWineDisplayLink];
2784     }
2786     - (void)windowDidMove:(NSNotification *)notification
2787     {
2788         [self windowDidResize:notification];
2789     }
2791     - (void)windowDidResignKey:(NSNotification *)notification
2792     {
2793         macdrv_event* event;
2795         if (causing_becomeKeyWindow) return;
2797         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2798         [queue postEvent:event];
2799         macdrv_release_event(event);
2800     }
2802     - (void)windowDidResize:(NSNotification *)notification
2803     {
2804         NSRect frame = self.wine_fractionalFrame;
2806         if ([self inLiveResize])
2807         {
2808             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2809                 resizingFromLeft = TRUE;
2810             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2811                 resizingFromTop = TRUE;
2812         }
2814         if (ignore_windowResize || exitingFullScreen) return;
2816         if ([self preventResizing])
2817         {
2818             NSRect contentRect = [self contentRectForFrameRect:frame];
2819             [self setContentMinSize:contentRect.size];
2820             [self setContentMaxSize:contentRect.size];
2821         }
2823         [self postWindowFrameChanged:frame
2824                           fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2825                             resizing:[self inLiveResize]];
2827         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2828         [self updateFullscreen];
2829     }
2831     - (BOOL)windowShouldClose:(id)sender
2832     {
2833         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2834         [queue postEvent:event];
2835         macdrv_release_event(event);
2836         return NO;
2837     }
2839     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2840     {
2841         if (maximized)
2842         {
2843             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2844             [queue postEvent:event];
2845             macdrv_release_event(event);
2846             return NO;
2847         }
2848         else if (!resizable)
2849         {
2850             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2851             [queue postEvent:event];
2852             macdrv_release_event(event);
2853             return NO;
2854         }
2856         return YES;
2857     }
2859     - (void) windowWillClose:(NSNotification*)notification
2860     {
2861         WineWindow* child;
2863         if (fakingClose) return;
2864         if (latentParentWindow)
2865         {
2866             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2867             self.latentParentWindow = nil;
2868         }
2870         for (child in latentChildWindows)
2871         {
2872             if (child.latentParentWindow == self)
2873                 child.latentParentWindow = nil;
2874         }
2875         [latentChildWindows removeAllObjects];
2876     }
2878     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2879     {
2880         enteringFullScreen = TRUE;
2881         nonFullscreenFrame = self.wine_fractionalFrame;
2882     }
2884     - (void) windowWillExitFullScreen:(NSNotification*)notification
2885     {
2886         exitingFullScreen = TRUE;
2887         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2888     }
2890     - (void)windowWillMiniaturize:(NSNotification *)notification
2891     {
2892         [self becameIneligibleParentOrChild];
2893         [self grabDockIconSnapshotFromWindow:nil force:NO];
2894     }
2896     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2897     {
2898         if ([self inLiveResize])
2899         {
2900             if (maximized)
2901                 return self.wine_fractionalFrame.size;
2903             NSRect rect;
2904             macdrv_query* query;
2906             rect = [self frame];
2907             if (resizingFromLeft)
2908                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2909             if (!resizingFromTop)
2910                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2911             rect.size = frameSize;
2912             rect = [self contentRectForFrameRect:rect];
2913             [[WineApplicationController sharedController] flipRect:&rect];
2915             query = macdrv_create_query();
2916             query->type = QUERY_RESIZE_SIZE;
2917             query->window = (macdrv_window)[self retain];
2918             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2919             query->resize_size.from_left = resizingFromLeft;
2920             query->resize_size.from_top = resizingFromTop;
2922             if ([self.queue query:query timeout:0.1])
2923             {
2924                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2925                 rect = [self frameRectForContentRect:rect];
2926                 frameSize = rect.size;
2927             }
2929             macdrv_release_query(query);
2930         }
2932         return frameSize;
2933     }
2935     - (void) windowWillStartLiveResize:(NSNotification *)notification
2936     {
2937         [self endWindowDragging];
2939         if (maximized)
2940         {
2941             macdrv_event* event;
2942             NSRect frame = [self contentRectForFrameRect:self.frame];
2944             [[WineApplicationController sharedController] flipRect:&frame];
2946             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2947             event->window_restore_requested.keep_frame = TRUE;
2948             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2949             [queue postEvent:event];
2950             macdrv_release_event(event);
2951         }
2952         else
2953             [self sendResizeStartQuery];
2955         frameAtResizeStart = [self frame];
2956         resizingFromLeft = resizingFromTop = FALSE;
2957     }
2959     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2960     {
2961         macdrv_query* query;
2962         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2963         NSSize maxSize;
2965         query = macdrv_create_query();
2966         query->type = QUERY_MIN_MAX_INFO;
2967         query->window = (macdrv_window)[self retain];
2968         [self.queue query:query timeout:0.5];
2969         macdrv_release_query(query);
2971         currentContentRect = [self contentRectForFrameRect:[self frame]];
2972         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2974         maxSize = [self contentMaxSize];
2975         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2976         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2978         // Try to keep the top-left corner where it is.
2979         newContentRect.origin.x = NSMinX(currentContentRect);
2980         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2982         // If that pushes the bottom or right off the screen, pull it up and to the left.
2983         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2984         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2985             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2986         if (NSMinY(newContentRect) < NSMinY(screenRect))
2987             newContentRect.origin.y = NSMinY(screenRect);
2989         // If that pushes the top or left off the screen, push it down and the right
2990         // again.  Do this last because the top-left corner is more important than the
2991         // bottom-right.
2992         if (NSMinX(newContentRect) < NSMinX(screenRect))
2993             newContentRect.origin.x = NSMinX(screenRect);
2994         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2995             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2997         return [self frameRectForContentRect:newContentRect];
2998     }
3001     /*
3002      * ---------- NSPasteboardOwner methods ----------
3003      */
3004     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3005     {
3006         macdrv_query* query = macdrv_create_query();
3007         query->type = QUERY_PASTEBOARD_DATA;
3008         query->window = (macdrv_window)[self retain];
3009         query->pasteboard_data.type = (CFStringRef)[type copy];
3011         [self.queue query:query timeout:3];
3012         macdrv_release_query(query);
3013     }
3015     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3016     {
3017         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3018         [queue postEvent:event];
3019         macdrv_release_event(event);
3020     }
3023     /*
3024      * ---------- NSDraggingDestination methods ----------
3025      */
3026     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3027     {
3028         return [self draggingUpdated:sender];
3029     }
3031     - (void) draggingExited:(id <NSDraggingInfo>)sender
3032     {
3033         // This isn't really a query.  We don't need any response.  However, it
3034         // has to be processed in a similar manner as the other drag-and-drop
3035         // queries in order to maintain the proper order of operations.
3036         macdrv_query* query = macdrv_create_query();
3037         query->type = QUERY_DRAG_EXITED;
3038         query->window = (macdrv_window)[self retain];
3040         [self.queue query:query timeout:0.1];
3041         macdrv_release_query(query);
3042     }
3044     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3045     {
3046         NSDragOperation ret;
3047         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3048         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3049         NSPasteboard* pb = [sender draggingPasteboard];
3051         macdrv_query* query = macdrv_create_query();
3052         query->type = QUERY_DRAG_OPERATION;
3053         query->window = (macdrv_window)[self retain];
3054         query->drag_operation.x = floor(cgpt.x);
3055         query->drag_operation.y = floor(cgpt.y);
3056         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3057         query->drag_operation.accepted_op = NSDragOperationNone;
3058         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3060         [self.queue query:query timeout:3];
3061         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3062         macdrv_release_query(query);
3064         return ret;
3065     }
3067     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3068     {
3069         BOOL ret;
3070         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3071         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3072         NSPasteboard* pb = [sender draggingPasteboard];
3074         macdrv_query* query = macdrv_create_query();
3075         query->type = QUERY_DRAG_DROP;
3076         query->window = (macdrv_window)[self retain];
3077         query->drag_drop.x = floor(cgpt.x);
3078         query->drag_drop.y = floor(cgpt.y);
3079         query->drag_drop.op = [sender draggingSourceOperationMask];
3080         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3082         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3083         ret = query->status;
3084         macdrv_release_query(query);
3086         return ret;
3087     }
3089     - (BOOL) wantsPeriodicDraggingUpdates
3090     {
3091         return NO;
3092     }
3094 @end
3097 /***********************************************************************
3098  *              macdrv_create_cocoa_window
3100  * Create a Cocoa window with the given content frame and features (e.g.
3101  * title bar, close box, etc.).
3102  */
3103 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3104         CGRect frame, void* hwnd, macdrv_event_queue queue)
3106     __block WineWindow* window;
3108     OnMainThread(^{
3109         window = [[WineWindow createWindowWithFeatures:wf
3110                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3111                                                   hwnd:hwnd
3112                                                  queue:(WineEventQueue*)queue] retain];
3113     });
3115     return (macdrv_window)window;
3118 /***********************************************************************
3119  *              macdrv_destroy_cocoa_window
3121  * Destroy a Cocoa window.
3122  */
3123 void macdrv_destroy_cocoa_window(macdrv_window w)
3125     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3126     WineWindow* window = (WineWindow*)w;
3128     OnMainThread(^{
3129         [window doOrderOut];
3130         [window close];
3131     });
3132     [window.queue discardEventsMatchingMask:-1 forWindow:window];
3133     [window release];
3135     [pool release];
3138 /***********************************************************************
3139  *              macdrv_get_window_hwnd
3141  * Get the hwnd that was set for the window at creation.
3142  */
3143 void* macdrv_get_window_hwnd(macdrv_window w)
3145     WineWindow* window = (WineWindow*)w;
3146     return window.hwnd;
3149 /***********************************************************************
3150  *              macdrv_set_cocoa_window_features
3152  * Update a Cocoa window's features.
3153  */
3154 void macdrv_set_cocoa_window_features(macdrv_window w,
3155         const struct macdrv_window_features* wf)
3157     WineWindow* window = (WineWindow*)w;
3159     OnMainThread(^{
3160         [window setWindowFeatures:wf];
3161     });
3164 /***********************************************************************
3165  *              macdrv_set_cocoa_window_state
3167  * Update a Cocoa window's state.
3168  */
3169 void macdrv_set_cocoa_window_state(macdrv_window w,
3170         const struct macdrv_window_state* state)
3172     WineWindow* window = (WineWindow*)w;
3174     OnMainThread(^{
3175         [window setMacDrvState:state];
3176     });
3179 /***********************************************************************
3180  *              macdrv_set_cocoa_window_title
3182  * Set a Cocoa window's title.
3183  */
3184 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3185         size_t length)
3187     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3188     WineWindow* window = (WineWindow*)w;
3189     NSString* titleString;
3191     if (title)
3192         titleString = [NSString stringWithCharacters:title length:length];
3193     else
3194         titleString = @"";
3195     OnMainThreadAsync(^{
3196         [window setTitle:titleString];
3197         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3198             [NSApp changeWindowsItem:window title:titleString filename:NO];
3199     });
3201     [pool release];
3204 /***********************************************************************
3205  *              macdrv_order_cocoa_window
3207  * Reorder a Cocoa window relative to other windows.  If prev is
3208  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3209  * it is ordered above that window.  Otherwise, it is ordered to the
3210  * front.
3211  */
3212 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3213         macdrv_window n, int activate)
3215     WineWindow* window = (WineWindow*)w;
3216     WineWindow* prev = (WineWindow*)p;
3217     WineWindow* next = (WineWindow*)n;
3219     OnMainThreadAsync(^{
3220         [window orderBelow:prev
3221                    orAbove:next
3222                   activate:activate];
3223     });
3224     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3225                                   forWindow:window];
3226     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3227                                 forWindow:next];
3230 /***********************************************************************
3231  *              macdrv_hide_cocoa_window
3233  * Hides a Cocoa window.
3234  */
3235 void macdrv_hide_cocoa_window(macdrv_window w)
3237     WineWindow* window = (WineWindow*)w;
3239     OnMainThread(^{
3240         [window doOrderOut];
3241     });
3244 /***********************************************************************
3245  *              macdrv_set_cocoa_window_frame
3247  * Move a Cocoa window.
3248  */
3249 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3251     WineWindow* window = (WineWindow*)w;
3253     OnMainThread(^{
3254         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3255     });
3258 /***********************************************************************
3259  *              macdrv_get_cocoa_window_frame
3261  * Gets the frame of a Cocoa window.
3262  */
3263 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3265     WineWindow* window = (WineWindow*)w;
3267     OnMainThread(^{
3268         NSRect frame;
3270         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3271         [[WineApplicationController sharedController] flipRect:&frame];
3272         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3273     });
3276 /***********************************************************************
3277  *              macdrv_set_cocoa_parent_window
3279  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3280  * the parent window.
3281  */
3282 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3284     WineWindow* window = (WineWindow*)w;
3286     OnMainThread(^{
3287         [window setMacDrvParentWindow:(WineWindow*)parent];
3288     });
3291 /***********************************************************************
3292  *              macdrv_set_window_surface
3293  */
3294 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3296     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3297     WineWindow* window = (WineWindow*)w;
3299     OnMainThread(^{
3300         window.surface = surface;
3301         window.surface_mutex = mutex;
3302     });
3304     [pool release];
3307 /***********************************************************************
3308  *              macdrv_window_needs_display
3310  * Mark a window as needing display in a specified rect (in non-client
3311  * area coordinates).
3312  */
3313 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3315     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3316     WineWindow* window = (WineWindow*)w;
3318     OnMainThreadAsync(^{
3319         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3320     });
3322     [pool release];
3325 /***********************************************************************
3326  *              macdrv_set_window_shape
3328  * Sets the shape of a Cocoa window from an array of rectangles.  If
3329  * rects is NULL, resets the window's shape to its frame.
3330  */
3331 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3333     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3334     WineWindow* window = (WineWindow*)w;
3336     OnMainThread(^{
3337         if (!rects || !count)
3338         {
3339             window.shape = nil;
3340             window.shapeData = nil;
3341             [window checkEmptyShaped];
3342         }
3343         else
3344         {
3345             size_t length = sizeof(*rects) * count;
3346             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3347             {
3348                 NSBezierPath* path;
3349                 unsigned int i;
3351                 path = [NSBezierPath bezierPath];
3352                 for (i = 0; i < count; i++)
3353                     [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3354                 window.shape = path;
3355                 window.shapeData = [NSData dataWithBytes:rects length:length];
3356                 [window checkEmptyShaped];
3357             }
3358         }
3359     });
3361     [pool release];
3364 /***********************************************************************
3365  *              macdrv_set_window_alpha
3366  */
3367 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3369     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3370     WineWindow* window = (WineWindow*)w;
3372     [window setAlphaValue:alpha];
3374     [pool release];
3377 /***********************************************************************
3378  *              macdrv_set_window_color_key
3379  */
3380 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3381                                  CGFloat keyBlue)
3383     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3384     WineWindow* window = (WineWindow*)w;
3386     OnMainThread(^{
3387         window.colorKeyed       = TRUE;
3388         window.colorKeyRed      = keyRed;
3389         window.colorKeyGreen    = keyGreen;
3390         window.colorKeyBlue     = keyBlue;
3391         [window checkTransparency];
3392     });
3394     [pool release];
3397 /***********************************************************************
3398  *              macdrv_clear_window_color_key
3399  */
3400 void macdrv_clear_window_color_key(macdrv_window w)
3402     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3403     WineWindow* window = (WineWindow*)w;
3405     OnMainThread(^{
3406         window.colorKeyed = FALSE;
3407         [window checkTransparency];
3408     });
3410     [pool release];
3413 /***********************************************************************
3414  *              macdrv_window_use_per_pixel_alpha
3415  */
3416 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3418     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3419     WineWindow* window = (WineWindow*)w;
3421     OnMainThread(^{
3422         window.usePerPixelAlpha = use_per_pixel_alpha;
3423         [window checkTransparency];
3424     });
3426     [pool release];
3429 /***********************************************************************
3430  *              macdrv_give_cocoa_window_focus
3432  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3433  * orders it front and, if its frame was not within the desktop bounds,
3434  * Cocoa will typically move it on-screen.
3435  */
3436 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3438     WineWindow* window = (WineWindow*)w;
3440     OnMainThread(^{
3441         [window makeFocused:activate];
3442     });
3445 /***********************************************************************
3446  *              macdrv_set_window_min_max_sizes
3448  * Sets the window's minimum and maximum content sizes.
3449  */
3450 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3452     WineWindow* window = (WineWindow*)w;
3454     OnMainThread(^{
3455         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3456     });
3459 /***********************************************************************
3460  *              macdrv_create_view
3462  * Creates and returns a view with the specified frame rect.  The
3463  * caller is responsible for calling macdrv_dispose_view() on the view
3464  * when it is done with it.
3465  */
3466 macdrv_view macdrv_create_view(CGRect rect)
3468     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3469     __block WineContentView* view;
3471     if (CGRectIsNull(rect)) rect = CGRectZero;
3473     OnMainThread(^{
3474         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3476         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3477         [view setAutoresizesSubviews:NO];
3478         [view setHidden:YES];
3479         [nc addObserver:view
3480                selector:@selector(updateGLContexts)
3481                    name:NSViewGlobalFrameDidChangeNotification
3482                  object:view];
3483         [nc addObserver:view
3484                selector:@selector(updateGLContexts)
3485                    name:NSApplicationDidChangeScreenParametersNotification
3486                  object:NSApp];
3487     });
3489     [pool release];
3490     return (macdrv_view)view;
3493 /***********************************************************************
3494  *              macdrv_dispose_view
3496  * Destroys a view previously returned by macdrv_create_view.
3497  */
3498 void macdrv_dispose_view(macdrv_view v)
3500     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3501     WineContentView* view = (WineContentView*)v;
3503     OnMainThread(^{
3504         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3505         WineWindow* window = (WineWindow*)[view window];
3507         [nc removeObserver:view
3508                       name:NSViewGlobalFrameDidChangeNotification
3509                     object:view];
3510         [nc removeObserver:view
3511                       name:NSApplicationDidChangeScreenParametersNotification
3512                     object:NSApp];
3513         [view removeFromSuperview];
3514         [view release];
3515         [window updateForGLSubviews];
3516     });
3518     [pool release];
3521 /***********************************************************************
3522  *              macdrv_set_view_frame
3523  */
3524 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3526     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3527     WineContentView* view = (WineContentView*)v;
3529     if (CGRectIsNull(rect)) rect = CGRectZero;
3531     OnMainThreadAsync(^{
3532         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3533         NSRect oldFrame = [view frame];
3535         if (!NSEqualRects(oldFrame, newFrame))
3536         {
3537             [[view superview] setNeedsDisplayInRect:oldFrame];
3538             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3539                 [view setFrameSize:newFrame.size];
3540             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3541                 [view setFrameOrigin:newFrame.origin];
3542             else
3543                 [view setFrame:newFrame];
3544             [view setNeedsDisplay:YES];
3546             if (retina_enabled)
3547             {
3548                 int backing_size[2] = { 0 };
3549                 [view wine_setBackingSize:backing_size];
3550             }
3551             [(WineWindow*)[view window] updateForGLSubviews];
3552         }
3553     });
3555     [pool release];
3558 /***********************************************************************
3559  *              macdrv_set_view_superview
3561  * Move a view to a new superview and position it relative to its
3562  * siblings.  If p is non-NULL, the view is ordered behind it.
3563  * Otherwise, the view is ordered above n.  If s is NULL, use the
3564  * content view of w as the new superview.
3565  */
3566 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3568     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3569     WineContentView* view = (WineContentView*)v;
3570     WineContentView* superview = (WineContentView*)s;
3571     WineWindow* window = (WineWindow*)w;
3572     WineContentView* prev = (WineContentView*)p;
3573     WineContentView* next = (WineContentView*)n;
3575     if (!superview)
3576         superview = [window contentView];
3578     OnMainThreadAsync(^{
3579         if (superview == [view superview])
3580         {
3581             NSArray* subviews = [superview subviews];
3582             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3583             if (!prev && !next && index == [subviews count] - 1)
3584                 return;
3585             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3586                 return;
3587             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3588                 return;
3589         }
3591         WineWindow* oldWindow = (WineWindow*)[view window];
3592         WineWindow* newWindow = (WineWindow*)[superview window];
3594 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3595         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3596             [view removeFromSuperview];
3597 #endif
3598         if (prev)
3599             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3600         else
3601             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3603         if (oldWindow != newWindow)
3604         {
3605             [oldWindow updateForGLSubviews];
3606             [newWindow updateForGLSubviews];
3607         }
3608     });
3610     [pool release];
3613 /***********************************************************************
3614  *              macdrv_set_view_hidden
3615  */
3616 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3618     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3619     WineContentView* view = (WineContentView*)v;
3621     OnMainThreadAsync(^{
3622         [view setHidden:hidden];
3623         [(WineWindow*)view.window updateForGLSubviews];
3624     });
3626     [pool release];
3629 /***********************************************************************
3630  *              macdrv_add_view_opengl_context
3632  * Add an OpenGL context to the list being tracked for each view.
3633  */
3634 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3636     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3637     WineContentView* view = (WineContentView*)v;
3638     WineOpenGLContext *context = (WineOpenGLContext*)c;
3640     OnMainThread(^{
3641         [view addGLContext:context];
3642     });
3644     [pool release];
3647 /***********************************************************************
3648  *              macdrv_remove_view_opengl_context
3650  * Add an OpenGL context to the list being tracked for each view.
3651  */
3652 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3654     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3655     WineContentView* view = (WineContentView*)v;
3656     WineOpenGLContext *context = (WineOpenGLContext*)c;
3658     OnMainThreadAsync(^{
3659         [view removeGLContext:context];
3660     });
3662     [pool release];
3665 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3667     WineContentView* view = (WineContentView*)v;
3669     if (![view isKindOfClass:[WineContentView class]])
3670         return FALSE;
3672     [view wine_getBackingSize:backing_size];
3673     return TRUE;
3676 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3678     WineContentView* view = (WineContentView*)v;
3680     if ([view isKindOfClass:[WineContentView class]])
3681         [view wine_setBackingSize:backing_size];
3684 /***********************************************************************
3685  *              macdrv_window_background_color
3687  * Returns the standard Mac window background color as a 32-bit value of
3688  * the form 0x00rrggbb.
3689  */
3690 uint32_t macdrv_window_background_color(void)
3692     static uint32_t result;
3693     static dispatch_once_t once;
3695     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3696     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3697     // of it is to draw with it.
3698     dispatch_once(&once, ^{
3699         OnMainThread(^{
3700             unsigned char rgbx[4];
3701             unsigned char *planes = rgbx;
3702             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3703                                                                                pixelsWide:1
3704                                                                                pixelsHigh:1
3705                                                                             bitsPerSample:8
3706                                                                           samplesPerPixel:3
3707                                                                                  hasAlpha:NO
3708                                                                                  isPlanar:NO
3709                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3710                                                                              bitmapFormat:0
3711                                                                               bytesPerRow:4
3712                                                                              bitsPerPixel:32];
3713             [NSGraphicsContext saveGraphicsState];
3714             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3715             [[NSColor windowBackgroundColor] set];
3716             NSRectFill(NSMakeRect(0, 0, 1, 1));
3717             [NSGraphicsContext restoreGraphicsState];
3718             [bitmap release];
3719             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3720         });
3721     });
3723     return result;
3726 /***********************************************************************
3727  *              macdrv_send_text_input_event
3728  */
3729 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3731     OnMainThreadAsync(^{
3732         BOOL ret;
3733         macdrv_event* event;
3734         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3735         if (![window isKindOfClass:[WineWindow class]])
3736         {
3737             window = (WineWindow*)[NSApp mainWindow];
3738             if (![window isKindOfClass:[WineWindow class]])
3739                 window = [[WineApplicationController sharedController] frontWineWindow];
3740         }
3742         if (window)
3743         {
3744             NSUInteger localFlags = flags;
3745             CGEventRef c;
3746             NSEvent* event;
3748             window.imeData = data;
3749             fix_device_modifiers_by_generic(&localFlags);
3751             // An NSEvent created with +keyEventWithType:... is internally marked
3752             // as synthetic and doesn't get sent through input methods.  But one
3753             // created from a CGEvent doesn't have that problem.
3754             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3755             CGEventSetFlags(c, localFlags);
3756             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3757             event = [NSEvent eventWithCGEvent:c];
3758             CFRelease(c);
3760             window.commandDone = FALSE;
3761             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3762         }
3763         else
3764             ret = FALSE;
3766         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3767         event->sent_text_input.handled = ret;
3768         event->sent_text_input.done = done;
3769         [[window queue] postEvent:event];
3770         macdrv_release_event(event);
3771     });