msvcp140: Implement _Stat and _Lstat.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blob65dc39240814494890ae325c7c525b82fcc9e112
1 /*
2  * MACDRV Cocoa window code
3  *
4  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
21 #import <Carbon/Carbon.h>
22 #import <CoreVideo/CoreVideo.h>
24 #import "cocoa_window.h"
26 #include "macdrv_cocoa.h"
27 #import "cocoa_app.h"
28 #import "cocoa_event.h"
29 #import "cocoa_opengl.h"
32 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
33 enum {
34     NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
35     NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
36     NSWindowFullScreenButton = 7,
37     NSFullScreenWindowMask = 1 << 14,
40 @interface NSWindow (WineFullScreenExtensions)
41     - (void) toggleFullScreen:(id)sender;
42 @end
43 #endif
46 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
47 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
48 enum {
49     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
51 #endif
54 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
56     NSUInteger style_mask;
58     if (wf->title_bar)
59     {
60         style_mask = NSTitledWindowMask;
61         if (wf->close_button) style_mask |= NSClosableWindowMask;
62         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
63         if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
64         if (wf->utility) style_mask |= NSUtilityWindowMask;
65     }
66     else style_mask = NSBorderlessWindowMask;
68     return style_mask;
72 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
74     NSScreen* screen;
75     for (screen in screens)
76     {
77         if (NSIntersectsRect(frame, [screen frame]))
78             return TRUE;
79     }
80     return FALSE;
84 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
86     for (NSScreen* screen in screens)
87     {
88         if (NSContainsRect(rect, [screen frame]))
89             return screen;
90     }
91     return nil;
95 /* We rely on the supposedly device-dependent modifier flags to distinguish the
96    keys on the left side of the keyboard from those on the right.  Some event
97    sources don't set those device-depdendent flags.  If we see a device-independent
98    flag for a modifier without either corresponding device-dependent flag, assume
99    the left one. */
100 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
102     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
103         *modifiers |= NX_DEVICELCMDKEYMASK;
104     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
105         *modifiers |= NX_DEVICELSHIFTKEYMASK;
106     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
107         *modifiers |= NX_DEVICELCTLKEYMASK;
108     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
109         *modifiers |= NX_DEVICELALTKEYMASK;
112 /* As we manipulate individual bits of a modifier mask, we can end up with
113    inconsistent sets of flags.  In particular, we might set or clear one of the
114    left/right-specific bits, but not the corresponding non-side-specific bit.
115    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
116    otherwise clear it. */
117 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
119     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
120         *modifiers |= NX_COMMANDMASK;
121     else
122         *modifiers &= ~NX_COMMANDMASK;
123     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
124         *modifiers |= NX_SHIFTMASK;
125     else
126         *modifiers &= ~NX_SHIFTMASK;
127     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
128         *modifiers |= NX_CONTROLMASK;
129     else
130         *modifiers &= ~NX_CONTROLMASK;
131     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
132         *modifiers |= NX_ALTERNATEMASK;
133     else
134         *modifiers &= ~NX_ALTERNATEMASK;
137 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
139     fix_device_modifiers_by_generic(&modifiers);
140     if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
141     {
142         modifiers |= NX_DEVICELCMDKEYMASK;
143         modifiers &= ~NX_DEVICELALTKEYMASK;
144     }
145     if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
146     {
147         modifiers |= NX_DEVICERCMDKEYMASK;
148         modifiers &= ~NX_DEVICERALTKEYMASK;
149     }
150     fix_generic_modifiers_by_device(&modifiers);
152     return modifiers;
156 @interface NSWindow (WineAccessPrivateMethods)
157     - (id) _displayChanged;
158 @end
161 @interface WineDisplayLink : NSObject
163     CGDirectDisplayID _displayID;
164     CVDisplayLinkRef _link;
165     NSMutableSet* _windows;
167     NSTimeInterval _actualRefreshPeriod;
168     NSTimeInterval _nominalRefreshPeriod;
171     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
173     - (void) addWindow:(WineWindow*)window;
174     - (void) removeWindow:(WineWindow*)window;
176     - (NSTimeInterval) refreshPeriod;
178     - (void) start;
180 @end
182 @implementation WineDisplayLink
184 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
186     - (id) initWithDisplayID:(CGDirectDisplayID)displayID
187     {
188         self = [super init];
189         if (self)
190         {
191             CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
192             if (status == kCVReturnSuccess && !_link)
193                 status = kCVReturnError;
194             if (status == kCVReturnSuccess)
195                 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
196             if (status != kCVReturnSuccess)
197             {
198                 [self release];
199                 return nil;
200             }
202             _displayID = displayID;
203             _windows = [[NSMutableSet alloc] init];
204         }
205         return self;
206     }
208     - (void) dealloc
209     {
210         if (_link)
211         {
212             CVDisplayLinkStop(_link);
213             CVDisplayLinkRelease(_link);
214         }
215         [_windows release];
216         [super dealloc];
217     }
219     - (void) addWindow:(WineWindow*)window
220     {
221         BOOL needsStart;
222         @synchronized(self) {
223             needsStart = !_windows.count;
224             [_windows addObject:window];
225         }
226         if (needsStart)
227             CVDisplayLinkStart(_link);
228     }
230     - (void) removeWindow:(WineWindow*)window
231     {
232         BOOL shouldStop = FALSE;
233         @synchronized(self) {
234             BOOL wasRunning = _windows.count > 0;
235             [_windows removeObject:window];
236             if (wasRunning && !_windows.count)
237                 shouldStop = TRUE;
238         }
239         if (shouldStop)
240             CVDisplayLinkStop(_link);
241     }
243     - (void) fire
244     {
245         NSSet* windows;
246         @synchronized(self) {
247             windows = [_windows copy];
248         }
249         dispatch_async(dispatch_get_main_queue(), ^{
250             BOOL anyDisplayed = FALSE;
251             for (WineWindow* window in windows)
252             {
253                 if ([window viewsNeedDisplay])
254                 {
255                     [window displayIfNeeded];
256                     anyDisplayed = YES;
257                 }
258             }
259             if (!anyDisplayed)
260                 CVDisplayLinkStop(_link);
261         });
262         [windows release];
263     }
265     - (NSTimeInterval) refreshPeriod
266     {
267         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
268             return _actualRefreshPeriod;
270         if (_nominalRefreshPeriod)
271             return _nominalRefreshPeriod;
273         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
274         if (time.flags & kCVTimeIsIndefinite)
275             return 1.0 / 60.0;
276         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
277         return _nominalRefreshPeriod;
278     }
280     - (void) start
281     {
282         CVDisplayLinkStart(_link);
283     }
285 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
287     WineDisplayLink* link = displayLinkContext;
288     [link fire];
289     return kCVReturnSuccess;
292 @end
295 @interface WineContentView : NSView <NSTextInputClient>
297     NSMutableArray* glContexts;
298     NSMutableArray* pendingGlContexts;
299     BOOL _everHadGLContext;
300     BOOL _cachedHasGLDescendant;
301     BOOL _cachedHasGLDescendantValid;
302     BOOL clearedGlSurface;
304     NSMutableAttributedString* markedText;
305     NSRange markedTextSelection;
307     int backingSize[2];
310 @property (readonly, nonatomic) BOOL everHadGLContext;
312     - (void) addGLContext:(WineOpenGLContext*)context;
313     - (void) removeGLContext:(WineOpenGLContext*)context;
314     - (void) updateGLContexts;
316     - (void) wine_getBackingSize:(int*)outBackingSize;
317     - (void) wine_setBackingSize:(const int*)newBackingSize;
319 @end
322 @interface WineWindow ()
324 @property (readwrite, nonatomic) BOOL disabled;
325 @property (readwrite, nonatomic) BOOL noActivate;
326 @property (readwrite, nonatomic) BOOL floating;
327 @property (readwrite, nonatomic) BOOL drawnSinceShown;
328 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
329 @property (retain, nonatomic) NSWindow* latentParentWindow;
331 @property (nonatomic) void* hwnd;
332 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
334 @property (nonatomic) void* surface;
335 @property (nonatomic) pthread_mutex_t* surface_mutex;
337 @property (copy, nonatomic) NSBezierPath* shape;
338 @property (copy, nonatomic) NSData* shapeData;
339 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
340 @property (readonly, nonatomic) BOOL needsTransparency;
342 @property (nonatomic) BOOL colorKeyed;
343 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
344 @property (nonatomic) BOOL usePerPixelAlpha;
346 @property (assign, nonatomic) void* imeData;
347 @property (nonatomic) BOOL commandDone;
349 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
351     - (void) updateColorSpace;
352     - (void) updateForGLSubviews;
354     - (BOOL) becameEligibleParentOrChild;
355     - (void) becameIneligibleChild;
357     - (void) windowDidDrawContent;
359 @end
362 @implementation WineContentView
364 @synthesize everHadGLContext = _everHadGLContext;
366     - (void) dealloc
367     {
368         [markedText release];
369         [glContexts release];
370         [pendingGlContexts release];
371         [super dealloc];
372     }
374     - (BOOL) isFlipped
375     {
376         return YES;
377     }
379     - (void) drawRect:(NSRect)rect
380     {
381         WineWindow* window = (WineWindow*)[self window];
383         for (WineOpenGLContext* context in pendingGlContexts)
384         {
385             if (!clearedGlSurface)
386             {
387                 context.shouldClearToBlack = TRUE;
388                 clearedGlSurface = TRUE;
389             }
390             context.needsUpdate = TRUE;
391         }
392         [glContexts addObjectsFromArray:pendingGlContexts];
393         [pendingGlContexts removeAllObjects];
395         if ([window contentView] != self)
396             return;
398         if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
399         {
400             [[NSColor clearColor] setFill];
401             NSRectFill(rect);
403             [window.shape addClip];
405             [[NSColor windowBackgroundColor] setFill];
406             NSRectFill(rect);
407         }
409         if (window.surface && window.surface_mutex &&
410             !pthread_mutex_lock(window.surface_mutex))
411         {
412             const CGRect* rects;
413             int count;
415             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
416             {
417                 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
418                 CGContextRef context;
419                 int i;
421                 [window.shape addClip];
423                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
424                 CGContextSetBlendMode(context, kCGBlendModeCopy);
425                 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
427                 for (i = 0; i < count; i++)
428                 {
429                     CGRect imageRect;
430                     CGImageRef image;
432                     imageRect = CGRectIntersection(rects[i], dirtyRect);
433                     image = create_surface_image(window.surface, &imageRect, FALSE);
435                     if (image)
436                     {
437                         if (window.colorKeyed)
438                         {
439                             CGImageRef maskedImage;
440                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
441                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
442                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
443                             maskedImage = CGImageCreateWithMaskingColors(image, components);
444                             if (maskedImage)
445                             {
446                                 CGImageRelease(image);
447                                 image = maskedImage;
448                             }
449                         }
451                         CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
453                         CGImageRelease(image);
454                     }
455                 }
457                 [window windowDidDrawContent];
458             }
460             pthread_mutex_unlock(window.surface_mutex);
461         }
463         // If the window may be transparent, then we have to invalidate the
464         // shadow every time we draw.  Also, if this is the first time we've
465         // drawn since changing from transparent to opaque.
466         if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
467         {
468             window.shapeChangedSinceLastDraw = FALSE;
469             [window invalidateShadow];
470         }
471     }
473     - (void) addGLContext:(WineOpenGLContext*)context
474     {
475         BOOL hadContext = _everHadGLContext;
476         if (!glContexts)
477             glContexts = [[NSMutableArray alloc] init];
478         if (!pendingGlContexts)
479             pendingGlContexts = [[NSMutableArray alloc] init];
481         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
482         {
483             [glContexts addObject:context];
484             if (!clearedGlSurface)
485             {
486                 context.shouldClearToBlack = TRUE;
487                 clearedGlSurface = TRUE;
488             }
489             context.needsUpdate = TRUE;
490         }
491         else
492         {
493             [pendingGlContexts addObject:context];
494             [self setNeedsDisplay:YES];
495         }
497         _everHadGLContext = YES;
498         if (!hadContext)
499             [self invalidateHasGLDescendant];
500         [(WineWindow*)[self window] updateForGLSubviews];
501     }
503     - (void) removeGLContext:(WineOpenGLContext*)context
504     {
505         [glContexts removeObjectIdenticalTo:context];
506         [pendingGlContexts removeObjectIdenticalTo:context];
507         [(WineWindow*)[self window] updateForGLSubviews];
508     }
510     - (void) updateGLContexts:(BOOL)reattach
511     {
512         for (WineOpenGLContext* context in glContexts)
513         {
514             context.needsUpdate = TRUE;
515             if (reattach)
516                 context.needsReattach = TRUE;
517         }
518     }
520     - (void) updateGLContexts
521     {
522         [self updateGLContexts:NO];
523     }
525     - (BOOL) _hasGLDescendant
526     {
527         if ([self isHidden])
528             return NO;
529         if (_everHadGLContext)
530             return YES;
531         for (WineContentView* view in [self subviews])
532         {
533             if ([view hasGLDescendant])
534                 return YES;
535         }
536         return NO;
537     }
539     - (BOOL) hasGLDescendant
540     {
541         if (!_cachedHasGLDescendantValid)
542         {
543             _cachedHasGLDescendant = [self _hasGLDescendant];
544             _cachedHasGLDescendantValid = YES;
545         }
546         return _cachedHasGLDescendant;
547     }
549     - (void) invalidateHasGLDescendant
550     {
551         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
552         _cachedHasGLDescendantValid = NO;
553         if (invalidateAncestors && self != [[self window] contentView])
554         {
555             WineContentView* superview = (WineContentView*)[self superview];
556             if ([superview isKindOfClass:[WineContentView class]])
557                 [superview invalidateHasGLDescendant];
558         }
559     }
561     - (void) wine_getBackingSize:(int*)outBackingSize
562     {
563         @synchronized(self) {
564             memcpy(outBackingSize, backingSize, sizeof(backingSize));
565         }
566     }
567     - (void) wine_setBackingSize:(const int*)newBackingSize
568     {
569         @synchronized(self) {
570             memcpy(backingSize, newBackingSize, sizeof(backingSize));
571         }
572     }
574     - (void) setRetinaMode:(int)mode
575     {
576         double scale = mode ? 0.5 : 2.0;
577         NSRect frame = self.frame;
578         frame.origin.x *= scale;
579         frame.origin.y *= scale;
580         frame.size.width *= scale;
581         frame.size.height *= scale;
582         [self setFrame:frame];
583         [self updateGLContexts];
585         for (WineContentView* subview in [self subviews])
586         {
587             if ([subview isKindOfClass:[WineContentView class]])
588                 [subview setRetinaMode:mode];
589         }
590     }
592     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
593     {
594         return YES;
595     }
597     - (BOOL) preservesContentDuringLiveResize
598     {
599         // Returning YES from this tells Cocoa to keep our view's content during
600         // a Cocoa-driven resize.  In theory, we're also supposed to override
601         // -setFrameSize: to mark exposed sections as needing redisplay, but
602         // user32 will take care of that in a roundabout way.  This way, we don't
603         // redraw until the window surface is flushed.
604         //
605         // This doesn't do anything when we resize the window ourselves.
606         return YES;
607     }
609     - (BOOL)acceptsFirstResponder
610     {
611         return [[self window] contentView] == self;
612     }
614     - (BOOL) mouseDownCanMoveWindow
615     {
616         return NO;
617     }
619     - (void) viewDidHide
620     {
621         [super viewDidHide];
622         [self invalidateHasGLDescendant];
623     }
625     - (void) viewDidUnhide
626     {
627         [super viewDidUnhide];
628         [self updateGLContexts:YES];
629         [self invalidateHasGLDescendant];
630     }
632     - (void) completeText:(NSString*)text
633     {
634         macdrv_event* event;
635         WineWindow* window = (WineWindow*)[self window];
637         event = macdrv_create_event(IM_SET_TEXT, window);
638         event->im_set_text.data = [window imeData];
639         event->im_set_text.text = (CFStringRef)[text copy];
640         event->im_set_text.complete = TRUE;
642         [[window queue] postEvent:event];
644         macdrv_release_event(event);
646         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
647         markedTextSelection = NSMakeRange(0, 0);
648         [[self inputContext] discardMarkedText];
649     }
651     - (NSFocusRingType) focusRingType
652     {
653         return NSFocusRingTypeNone;
654     }
656     - (void) didAddSubview:(NSView*)subview
657     {
658         if ([subview isKindOfClass:[WineContentView class]])
659         {
660             WineContentView* view = (WineContentView*)subview;
661             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
662                 [self invalidateHasGLDescendant];
663         }
664         [super didAddSubview:subview];
665     }
667     - (void) willRemoveSubview:(NSView*)subview
668     {
669         if ([subview isKindOfClass:[WineContentView class]])
670         {
671             WineContentView* view = (WineContentView*)subview;
672             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
673                 [self invalidateHasGLDescendant];
674         }
675         [super willRemoveSubview:subview];
676     }
678     /*
679      * ---------- NSTextInputClient methods ----------
680      */
681     - (NSTextInputContext*) inputContext
682     {
683         if (!markedText)
684             markedText = [[NSMutableAttributedString alloc] init];
685         return [super inputContext];
686     }
688     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
689     {
690         if ([string isKindOfClass:[NSAttributedString class]])
691             string = [string string];
693         if ([string isKindOfClass:[NSString class]])
694             [self completeText:string];
695     }
697     - (void) doCommandBySelector:(SEL)aSelector
698     {
699         [(WineWindow*)[self window] setCommandDone:TRUE];
700     }
702     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
703     {
704         if ([string isKindOfClass:[NSAttributedString class]])
705             string = [string string];
707         if ([string isKindOfClass:[NSString class]])
708         {
709             macdrv_event* event;
710             WineWindow* window = (WineWindow*)[self window];
712             if (replacementRange.location == NSNotFound)
713                 replacementRange = NSMakeRange(0, [markedText length]);
715             [markedText replaceCharactersInRange:replacementRange withString:string];
716             markedTextSelection = selectedRange;
717             markedTextSelection.location += replacementRange.location;
719             event = macdrv_create_event(IM_SET_TEXT, window);
720             event->im_set_text.data = [window imeData];
721             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
722             event->im_set_text.complete = FALSE;
723             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
725             [[window queue] postEvent:event];
727             macdrv_release_event(event);
729             [[self inputContext] invalidateCharacterCoordinates];
730         }
731     }
733     - (void) unmarkText
734     {
735         [self completeText:nil];
736     }
738     - (NSRange) selectedRange
739     {
740         return markedTextSelection;
741     }
743     - (NSRange) markedRange
744     {
745         NSRange range = NSMakeRange(0, [markedText length]);
746         if (!range.length)
747             range.location = NSNotFound;
748         return range;
749     }
751     - (BOOL) hasMarkedText
752     {
753         return [markedText length] > 0;
754     }
756     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
757     {
758         if (aRange.location >= [markedText length])
759             return nil;
761         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
762         if (actualRange)
763             *actualRange = aRange;
764         return [markedText attributedSubstringFromRange:aRange];
765     }
767     - (NSArray*) validAttributesForMarkedText
768     {
769         return [NSArray array];
770     }
772     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
773     {
774         macdrv_query* query;
775         WineWindow* window = (WineWindow*)[self window];
776         NSRect ret;
778         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
780         query = macdrv_create_query();
781         query->type = QUERY_IME_CHAR_RECT;
782         query->window = (macdrv_window)[window retain];
783         query->ime_char_rect.data = [window imeData];
784         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
786         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
787         {
788             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
789             ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
790             [[WineApplicationController sharedController] flipRect:&ret];
791         }
792         else
793             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
795         macdrv_release_query(query);
797         if (actualRange)
798             *actualRange = aRange;
799         return ret;
800     }
802     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
803     {
804         return NSNotFound;
805     }
807     - (NSInteger) windowLevel
808     {
809         return [[self window] level];
810     }
812 @end
815 @implementation WineWindow
817     static WineWindow* causing_becomeKeyWindow;
819     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
820     @synthesize drawnSinceShown;
821     @synthesize surface, surface_mutex;
822     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
823     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
824     @synthesize usePerPixelAlpha;
825     @synthesize imeData, commandDone;
827     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
828                                  windowFrame:(NSRect)window_frame
829                                         hwnd:(void*)hwnd
830                                        queue:(WineEventQueue*)queue
831     {
832         WineWindow* window;
833         WineContentView* contentView;
834         NSTrackingArea* trackingArea;
835         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
837         [[WineApplicationController sharedController] flipRect:&window_frame];
839         window = [[[self alloc] initWithContentRect:window_frame
840                                           styleMask:style_mask_for_features(wf)
841                                             backing:NSBackingStoreBuffered
842                                               defer:YES] autorelease];
844         if (!window) return nil;
846         /* Standardize windows to eliminate differences between titled and
847            borderless windows and between NSWindow and NSPanel. */
848         [window setHidesOnDeactivate:NO];
849         [window setReleasedWhenClosed:NO];
851         [window setOneShot:YES];
852         [window disableCursorRects];
853         [window setShowsResizeIndicator:NO];
854         [window setHasShadow:wf->shadow];
855         [window setAcceptsMouseMovedEvents:YES];
856         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
857         [window setDelegate:window];
858         [window setBackgroundColor:[NSColor clearColor]];
859         [window setOpaque:NO];
860         window.hwnd = hwnd;
861         window.queue = queue;
862         window->savedContentMinSize = NSZeroSize;
863         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
864         window->resizable = wf->resizable;
865         window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
867         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
868                                                                   (NSString*)kUTTypeContent,
869                                                                   nil]];
871         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
872         if (!contentView)
873             return nil;
874         [contentView setAutoresizesSubviews:NO];
876         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
877            because they give us mouse moves in the background. */
878         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
879                                                      options:(NSTrackingMouseMoved |
880                                                               NSTrackingActiveAlways |
881                                                               NSTrackingInVisibleRect)
882                                                        owner:window
883                                                     userInfo:nil] autorelease];
884         if (!trackingArea)
885             return nil;
886         [contentView addTrackingArea:trackingArea];
888         [window setContentView:contentView];
889         [window setInitialFirstResponder:contentView];
891         [nc addObserver:window
892                selector:@selector(updateFullscreen)
893                    name:NSApplicationDidChangeScreenParametersNotification
894                  object:NSApp];
895         [window updateFullscreen];
897         [nc addObserver:window
898                selector:@selector(applicationWillHide)
899                    name:NSApplicationWillHideNotification
900                  object:NSApp];
901         [nc addObserver:window
902                selector:@selector(applicationDidUnhide)
903                    name:NSApplicationDidUnhideNotification
904                  object:NSApp];
906         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
907                                                               selector:@selector(checkWineDisplayLink)
908                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
909                                                                 object:[NSWorkspace sharedWorkspace]];
911         [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
913         return window;
914     }
916     - (void) dealloc
917     {
918         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
919         [[NSNotificationCenter defaultCenter] removeObserver:self];
920         [queue release];
921         [latentChildWindows release];
922         [latentParentWindow release];
923         [shape release];
924         [shapeData release];
925         [super dealloc];
926     }
928     - (BOOL) preventResizing
929     {
930         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
931         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
932     }
934     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
935     {
936         if (allow_immovable_windows && (disabled || inMaximized))
937             return NO;
938         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
939             return NO;
940         else
941             return YES;
942     }
944     - (void) adjustFeaturesForState
945     {
946         NSUInteger style = [self styleMask];
948         if (style & NSClosableWindowMask)
949             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
950         if (style & NSMiniaturizableWindowMask)
951             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
952         if (style & NSResizableWindowMask)
953             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
954         if ([self respondsToSelector:@selector(toggleFullScreen:)])
955         {
956             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
957                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
958         }
960         if ([self preventResizing])
961         {
962             NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
963             [self setContentMinSize:size];
964             [self setContentMaxSize:size];
965         }
966         else
967         {
968             [self setContentMaxSize:savedContentMaxSize];
969             [self setContentMinSize:savedContentMinSize];
970         }
972         if (allow_immovable_windows || cursor_clipping_locks_windows)
973             [self setMovable:[self allowsMovingWithMaximized:maximized]];
974     }
976     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
977     {
978         if ([self respondsToSelector:@selector(toggleFullScreen:)])
979         {
980             NSUInteger style = [self styleMask];
982             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
983                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
984                 !(self.parentWindow || self.latentParentWindow))
985             {
986                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
987                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
988             }
989             else
990             {
991                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
992                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
993                 if (style & NSFullScreenWindowMask)
994                     [super toggleFullScreen:nil];
995             }
996         }
998         if (behavior != [self collectionBehavior])
999         {
1000             [self setCollectionBehavior:behavior];
1001             [self adjustFeaturesForState];
1002         }
1003     }
1005     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1006     {
1007         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1008                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1009         NSUInteger currentStyle = [self styleMask];
1010         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1012         if (newStyle != currentStyle)
1013         {
1014             NSString* title = [[[self title] copy] autorelease];
1015             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1016             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1017             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1018             {
1019                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1020                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1021                 // just NSTitledWindowMask, the window buttons should disappear rather
1022                 // than just being disabled.  But they don't.  Similarly in reverse.
1023                 // The workaround is to also toggle NSClosableWindowMask at the same time.
1024                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1025             }
1026             [self setStyleMask:newStyle];
1028             // -setStyleMask: resets the firstResponder to the window.  Set it
1029             // back to the content view.
1030             if ([[self contentView] acceptsFirstResponder])
1031                 [self makeFirstResponder:[self contentView]];
1033             [self adjustFullScreenBehavior:[self collectionBehavior]];
1035             if ([[self title] length] == 0 && [title length] > 0)
1036                 [self setTitle:title];
1037         }
1039         resizable = wf->resizable;
1040         [self adjustFeaturesForState];
1041         [self setHasShadow:wf->shadow];
1042     }
1044     // Indicates if the window would be visible if the app were not hidden.
1045     - (BOOL) wouldBeVisible
1046     {
1047         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1048     }
1050     - (BOOL) isOrderedIn
1051     {
1052         return [self wouldBeVisible] || [self isMiniaturized];
1053     }
1055     - (NSInteger) minimumLevelForActive:(BOOL)active
1056     {
1057         NSInteger level;
1059         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1060                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1061             level = NSFloatingWindowLevel;
1062         else
1063             level = NSNormalWindowLevel;
1065         if (active)
1066         {
1067             BOOL captured;
1069             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1071             if (captured || fullscreen)
1072             {
1073                 if (captured)
1074                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1075                 else
1076                     level = NSStatusWindowLevel + 1;
1078                 if (self.floating)
1079                     level++;
1080             }
1081         }
1083         return level;
1084     }
1086     - (void) postDidUnminimizeEvent
1087     {
1088         macdrv_event* event;
1090         /* Coalesce events by discarding any previous ones still in the queue. */
1091         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1092                                forWindow:self];
1094         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1095         [queue postEvent:event];
1096         macdrv_release_event(event);
1097     }
1099     - (void) sendResizeStartQuery
1100     {
1101         macdrv_query* query = macdrv_create_query();
1102         query->type = QUERY_RESIZE_START;
1103         query->window = (macdrv_window)[self retain];
1105         [self.queue query:query timeout:0.3];
1106         macdrv_release_query(query);
1107     }
1109     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1110     {
1111         NSWindowCollectionBehavior behavior;
1113         self.disabled = state->disabled;
1114         self.noActivate = state->no_activate;
1116         if (self.floating != state->floating)
1117         {
1118             self.floating = state->floating;
1119             if (state->floating)
1120             {
1121                 // Became floating.  If child of non-floating window, make that
1122                 // relationship latent.
1123                 WineWindow* parent = (WineWindow*)[self parentWindow];
1124                 if (parent && !parent.floating)
1125                     [self becameIneligibleChild];
1126             }
1127             else
1128             {
1129                 // Became non-floating.  If parent of floating children, make that
1130                 // relationship latent.
1131                 WineWindow* child;
1132                 for (child in [self childWineWindows])
1133                 {
1134                     if (child.floating)
1135                         [child becameIneligibleChild];
1136                 }
1137             }
1139             // Check our latent relationships.  If floating status was the only
1140             // reason they were latent, then make them active.
1141             if ([self isVisible])
1142                 [self becameEligibleParentOrChild];
1144             [[WineApplicationController sharedController] adjustWindowLevels];
1145         }
1147         if (state->minimized_valid)
1148         {
1149             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1151             pendingMinimize = FALSE;
1152             if (state->minimized && ![self isMiniaturized])
1153             {
1154                 if ([self wouldBeVisible])
1155                 {
1156                     if ([self styleMask] & NSFullScreenWindowMask)
1157                     {
1158                         [self postDidUnminimizeEvent];
1159                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1160                     }
1161                     else
1162                     {
1163                         [super miniaturize:nil];
1164                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1165                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1166                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1167                     }
1168                 }
1169                 else
1170                     pendingMinimize = TRUE;
1171             }
1172             else if (!state->minimized && [self isMiniaturized])
1173             {
1174                 ignore_windowDeminiaturize = TRUE;
1175                 [self deminiaturize:nil];
1176                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1177             }
1179             if (discard)
1180                 [queue discardEventsMatchingMask:discard forWindow:self];
1181         }
1183         if (state->maximized != maximized)
1184         {
1185             maximized = state->maximized;
1186             [self adjustFeaturesForState];
1188             if (!maximized && [self inLiveResize])
1189                 [self sendResizeStartQuery];
1190         }
1192         behavior = NSWindowCollectionBehaviorDefault;
1193         if (state->excluded_by_expose)
1194             behavior |= NSWindowCollectionBehaviorTransient;
1195         else
1196             behavior |= NSWindowCollectionBehaviorManaged;
1197         if (state->excluded_by_cycle)
1198         {
1199             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1200             if ([self isOrderedIn])
1201                 [NSApp removeWindowsItem:self];
1202         }
1203         else
1204         {
1205             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1206             if ([self isOrderedIn])
1207                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1208         }
1209         [self adjustFullScreenBehavior:behavior];
1210     }
1212     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1213     {
1214         BOOL reordered = FALSE;
1216         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1217         {
1218             if ([self level] > [child level])
1219                 [child setLevel:[self level]];
1220             if (![child isVisible])
1221                 [child setAutodisplay:YES];
1222             [self addChildWindow:child ordered:NSWindowAbove];
1223             [child checkWineDisplayLink];
1224             [latentChildWindows removeObjectIdenticalTo:child];
1225             child.latentParentWindow = nil;
1226             reordered = TRUE;
1227         }
1228         else
1229         {
1230             if (!latentChildWindows)
1231                 latentChildWindows = [[NSMutableArray alloc] init];
1232             if (![latentChildWindows containsObject:child])
1233                 [latentChildWindows addObject:child];
1234             child.latentParentWindow = self;
1235         }
1237         return reordered;
1238     }
1240     - (BOOL) addChildWineWindow:(WineWindow*)child
1241     {
1242         return [self addChildWineWindow:child assumeVisible:FALSE];
1243     }
1245     - (void) removeChildWineWindow:(WineWindow*)child
1246     {
1247         [self removeChildWindow:child];
1248         if (child.latentParentWindow == self)
1249             child.latentParentWindow = nil;
1250         [latentChildWindows removeObjectIdenticalTo:child];
1251     }
1253     - (void) setChildWineWindows:(NSArray*)childWindows
1254     {
1255         NSArray* origChildren;
1256         NSUInteger count, start, limit, i;
1258         origChildren = self.childWineWindows;
1260         // If the current and desired children arrays match up to a point, leave
1261         // those matching children alone.
1262         count = childWindows.count;
1263         limit = MIN(origChildren.count, count);
1264         for (start = 0; start < limit; start++)
1265         {
1266             if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1267                 break;
1268         }
1270         // Remove all of the child windows and re-add them back-to-front so they
1271         // are in the desired order.
1272         for (i = start; i < count; i++)
1273         {
1274             WineWindow* child = [childWindows objectAtIndex:i];
1275             [self removeChildWindow:child];
1276         }
1277         for (i = start; i < count; i++)
1278         {
1279             WineWindow* child = [childWindows objectAtIndex:i];
1280             [self addChildWindow:child ordered:NSWindowAbove];
1281         }
1282     }
1284     static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1285     {
1286         NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1287         NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1288         NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1289         NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1290         if (index1 == NSNotFound)
1291         {
1292             if (index2 == NSNotFound)
1293                 return NSOrderedSame;
1294             else
1295                 return NSOrderedAscending;
1296         }
1297         else if (index2 == NSNotFound)
1298             return NSOrderedDescending;
1299         else if (index1 < index2)
1300             return NSOrderedDescending;
1301         else if (index2 < index1)
1302             return NSOrderedAscending;
1304         return NSOrderedSame;
1305     }
1307     - (BOOL) becameEligibleParentOrChild
1308     {
1309         BOOL reordered = FALSE;
1310         NSUInteger count;
1312         if (latentParentWindow.floating || !self.floating)
1313         {
1314             // If we aren't visible currently, we assume that we should be and soon
1315             // will be.  So, if the latent parent is visible that's enough to assume
1316             // we can establish the parent-child relationship in Cocoa.  That will
1317             // actually make us visible, which is fine.
1318             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1319                 reordered = TRUE;
1320         }
1322         // Here, though, we may not actually be visible yet and adding a child
1323         // won't make us visible.  The caller will have to call this method
1324         // again after actually making us visible.
1325         if ([self isVisible] && (count = [latentChildWindows count]))
1326         {
1327             NSMutableArray* windowNumbers;
1328             NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1329             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1330             NSUInteger i;
1332             windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1334             for (i = 0; i < count; i++)
1335             {
1336                 WineWindow* child = [latentChildWindows objectAtIndex:i];
1337                 if ([child isVisible] && (self.floating || !child.floating))
1338                 {
1339                     if (child.latentParentWindow == self)
1340                     {
1341                         if ([self level] > [child level])
1342                             [child setLevel:[self level]];
1343                         [childWindows addObject:child];
1344                         child.latentParentWindow = nil;
1345                         reordered = TRUE;
1346                     }
1347                     else
1348                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1349                     [indexesToRemove addIndex:i];
1350                 }
1351             }
1353             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1355             [childWindows sortWithOptions:NSSortStable
1356                           usingComparator:^NSComparisonResult(id obj1, id obj2){
1357                 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1358             }];
1359             [self setChildWineWindows:childWindows];
1360         }
1362         return reordered;
1363     }
1365     - (void) becameIneligibleChild
1366     {
1367         WineWindow* parent = (WineWindow*)[self parentWindow];
1368         if (parent)
1369         {
1370             if (!parent->latentChildWindows)
1371                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1372             [parent->latentChildWindows insertObject:self atIndex:0];
1373             self.latentParentWindow = parent;
1374             [parent removeChildWindow:self];
1375         }
1376     }
1378     - (void) becameIneligibleParentOrChild
1379     {
1380         NSArray* childWindows = [self childWineWindows];
1382         [self becameIneligibleChild];
1384         if ([childWindows count])
1385         {
1386             WineWindow* child;
1388             for (child in childWindows)
1389             {
1390                 child.latentParentWindow = self;
1391                 [self removeChildWindow:child];
1392             }
1394             if (latentChildWindows)
1395                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1396             else
1397                 latentChildWindows = [childWindows mutableCopy];
1398         }
1399     }
1401     // Determine if, among Wine windows, this window is directly above or below
1402     // a given other Wine window with no other Wine window intervening.
1403     // Intervening non-Wine windows are ignored.
1404     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1405     {
1406         NSNumber* windowNumber;
1407         NSNumber* otherWindowNumber;
1408         NSArray* windowNumbers;
1409         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1411         if (![self isVisible] || ![otherWindow isVisible])
1412             return FALSE;
1414         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1415         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1416         windowNumbers = [[self class] windowNumbersWithOptions:0];
1417         windowIndex = [windowNumbers indexOfObject:windowNumber];
1418         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1420         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1421             return FALSE;
1423         if (orderingMode == NSWindowAbove)
1424         {
1425             lowIndex = windowIndex;
1426             highIndex = otherWindowIndex;
1427         }
1428         else if (orderingMode == NSWindowBelow)
1429         {
1430             lowIndex = otherWindowIndex;
1431             highIndex = windowIndex;
1432         }
1433         else
1434             return FALSE;
1436         if (highIndex <= lowIndex)
1437             return FALSE;
1439         for (i = lowIndex + 1; i < highIndex; i++)
1440         {
1441             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1442             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1443             if ([interveningWindow isKindOfClass:[WineWindow class]])
1444                 return FALSE;
1445         }
1447         return TRUE;
1448     }
1450     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1451     {
1452         NSMutableArray* windowNumbers;
1453         NSNumber* childWindowNumber;
1454         NSUInteger otherIndex;
1455         NSArray* origChildren;
1456         NSMutableArray* children;
1458         // Get the z-order from the window server and modify it to reflect the
1459         // requested window ordering.
1460         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1461         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1462         [windowNumbers removeObject:childWindowNumber];
1463         if (other)
1464         {
1465             otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1466             [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1467         }
1468         else if (mode == NSWindowAbove)
1469             [windowNumbers insertObject:childWindowNumber atIndex:0];
1470         else
1471             [windowNumbers addObject:childWindowNumber];
1473         // Get our child windows and sort them in the reverse of the desired
1474         // z-order (back-to-front).
1475         origChildren = [self childWineWindows];
1476         children = [[origChildren mutableCopy] autorelease];
1477         [children sortWithOptions:NSSortStable
1478                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1479             return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1480         }];
1482         [self setChildWineWindows:children];
1483     }
1485     // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1486     // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1487     //      (non-nil, non-nil)  there is a level in the window tree where the two windows have sibling ancestors
1488     //                          if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1489     //                          otherwise, the two ancestors are each roots of disjoint window trees
1490     //      (nil, non-nil)      the other window is a descendent of self and *ancestorOfOther is the direct child
1491     //      (non-nil, nil)      self is a descendent of other and *ancestor is the direct child
1492     - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1493     {
1494         NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1495         WineWindow* child;
1496         WineWindow* parent;
1497         for (child = other;
1498              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1499              child = parent)
1500         {
1501             if (parent == self)
1502             {
1503                 *ancestor = nil;
1504                 *ancestorOfOther = child;
1505                 return;
1506             }
1508             [otherAncestors addObject:parent];
1509         }
1511         for (child = self;
1512              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1513              child = parent)
1514         {
1515             NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1516             if (index != NSNotFound)
1517             {
1518                 *ancestor = child;
1519                 if (index == 0)
1520                     *ancestorOfOther = nil;
1521                 else
1522                     *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1523                 return;
1524             }
1525         }
1527         *ancestor = child;
1528         *ancestorOfOther = otherAncestors.lastObject;;
1529     }
1531     /* Returns whether or not the window was ordered in, which depends on if
1532        its frame intersects any screen. */
1533     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1534     {
1535         WineApplicationController* controller = [WineApplicationController sharedController];
1536         if (![self isMiniaturized])
1537         {
1538             BOOL needAdjustWindowLevels = FALSE;
1539             BOOL wasVisible;
1540             WineWindow* parent;
1541             WineWindow* child;
1543             [controller transformProcessToForeground];
1544             if ([NSApp isHidden])
1545                 [NSApp unhide:nil];
1546             wasVisible = [self isVisible];
1548             if (activate)
1549                 [NSApp activateIgnoringOtherApps:YES];
1551             NSDisableScreenUpdates();
1553             if ([self becameEligibleParentOrChild])
1554                 needAdjustWindowLevels = TRUE;
1556             if (prev || next)
1557             {
1558                 WineWindow* other = [prev isVisible] ? prev : next;
1559                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1561                 if (![self isOrdered:orderingMode relativeTo:other])
1562                 {
1563                     WineWindow* ancestor;
1564                     WineWindow* ancestorOfOther;
1566                     [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1567                     if (ancestor)
1568                     {
1569                         [self setAutodisplay:YES];
1570                         if (ancestorOfOther)
1571                         {
1572                             // This window level may not be right for this window based
1573                             // on floating-ness, fullscreen-ness, etc.  But we set it
1574                             // temporarily to allow us to order the windows properly.
1575                             // Then the levels get fixed by -adjustWindowLevels.
1576                             if ([ancestor level] != [ancestorOfOther level])
1577                                 [ancestor setLevel:[ancestorOfOther level]];
1579                             parent = (WineWindow*)ancestor.parentWindow;
1580                             if ([parent isKindOfClass:[WineWindow class]])
1581                                 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1582                             else
1583                                 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1584                         }
1586                         for (child = self;
1587                              (parent = (WineWindow*)child.parentWindow);
1588                              child = parent)
1589                         {
1590                             if ([parent isKindOfClass:[WineWindow class]])
1591                                 [parent order:-orderingMode childWindow:child relativeTo:nil];
1592                             if (parent == ancestor)
1593                                 break;
1594                         }
1596                         [self checkWineDisplayLink];
1597                         needAdjustWindowLevels = TRUE;
1598                     }
1599                 }
1600             }
1601             else
1602             {
1603                 for (child = self;
1604                      (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1605                      child = parent)
1606                 {
1607                     [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1608                 }
1610                 // Again, temporarily set level to make sure we can order to
1611                 // the right place.
1612                 next = [controller frontWineWindow];
1613                 if (next && [self level] < [next level])
1614                     [self setLevel:[next level]];
1615                 [self setAutodisplay:YES];
1616                 [self orderFront:nil];
1617                 [self checkWineDisplayLink];
1618                 needAdjustWindowLevels = TRUE;
1619             }
1620             pendingOrderOut = FALSE;
1622             if ([self becameEligibleParentOrChild])
1623                 needAdjustWindowLevels = TRUE;
1625             if (needAdjustWindowLevels)
1626             {
1627                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1628                     [controller updateFullscreenWindows];
1629                 [controller adjustWindowLevels];
1630             }
1632             if (pendingMinimize)
1633             {
1634                 [super miniaturize:nil];
1635                 pendingMinimize = FALSE;
1636             }
1638             NSEnableScreenUpdates();
1640             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1641                Generate a frame-changed event just in case.  The back end will ignore
1642                it if nothing actually changed. */
1643             [self windowDidResize:nil];
1645             if (![self isExcludedFromWindowsMenu])
1646                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1647         }
1648     }
1650     - (void) doOrderOut
1651     {
1652         WineApplicationController* controller = [WineApplicationController sharedController];
1653         BOOL wasVisible = [self isVisible];
1654         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1656         [self endWindowDragging];
1657         [controller windowWillOrderOut:self];
1659         if (enteringFullScreen || exitingFullScreen)
1660         {
1661             pendingOrderOut = TRUE;
1662             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1663                                              event_mask_for_type(WINDOW_GOT_FOCUS) |
1664                                              event_mask_for_type(WINDOW_LOST_FOCUS) |
1665                                              event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1666                                              event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1667                                              event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1668                                    forWindow:self];
1669             return;
1670         }
1672         pendingOrderOut = FALSE;
1674         if ([self isMiniaturized])
1675             pendingMinimize = TRUE;
1677         WineWindow* parent = (WineWindow*)self.parentWindow;
1678         if ([parent isKindOfClass:[WineWindow class]])
1679             [parent grabDockIconSnapshotFromWindow:self force:NO];
1681         [self becameIneligibleParentOrChild];
1682         if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1683         {
1684             fakingClose = TRUE;
1685             [self close];
1686             fakingClose = FALSE;
1687         }
1688         else
1689             [self orderOut:nil];
1690         [self checkWineDisplayLink];
1691         [self setBackgroundColor:[NSColor clearColor]];
1692         [self setOpaque:NO];
1693         drawnSinceShown = NO;
1694         savedVisibleState = FALSE;
1695         if (wasVisible && wasOnActiveSpace && fullscreen)
1696             [controller updateFullscreenWindows];
1697         [controller adjustWindowLevels];
1698         [NSApp removeWindowsItem:self];
1700         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1701                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1702                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1703                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1704                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1705                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1706                                forWindow:self];
1707     }
1709     - (void) updateFullscreen
1710     {
1711         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1712         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1714         if (nowFullscreen != fullscreen)
1715         {
1716             WineApplicationController* controller = [WineApplicationController sharedController];
1718             fullscreen = nowFullscreen;
1719             if ([self isVisible] && [self isOnActiveSpace])
1720                 [controller updateFullscreenWindows];
1722             [controller adjustWindowLevels];
1723         }
1724     }
1726     - (void) setFrameAndWineFrame:(NSRect)frame
1727     {
1728         [self setFrame:frame display:YES];
1730         wineFrame = frame;
1731         roundedWineFrame = self.frame;
1732         CGFloat junk;
1733 #if CGFLOAT_IS_DOUBLE
1734         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1735              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1736             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1737             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1738             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1739             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1740             roundedWineFrame = wineFrame;
1741 #else
1742         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1743              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1744             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1745             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1746             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1747             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1748             roundedWineFrame = wineFrame;
1749 #endif
1750     }
1752     - (void) setFrameFromWine:(NSRect)contentRect
1753     {
1754         /* Origin is (left, top) in a top-down space.  Need to convert it to
1755            (left, bottom) in a bottom-up space. */
1756         [[WineApplicationController sharedController] flipRect:&contentRect];
1758         /* The back end is establishing a new window size and position.  It's
1759            not interested in any stale events regarding those that may be sitting
1760            in the queue. */
1761         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1762                                forWindow:self];
1764         if (!NSIsEmptyRect(contentRect))
1765         {
1766             NSRect frame, oldFrame;
1768             oldFrame = self.wine_fractionalFrame;
1769             frame = [self frameRectForContentRect:contentRect];
1770             if (!NSEqualRects(frame, oldFrame))
1771             {
1772                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1773                 BOOL needEnableScreenUpdates = FALSE;
1775                 if ([self preventResizing])
1776                 {
1777                     // Allow the following calls to -setFrame:display: to work even
1778                     // if they would violate the content size constraints. This
1779                     // shouldn't be necessary since the content size constraints are
1780                     // documented to not constrain that method, but it seems to be.
1781                     [self setContentMinSize:NSZeroSize];
1782                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1783                 }
1785                 if (equalSizes && [[self childWineWindows] count])
1786                 {
1787                     // If we change the window frame such that the origin moves
1788                     // but the size doesn't change, then Cocoa moves child
1789                     // windows with the parent.  We don't want that so we fake
1790                     // a change of the size and then change it back.
1791                     NSRect bogusFrame = frame;
1792                     bogusFrame.size.width++;
1794                     NSDisableScreenUpdates();
1795                     needEnableScreenUpdates = TRUE;
1797                     ignore_windowResize = TRUE;
1798                     [self setFrame:bogusFrame display:NO];
1799                     ignore_windowResize = FALSE;
1800                 }
1802                 [self setFrameAndWineFrame:frame];
1803                 if ([self preventResizing])
1804                 {
1805                     [self setContentMinSize:contentRect.size];
1806                     [self setContentMaxSize:contentRect.size];
1807                 }
1809                 if (needEnableScreenUpdates)
1810                     NSEnableScreenUpdates();
1812                 if (!equalSizes)
1813                     [self updateColorSpace];
1815                 if (!enteringFullScreen &&
1816                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1817                     nonFullscreenFrame = frame;
1819                 [self updateFullscreen];
1821                 if ([self isOrderedIn])
1822                 {
1823                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1824                        event.  The back end will ignore it if nothing actually changed. */
1825                     [self windowDidResize:nil];
1826                 }
1827             }
1828         }
1829     }
1831     - (NSRect) wine_fractionalFrame
1832     {
1833         NSRect frame = self.frame;
1834         if (NSEqualRects(frame, roundedWineFrame))
1835             frame = wineFrame;
1836         return frame;
1837     }
1839     - (void) setMacDrvParentWindow:(WineWindow*)parent
1840     {
1841         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1842         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1843         {
1844             [oldParent removeChildWineWindow:self];
1845             [latentParentWindow removeChildWineWindow:self];
1846             if ([parent addChildWineWindow:self])
1847                 [[WineApplicationController sharedController] adjustWindowLevels];
1848             [self adjustFullScreenBehavior:[self collectionBehavior]];
1849         }
1850     }
1852     - (void) setDisabled:(BOOL)newValue
1853     {
1854         if (disabled != newValue)
1855         {
1856             disabled = newValue;
1857             [self adjustFeaturesForState];
1858         }
1859     }
1861     - (BOOL) needsTransparency
1862     {
1863         return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1864                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1865     }
1867     - (void) checkTransparency
1868     {
1869         if (![self isOpaque] && !self.needsTransparency)
1870         {
1871             self.shapeChangedSinceLastDraw = TRUE;
1872             [[self contentView] setNeedsDisplay:YES];
1873             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1874             [self setOpaque:YES];
1875         }
1876         else if ([self isOpaque] && self.needsTransparency)
1877         {
1878             self.shapeChangedSinceLastDraw = TRUE;
1879             [[self contentView] setNeedsDisplay:YES];
1880             [self setBackgroundColor:[NSColor clearColor]];
1881             [self setOpaque:NO];
1882         }
1883     }
1885     - (void) setShape:(NSBezierPath*)newShape
1886     {
1887         if (shape == newShape) return;
1889         if (shape)
1890         {
1891             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1892             [shape release];
1893         }
1894         if (newShape)
1895             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1897         shape = [newShape copy];
1898         self.shapeChangedSinceLastDraw = TRUE;
1900         [self checkTransparency];
1901     }
1903     - (void) makeFocused:(BOOL)activate
1904     {
1905         if (activate)
1906         {
1907             [[WineApplicationController sharedController] transformProcessToForeground];
1908             [NSApp activateIgnoringOtherApps:YES];
1909         }
1911         causing_becomeKeyWindow = self;
1912         [self makeKeyWindow];
1913         causing_becomeKeyWindow = nil;
1915         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1916                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1917                                forWindow:self];
1918     }
1920     - (void) postKey:(uint16_t)keyCode
1921              pressed:(BOOL)pressed
1922            modifiers:(NSUInteger)modifiers
1923                event:(NSEvent*)theEvent
1924     {
1925         macdrv_event* event;
1926         CGEventRef cgevent;
1927         WineApplicationController* controller = [WineApplicationController sharedController];
1929         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1930         event->key.keycode   = keyCode;
1931         event->key.modifiers = modifiers;
1932         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1934         if ((cgevent = [theEvent CGEvent]))
1935             controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
1937         [queue postEvent:event];
1939         macdrv_release_event(event);
1941         [controller noteKey:keyCode pressed:pressed];
1942     }
1944     - (void) postKeyEvent:(NSEvent *)theEvent
1945     {
1946         [self flagsChanged:theEvent];
1947         [self postKey:[theEvent keyCode]
1948               pressed:[theEvent type] == NSKeyDown
1949             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1950                 event:theEvent];
1951     }
1953     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1954     {
1955         savedContentMinSize = minSize;
1956         savedContentMaxSize = maxSize;
1957         if (![self preventResizing])
1958         {
1959             [self setContentMinSize:minSize];
1960             [self setContentMaxSize:maxSize];
1961         }
1962     }
1964     - (WineWindow*) ancestorWineWindow
1965     {
1966         WineWindow* ancestor = self;
1967         for (;;)
1968         {
1969             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1970             if ([parent isKindOfClass:[WineWindow class]])
1971                 ancestor = parent;
1972             else
1973                 break;
1974         }
1975         return ancestor;
1976     }
1978     - (void) postBroughtForwardEvent
1979     {
1980         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1981         [queue postEvent:event];
1982         macdrv_release_event(event);
1983     }
1985     - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
1986     {
1987         macdrv_event* event;
1988         NSUInteger style = self.styleMask;
1990         if (isFullscreen)
1991             style |= NSFullScreenWindowMask;
1992         else
1993             style &= ~NSFullScreenWindowMask;
1994         frame = [[self class] contentRectForFrameRect:frame styleMask:style];
1995         [[WineApplicationController sharedController] flipRect:&frame];
1997         /* Coalesce events by discarding any previous ones still in the queue. */
1998         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1999                                forWindow:self];
2001         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2002         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2003         event->window_frame_changed.fullscreen = isFullscreen;
2004         event->window_frame_changed.in_resize = resizing;
2005         [queue postEvent:event];
2006         macdrv_release_event(event);
2007     }
2009     - (void) updateForCursorClipping
2010     {
2011         [self adjustFeaturesForState];
2012     }
2014     - (void) endWindowDragging
2015     {
2016         if (draggingPhase)
2017         {
2018             if (draggingPhase == 3)
2019             {
2020                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2021                 [queue postEvent:event];
2022                 macdrv_release_event(event);
2023             }
2025             draggingPhase = 0;
2026             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2027         }
2028     }
2030     - (NSMutableDictionary*) displayIDToDisplayLinkMap
2031     {
2032         static NSMutableDictionary* displayIDToDisplayLinkMap;
2033         if (!displayIDToDisplayLinkMap)
2034         {
2035             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2037             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2038                                                               object:NSApp
2039                                                                queue:nil
2040                                                           usingBlock:^(NSNotification *note){
2041                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2042                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2043                 [badDisplayIDs minusSet:validDisplayIDs];
2044                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2045             }];
2046         }
2047         return displayIDToDisplayLinkMap;
2048     }
2050     - (WineDisplayLink*) wineDisplayLink
2051     {
2052         if (!_lastDisplayID)
2053             return nil;
2055         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2056         return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2057     }
2059     - (void) checkWineDisplayLink
2060     {
2061         NSScreen* screen = self.screen;
2062         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2063             screen = nil;
2064 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2065         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2066             screen = nil;
2067 #endif
2069         NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2070         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2071         if (displayID == _lastDisplayID)
2072             return;
2074         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2076         if (_lastDisplayID)
2077         {
2078             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2079             [link removeWindow:self];
2080         }
2081         if (displayID)
2082         {
2083             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2084             if (!link)
2085             {
2086                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2087                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2088             }
2089             [link addWindow:self];
2090             [self displayIfNeeded];
2091         }
2092         _lastDisplayID = displayID;
2093     }
2095     - (BOOL) isEmptyShaped
2096     {
2097         return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2098     }
2100     - (BOOL) canProvideSnapshot
2101     {
2102         return (self.windowNumber > 0 && ![self isEmptyShaped]);
2103     }
2105     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2106     {
2107         if (![self isEmptyShaped])
2108             return;
2110         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2111         if (!force && now < lastDockIconSnapshot + 1)
2112             return;
2114         if (window)
2115         {
2116             if (![window canProvideSnapshot])
2117                 return;
2118         }
2119         else
2120         {
2121             CGFloat bestArea;
2122             for (WineWindow* childWindow in self.childWindows)
2123             {
2124                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2125                     continue;
2127                 NSSize size = childWindow.frame.size;
2128                 CGFloat area = size.width * size.height;
2129                 if (!window || area > bestArea)
2130                 {
2131                     window = childWindow;
2132                     bestArea = area;
2133                 }
2134             }
2136             if (!window)
2137                 return;
2138         }
2140         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2141         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2142         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2143         CFRelease(windowIDs);
2144         if (!windowImage)
2145             return;
2147         NSImage* appImage = [NSApp applicationIconImage];
2148         if (!appImage)
2149             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2151         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2152         [dockIcon lockFocus];
2154         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2156         CGRect rect = CGRectMake(8, 8, 240, 240);
2157         size_t width = CGImageGetWidth(windowImage);
2158         size_t height = CGImageGetHeight(windowImage);
2159         if (width > height)
2160         {
2161             rect.size.height *= height / (double)width;
2162             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2163         }
2164         else if (width != height)
2165         {
2166             rect.size.width *= width / (double)height;
2167             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2168         }
2170         CGContextDrawImage(cgcontext, rect, windowImage);
2171         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2172                     fromRect:NSZeroRect
2173                    operation:NSCompositeSourceOver
2174                     fraction:1
2175               respectFlipped:YES
2176                        hints:nil];
2178         [dockIcon unlockFocus];
2180         CGImageRelease(windowImage);
2182         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2183         if (![imageView isKindOfClass:[NSImageView class]])
2184         {
2185             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2186             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2187             self.dockTile.contentView = imageView;
2188         }
2189         imageView.image = dockIcon;
2190         [self.dockTile display];
2191         lastDockIconSnapshot = now;
2192     }
2194     - (void) checkEmptyShaped
2195     {
2196         if (self.dockTile.contentView && ![self isEmptyShaped])
2197         {
2198             self.dockTile.contentView = nil;
2199             lastDockIconSnapshot = 0;
2200         }
2201         [self checkWineDisplayLink];
2202     }
2205     /*
2206      * ---------- NSWindow method overrides ----------
2207      */
2208     - (BOOL) canBecomeKeyWindow
2209     {
2210         if (causing_becomeKeyWindow == self) return YES;
2211         if (self.disabled || self.noActivate) return NO;
2212         return [self isKeyWindow];
2213     }
2215     - (BOOL) canBecomeMainWindow
2216     {
2217         return [self canBecomeKeyWindow];
2218     }
2220     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2221     {
2222         // If a window is sized to completely cover a screen, then it's in
2223         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2224         // it.
2225         NSArray* screens = [NSScreen screens];
2226         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2227         if (!screen_covered_by_rect(contentRect, screens) &&
2228             frame_intersects_screens(frameRect, screens))
2229             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2230         return frameRect;
2231     }
2233     // This private method of NSWindow is called as Cocoa reacts to the display
2234     // configuration changing.  Among other things, it adjusts the window's
2235     // frame based on how the screen(s) changed size.  That tells Wine that the
2236     // window has been moved.  We don't want that.  Rather, we want to make
2237     // sure that the WinAPI notion of the window position is maintained/
2238     // restored, possibly undoing or overriding Cocoa's adjustment.
2239     //
2240     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2241     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2242     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2243     // reassert its notion of the position.  That call won't get processed
2244     // until after this method returns, so it will override whatever this
2245     // method does to the window position.  It will also discard any pending
2246     // WINDOW_FRAME_CHANGED events.
2247     //
2248     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2249     // adjust the window's position due to a display change is to hook into
2250     // this private method.  This private method has remained stable from 10.6
2251     // through 10.11.  If it does change, the most likely thing is that it
2252     // will be removed and no longer called and this fix will simply stop
2253     // working.  The only real danger would be if Apple changed the return type
2254     // to a struct or floating-point type, which would change the calling
2255     // convention.
2256     - (id) _displayChanged
2257     {
2258         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2259         [queue postEvent:event];
2260         macdrv_release_event(event);
2262         return [super _displayChanged];
2263     }
2265     - (BOOL) isExcludedFromWindowsMenu
2266     {
2267         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2268     }
2270     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2271     {
2272         BOOL ret = [super validateMenuItem:menuItem];
2274         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2275             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2276         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2277             ret = NO;
2279         return ret;
2280     }
2282     /* We don't call this.  It's the action method of the items in the Window menu. */
2283     - (void) makeKeyAndOrderFront:(id)sender
2284     {
2285         if ([self isMiniaturized])
2286             [self deminiaturize:nil];
2287         [self orderBelow:nil orAbove:nil activate:NO];
2288         [[self ancestorWineWindow] postBroughtForwardEvent];
2290         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2291             [[WineApplicationController sharedController] windowGotFocus:self];
2292     }
2294     - (void) sendEvent:(NSEvent*)event
2295     {
2296         NSEventType type = event.type;
2298         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2299            interface control.  For example, Control-Tab switches focus among
2300            views.  We want to bypass that feature, so directly route key-down
2301            events to -keyDown:. */
2302         if (type == NSKeyDown)
2303             [[self firstResponder] keyDown:event];
2304         else
2305         {
2306             if (!draggingPhase && maximized && ![self isMovable] &&
2307                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2308                 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2309             {
2310                 NSRect titleBar = self.frame;
2311                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2312                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2313                 titleBar.origin.y = NSMaxY(contentRect);
2315                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2317                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2318                 {
2319                     static const NSWindowButton buttons[] = {
2320                         NSWindowCloseButton,
2321                         NSWindowMiniaturizeButton,
2322                         NSWindowZoomButton,
2323                         NSWindowFullScreenButton,
2324                     };
2325                     BOOL hitButton = NO;
2326                     int i;
2328                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2329                     {
2330                         NSButton* button;
2332                         if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2333                             continue;
2335                         button = [self standardWindowButton:buttons[i]];
2336                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2337                         {
2338                             hitButton = YES;
2339                             break;
2340                         }
2341                     }
2343                     if (!hitButton)
2344                     {
2345                         draggingPhase = 1;
2346                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2347                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2348                     }
2349                 }
2350             }
2351             else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2352             {
2353                 if ([self isMovable])
2354                 {
2355                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2356                     NSPoint newTopLeft = dragWindowStartPosition;
2358                     newTopLeft.x += point.x - dragStartPosition.x;
2359                     newTopLeft.y += point.y - dragStartPosition.y;
2361                     if (draggingPhase == 2)
2362                     {
2363                         macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2364                         mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2365                         [queue postEvent:mevent];
2366                         macdrv_release_event(mevent);
2368                         draggingPhase = 3;
2369                     }
2371                     [self setFrameTopLeftPoint:newTopLeft];
2372                 }
2373                 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2374                 {
2375                     macdrv_event* event;
2376                     NSRect frame = [self contentRectForFrameRect:self.frame];
2378                     [[WineApplicationController sharedController] flipRect:&frame];
2380                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2381                     event->window_restore_requested.keep_frame = TRUE;
2382                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2383                     [queue postEvent:event];
2384                     macdrv_release_event(event);
2386                     draggingPhase = 2;
2387                 }
2389                 if (type == NSLeftMouseUp)
2390                     [self endWindowDragging];
2391             }
2393             [super sendEvent:event];
2394         }
2395     }
2397     - (void) miniaturize:(id)sender
2398     {
2399         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2400         [queue postEvent:event];
2401         macdrv_release_event(event);
2403         WineWindow* parent = (WineWindow*)self.parentWindow;
2404         if ([parent isKindOfClass:[WineWindow class]])
2405             [parent grabDockIconSnapshotFromWindow:self force:YES];
2406     }
2408     - (void) toggleFullScreen:(id)sender
2409     {
2410         if (!self.disabled && !maximized)
2411             [super toggleFullScreen:sender];
2412     }
2414     - (void) setViewsNeedDisplay:(BOOL)value
2415     {
2416         if (value && ![self viewsNeedDisplay])
2417         {
2418             WineDisplayLink* link = [self wineDisplayLink];
2419             if (link)
2420             {
2421                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2422                 if (_lastDisplayTime + [link refreshPeriod] < now)
2423                     [self setAutodisplay:YES];
2424                 else
2425                 {
2426                     [link start];
2427                     _lastDisplayTime = now;
2428                 }
2429             }
2430         }
2431         [super setViewsNeedDisplay:value];
2432     }
2434     - (void) display
2435     {
2436         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2437         [super display];
2438         [self setAutodisplay:NO];
2439     }
2441     - (void) displayIfNeeded
2442     {
2443         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2444         [super displayIfNeeded];
2445         [self setAutodisplay:NO];
2446     }
2448     - (void) windowDidDrawContent
2449     {
2450         if (!drawnSinceShown)
2451         {
2452             drawnSinceShown = YES;
2453             dispatch_async(dispatch_get_main_queue(), ^{
2454                 [self checkTransparency];
2455             });
2456         }
2457     }
2459     - (NSArray*) childWineWindows
2460     {
2461         NSArray* childWindows = self.childWindows;
2462         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2463             return [child isKindOfClass:[WineWindow class]];
2464         }];
2465         return [childWindows objectsAtIndexes:indexes];
2466     }
2468     // We normally use the generic/calibrated RGB color space for the window,
2469     // rather than the device color space, to avoid expensive color conversion
2470     // which slows down drawing.  However, for windows displaying OpenGL, having
2471     // a different color space than the screen greatly reduces frame rates, often
2472     // limiting it to the display refresh rate.
2473     //
2474     // To avoid this, we switch back to the screen color space whenever the
2475     // window is covered by a view with an attached OpenGL context.
2476     - (void) updateColorSpace
2477     {
2478         NSRect contentRect = [[self contentView] frame];
2479         BOOL coveredByGLView = FALSE;
2480         WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2481         if ([view isKindOfClass:[WineContentView class]] && view.everHadGLContext)
2482         {
2483             NSRect frame = [view convertRect:[view bounds] toView:nil];
2484             if (NSContainsRect(frame, contentRect))
2485                 coveredByGLView = TRUE;
2486         }
2488         if (coveredByGLView)
2489             [self setColorSpace:nil];
2490         else
2491             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2492     }
2494     - (void) updateForGLSubviews
2495     {
2496         [self updateColorSpace];
2497         if (gl_surface_mode == GL_SURFACE_BEHIND)
2498             [self checkTransparency];
2499     }
2501     - (void) setRetinaMode:(int)mode
2502     {
2503         NSRect frame;
2504         double scale = mode ? 0.5 : 2.0;
2505         NSAffineTransform* transform = [NSAffineTransform transform];
2507         [transform scaleBy:scale];
2509         if (shape)
2510             [shape transformUsingAffineTransform:transform];
2512         for (WineContentView* subview in [self.contentView subviews])
2513         {
2514             if ([subview isKindOfClass:[WineContentView class]])
2515                 [subview setRetinaMode:mode];
2516         }
2518         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2519         frame.origin.x *= scale;
2520         frame.origin.y *= scale;
2521         frame.size.width *= scale;
2522         frame.size.height *= scale;
2523         frame = [self frameRectForContentRect:frame];
2525         savedContentMinSize = [transform transformSize:savedContentMinSize];
2526         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2527             savedContentMaxSize.width *= scale;
2528         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2529             savedContentMaxSize.height *= scale;
2531         self.contentMinSize = [transform transformSize:self.contentMinSize];
2532         NSSize temp = self.contentMaxSize;
2533         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2534             temp.width *= scale;
2535         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2536             temp.height *= scale;
2537         self.contentMaxSize = temp;
2539         ignore_windowResize = TRUE;
2540         [self setFrameAndWineFrame:frame];
2541         ignore_windowResize = FALSE;
2542     }
2545     /*
2546      * ---------- NSResponder method overrides ----------
2547      */
2548     - (void) keyDown:(NSEvent *)theEvent
2549     {
2550         if ([theEvent isARepeat])
2551         {
2552             if (!allowKeyRepeats)
2553                 return;
2554         }
2555         else
2556             allowKeyRepeats = YES;
2558         [self postKeyEvent:theEvent];
2559     }
2561     - (void) flagsChanged:(NSEvent *)theEvent
2562     {
2563         static const struct {
2564             NSUInteger  mask;
2565             uint16_t    keycode;
2566         } modifiers[] = {
2567             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2568             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2569             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2570             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2571             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2572             { NX_DEVICELALTKEYMASK,     kVK_Option },
2573             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2574             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2575             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2576         };
2578         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2579         NSUInteger changed;
2580         int i, last_changed;
2582         fix_device_modifiers_by_generic(&modifierFlags);
2583         changed = modifierFlags ^ lastModifierFlags;
2585         last_changed = -1;
2586         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2587             if (changed & modifiers[i].mask)
2588                 last_changed = i;
2590         for (i = 0; i <= last_changed; i++)
2591         {
2592             if (changed & modifiers[i].mask)
2593             {
2594                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2596                 if (pressed)
2597                     allowKeyRepeats = NO;
2599                 if (i == last_changed)
2600                     lastModifierFlags = modifierFlags;
2601                 else
2602                 {
2603                     lastModifierFlags ^= modifiers[i].mask;
2604                     fix_generic_modifiers_by_device(&lastModifierFlags);
2605                 }
2607                 // Caps lock generates one event for each press-release action.
2608                 // We need to simulate a pair of events for each actual event.
2609                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2610                 {
2611                     [self postKey:modifiers[i].keycode
2612                           pressed:TRUE
2613                         modifiers:lastModifierFlags
2614                             event:(NSEvent*)theEvent];
2615                     pressed = FALSE;
2616                 }
2618                 [self postKey:modifiers[i].keycode
2619                       pressed:pressed
2620                     modifiers:lastModifierFlags
2621                         event:(NSEvent*)theEvent];
2622             }
2623         }
2624     }
2626     - (void) applicationWillHide
2627     {
2628         savedVisibleState = [self isVisible];
2629     }
2631     - (void) applicationDidUnhide
2632     {
2633         if ([self isVisible])
2634             [self becameEligibleParentOrChild];
2635     }
2638     /*
2639      * ---------- NSWindowDelegate methods ----------
2640      */
2641     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2642     {
2643         macdrv_query* query;
2644         NSSize size;
2646         query = macdrv_create_query();
2647         query->type = QUERY_MIN_MAX_INFO;
2648         query->window = (macdrv_window)[self retain];
2649         [self.queue query:query timeout:0.5];
2650         macdrv_release_query(query);
2652         size = [self contentMaxSize];
2653         if (proposedSize.width < size.width)
2654             size.width = proposedSize.width;
2655         if (proposedSize.height < size.height)
2656             size.height = proposedSize.height;
2657         return size;
2658     }
2660     - (void)windowDidBecomeKey:(NSNotification *)notification
2661     {
2662         WineApplicationController* controller = [WineApplicationController sharedController];
2663         NSEvent* event = [controller lastFlagsChanged];
2664         if (event)
2665             [self flagsChanged:event];
2667         if (causing_becomeKeyWindow == self) return;
2669         [controller windowGotFocus:self];
2670     }
2672     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2673     {
2674         [self checkWineDisplayLink];
2675     }
2677     - (void) windowDidChangeScreen:(NSNotification*)notification
2678     {
2679         [self checkWineDisplayLink];
2680     }
2682     - (void)windowDidDeminiaturize:(NSNotification *)notification
2683     {
2684         WineApplicationController* controller = [WineApplicationController sharedController];
2686         if (!ignore_windowDeminiaturize)
2687             [self postDidUnminimizeEvent];
2688         ignore_windowDeminiaturize = FALSE;
2690         [self becameEligibleParentOrChild];
2692         if (fullscreen && [self isOnActiveSpace])
2693             [controller updateFullscreenWindows];
2694         [controller adjustWindowLevels];
2696         if (![self parentWindow])
2697             [self postBroughtForwardEvent];
2699         if (!self.disabled && !self.noActivate)
2700         {
2701             causing_becomeKeyWindow = self;
2702             [self makeKeyWindow];
2703             causing_becomeKeyWindow = nil;
2704             [controller windowGotFocus:self];
2705         }
2707         [self windowDidResize:notification];
2708         [self checkWineDisplayLink];
2709     }
2711     - (void) windowDidEndLiveResize:(NSNotification *)notification
2712     {
2713         if (!maximized)
2714         {
2715             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2716             [queue postEvent:event];
2717             macdrv_release_event(event);
2718         }
2719     }
2721     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2722     {
2723         enteringFullScreen = FALSE;
2724         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2725         if (pendingOrderOut)
2726             [self doOrderOut];
2727     }
2729     - (void) windowDidExitFullScreen:(NSNotification*)notification
2730     {
2731         exitingFullScreen = FALSE;
2732         [self setFrameAndWineFrame:nonFullscreenFrame];
2733         [self windowDidResize:nil];
2734         if (pendingOrderOut)
2735             [self doOrderOut];
2736     }
2738     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2739     {
2740         enteringFullScreen = FALSE;
2741         enteredFullScreenTime = 0;
2742         if (pendingOrderOut)
2743             [self doOrderOut];
2744     }
2746     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2747     {
2748         exitingFullScreen = FALSE;
2749         [self windowDidResize:nil];
2750         if (pendingOrderOut)
2751             [self doOrderOut];
2752     }
2754     - (void)windowDidMiniaturize:(NSNotification *)notification
2755     {
2756         if (fullscreen && [self isOnActiveSpace])
2757             [[WineApplicationController sharedController] updateFullscreenWindows];
2758         [self checkWineDisplayLink];
2759     }
2761     - (void)windowDidMove:(NSNotification *)notification
2762     {
2763         [self windowDidResize:notification];
2764     }
2766     - (void)windowDidResignKey:(NSNotification *)notification
2767     {
2768         macdrv_event* event;
2770         if (causing_becomeKeyWindow) return;
2772         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2773         [queue postEvent:event];
2774         macdrv_release_event(event);
2775     }
2777     - (void)windowDidResize:(NSNotification *)notification
2778     {
2779         NSRect frame = self.wine_fractionalFrame;
2781         if ([self inLiveResize])
2782         {
2783             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2784                 resizingFromLeft = TRUE;
2785             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2786                 resizingFromTop = TRUE;
2787         }
2789         if (ignore_windowResize || exitingFullScreen) return;
2791         if ([self preventResizing])
2792         {
2793             NSRect contentRect = [self contentRectForFrameRect:frame];
2794             [self setContentMinSize:contentRect.size];
2795             [self setContentMaxSize:contentRect.size];
2796         }
2798         [self postWindowFrameChanged:frame
2799                           fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2800                             resizing:[self inLiveResize]];
2802         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2803         [self updateFullscreen];
2804     }
2806     - (BOOL)windowShouldClose:(id)sender
2807     {
2808         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2809         [queue postEvent:event];
2810         macdrv_release_event(event);
2811         return NO;
2812     }
2814     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2815     {
2816         if (maximized)
2817         {
2818             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2819             [queue postEvent:event];
2820             macdrv_release_event(event);
2821             return NO;
2822         }
2823         else if (!resizable)
2824         {
2825             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2826             [queue postEvent:event];
2827             macdrv_release_event(event);
2828             return NO;
2829         }
2831         return YES;
2832     }
2834     - (void) windowWillClose:(NSNotification*)notification
2835     {
2836         WineWindow* child;
2838         if (fakingClose) return;
2839         if (latentParentWindow)
2840         {
2841             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2842             self.latentParentWindow = nil;
2843         }
2845         for (child in latentChildWindows)
2846         {
2847             if (child.latentParentWindow == self)
2848                 child.latentParentWindow = nil;
2849         }
2850         [latentChildWindows removeAllObjects];
2851     }
2853     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2854     {
2855         enteringFullScreen = TRUE;
2856         nonFullscreenFrame = self.wine_fractionalFrame;
2857     }
2859     - (void) windowWillExitFullScreen:(NSNotification*)notification
2860     {
2861         exitingFullScreen = TRUE;
2862         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2863     }
2865     - (void)windowWillMiniaturize:(NSNotification *)notification
2866     {
2867         [self becameIneligibleParentOrChild];
2868         [self grabDockIconSnapshotFromWindow:nil force:NO];
2869     }
2871     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2872     {
2873         if ([self inLiveResize])
2874         {
2875             if (maximized)
2876                 return self.wine_fractionalFrame.size;
2878             NSRect rect;
2879             macdrv_query* query;
2881             rect = [self frame];
2882             if (resizingFromLeft)
2883                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2884             if (!resizingFromTop)
2885                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2886             rect.size = frameSize;
2887             rect = [self contentRectForFrameRect:rect];
2888             [[WineApplicationController sharedController] flipRect:&rect];
2890             query = macdrv_create_query();
2891             query->type = QUERY_RESIZE_SIZE;
2892             query->window = (macdrv_window)[self retain];
2893             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2894             query->resize_size.from_left = resizingFromLeft;
2895             query->resize_size.from_top = resizingFromTop;
2897             if ([self.queue query:query timeout:0.1])
2898             {
2899                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2900                 rect = [self frameRectForContentRect:rect];
2901                 frameSize = rect.size;
2902             }
2904             macdrv_release_query(query);
2905         }
2907         return frameSize;
2908     }
2910     - (void) windowWillStartLiveResize:(NSNotification *)notification
2911     {
2912         [self endWindowDragging];
2914         if (maximized)
2915         {
2916             macdrv_event* event;
2917             NSRect frame = [self contentRectForFrameRect:self.frame];
2919             [[WineApplicationController sharedController] flipRect:&frame];
2921             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2922             event->window_restore_requested.keep_frame = TRUE;
2923             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2924             [queue postEvent:event];
2925             macdrv_release_event(event);
2926         }
2927         else
2928             [self sendResizeStartQuery];
2930         frameAtResizeStart = [self frame];
2931         resizingFromLeft = resizingFromTop = FALSE;
2932     }
2934     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2935     {
2936         macdrv_query* query;
2937         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2938         NSSize maxSize;
2940         query = macdrv_create_query();
2941         query->type = QUERY_MIN_MAX_INFO;
2942         query->window = (macdrv_window)[self retain];
2943         [self.queue query:query timeout:0.5];
2944         macdrv_release_query(query);
2946         currentContentRect = [self contentRectForFrameRect:[self frame]];
2947         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2949         maxSize = [self contentMaxSize];
2950         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2951         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2953         // Try to keep the top-left corner where it is.
2954         newContentRect.origin.x = NSMinX(currentContentRect);
2955         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2957         // If that pushes the bottom or right off the screen, pull it up and to the left.
2958         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2959         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2960             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2961         if (NSMinY(newContentRect) < NSMinY(screenRect))
2962             newContentRect.origin.y = NSMinY(screenRect);
2964         // If that pushes the top or left off the screen, push it down and the right
2965         // again.  Do this last because the top-left corner is more important than the
2966         // bottom-right.
2967         if (NSMinX(newContentRect) < NSMinX(screenRect))
2968             newContentRect.origin.x = NSMinX(screenRect);
2969         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2970             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2972         return [self frameRectForContentRect:newContentRect];
2973     }
2976     /*
2977      * ---------- NSPasteboardOwner methods ----------
2978      */
2979     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2980     {
2981         macdrv_query* query = macdrv_create_query();
2982         query->type = QUERY_PASTEBOARD_DATA;
2983         query->window = (macdrv_window)[self retain];
2984         query->pasteboard_data.type = (CFStringRef)[type copy];
2986         [self.queue query:query timeout:3];
2987         macdrv_release_query(query);
2988     }
2990     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
2991     {
2992         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
2993         [queue postEvent:event];
2994         macdrv_release_event(event);
2995     }
2998     /*
2999      * ---------- NSDraggingDestination methods ----------
3000      */
3001     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3002     {
3003         return [self draggingUpdated:sender];
3004     }
3006     - (void) draggingExited:(id <NSDraggingInfo>)sender
3007     {
3008         // This isn't really a query.  We don't need any response.  However, it
3009         // has to be processed in a similar manner as the other drag-and-drop
3010         // queries in order to maintain the proper order of operations.
3011         macdrv_query* query = macdrv_create_query();
3012         query->type = QUERY_DRAG_EXITED;
3013         query->window = (macdrv_window)[self retain];
3015         [self.queue query:query timeout:0.1];
3016         macdrv_release_query(query);
3017     }
3019     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3020     {
3021         NSDragOperation ret;
3022         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3023         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3024         NSPasteboard* pb = [sender draggingPasteboard];
3026         macdrv_query* query = macdrv_create_query();
3027         query->type = QUERY_DRAG_OPERATION;
3028         query->window = (macdrv_window)[self retain];
3029         query->drag_operation.x = floor(cgpt.x);
3030         query->drag_operation.y = floor(cgpt.y);
3031         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3032         query->drag_operation.accepted_op = NSDragOperationNone;
3033         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3035         [self.queue query:query timeout:3];
3036         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3037         macdrv_release_query(query);
3039         return ret;
3040     }
3042     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3043     {
3044         BOOL ret;
3045         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3046         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3047         NSPasteboard* pb = [sender draggingPasteboard];
3049         macdrv_query* query = macdrv_create_query();
3050         query->type = QUERY_DRAG_DROP;
3051         query->window = (macdrv_window)[self retain];
3052         query->drag_drop.x = floor(cgpt.x);
3053         query->drag_drop.y = floor(cgpt.y);
3054         query->drag_drop.op = [sender draggingSourceOperationMask];
3055         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3057         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3058         ret = query->status;
3059         macdrv_release_query(query);
3061         return ret;
3062     }
3064     - (BOOL) wantsPeriodicDraggingUpdates
3065     {
3066         return NO;
3067     }
3069 @end
3072 /***********************************************************************
3073  *              macdrv_create_cocoa_window
3075  * Create a Cocoa window with the given content frame and features (e.g.
3076  * title bar, close box, etc.).
3077  */
3078 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3079         CGRect frame, void* hwnd, macdrv_event_queue queue)
3081     __block WineWindow* window;
3083     OnMainThread(^{
3084         window = [[WineWindow createWindowWithFeatures:wf
3085                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3086                                                   hwnd:hwnd
3087                                                  queue:(WineEventQueue*)queue] retain];
3088     });
3090     return (macdrv_window)window;
3093 /***********************************************************************
3094  *              macdrv_destroy_cocoa_window
3096  * Destroy a Cocoa window.
3097  */
3098 void macdrv_destroy_cocoa_window(macdrv_window w)
3100     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3101     WineWindow* window = (WineWindow*)w;
3103     OnMainThread(^{
3104         [window doOrderOut];
3105         [window close];
3106     });
3107     [window.queue discardEventsMatchingMask:-1 forWindow:window];
3108     [window release];
3110     [pool release];
3113 /***********************************************************************
3114  *              macdrv_get_window_hwnd
3116  * Get the hwnd that was set for the window at creation.
3117  */
3118 void* macdrv_get_window_hwnd(macdrv_window w)
3120     WineWindow* window = (WineWindow*)w;
3121     return window.hwnd;
3124 /***********************************************************************
3125  *              macdrv_set_cocoa_window_features
3127  * Update a Cocoa window's features.
3128  */
3129 void macdrv_set_cocoa_window_features(macdrv_window w,
3130         const struct macdrv_window_features* wf)
3132     WineWindow* window = (WineWindow*)w;
3134     OnMainThread(^{
3135         [window setWindowFeatures:wf];
3136     });
3139 /***********************************************************************
3140  *              macdrv_set_cocoa_window_state
3142  * Update a Cocoa window's state.
3143  */
3144 void macdrv_set_cocoa_window_state(macdrv_window w,
3145         const struct macdrv_window_state* state)
3147     WineWindow* window = (WineWindow*)w;
3149     OnMainThread(^{
3150         [window setMacDrvState:state];
3151     });
3154 /***********************************************************************
3155  *              macdrv_set_cocoa_window_title
3157  * Set a Cocoa window's title.
3158  */
3159 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3160         size_t length)
3162     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3163     WineWindow* window = (WineWindow*)w;
3164     NSString* titleString;
3166     if (title)
3167         titleString = [NSString stringWithCharacters:title length:length];
3168     else
3169         titleString = @"";
3170     OnMainThreadAsync(^{
3171         [window setTitle:titleString];
3172         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3173             [NSApp changeWindowsItem:window title:titleString filename:NO];
3174     });
3176     [pool release];
3179 /***********************************************************************
3180  *              macdrv_order_cocoa_window
3182  * Reorder a Cocoa window relative to other windows.  If prev is
3183  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3184  * it is ordered above that window.  Otherwise, it is ordered to the
3185  * front.
3186  */
3187 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3188         macdrv_window n, int activate)
3190     WineWindow* window = (WineWindow*)w;
3191     WineWindow* prev = (WineWindow*)p;
3192     WineWindow* next = (WineWindow*)n;
3194     OnMainThreadAsync(^{
3195         [window orderBelow:prev
3196                    orAbove:next
3197                   activate:activate];
3198     });
3199     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3200                                   forWindow:window];
3201     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3202                                 forWindow:next];
3205 /***********************************************************************
3206  *              macdrv_hide_cocoa_window
3208  * Hides a Cocoa window.
3209  */
3210 void macdrv_hide_cocoa_window(macdrv_window w)
3212     WineWindow* window = (WineWindow*)w;
3214     OnMainThread(^{
3215         [window doOrderOut];
3216     });
3219 /***********************************************************************
3220  *              macdrv_set_cocoa_window_frame
3222  * Move a Cocoa window.
3223  */
3224 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3226     WineWindow* window = (WineWindow*)w;
3228     OnMainThread(^{
3229         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3230     });
3233 /***********************************************************************
3234  *              macdrv_get_cocoa_window_frame
3236  * Gets the frame of a Cocoa window.
3237  */
3238 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3240     WineWindow* window = (WineWindow*)w;
3242     OnMainThread(^{
3243         NSRect frame;
3245         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3246         [[WineApplicationController sharedController] flipRect:&frame];
3247         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3248     });
3251 /***********************************************************************
3252  *              macdrv_set_cocoa_parent_window
3254  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3255  * the parent window.
3256  */
3257 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3259     WineWindow* window = (WineWindow*)w;
3261     OnMainThread(^{
3262         [window setMacDrvParentWindow:(WineWindow*)parent];
3263     });
3266 /***********************************************************************
3267  *              macdrv_set_window_surface
3268  */
3269 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3271     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3272     WineWindow* window = (WineWindow*)w;
3274     OnMainThread(^{
3275         window.surface = surface;
3276         window.surface_mutex = mutex;
3277     });
3279     [pool release];
3282 /***********************************************************************
3283  *              macdrv_window_needs_display
3285  * Mark a window as needing display in a specified rect (in non-client
3286  * area coordinates).
3287  */
3288 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3290     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3291     WineWindow* window = (WineWindow*)w;
3293     OnMainThreadAsync(^{
3294         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3295     });
3297     [pool release];
3300 /***********************************************************************
3301  *              macdrv_set_window_shape
3303  * Sets the shape of a Cocoa window from an array of rectangles.  If
3304  * rects is NULL, resets the window's shape to its frame.
3305  */
3306 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3308     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3309     WineWindow* window = (WineWindow*)w;
3311     OnMainThread(^{
3312         if (!rects || !count)
3313         {
3314             window.shape = nil;
3315             window.shapeData = nil;
3316             [window checkEmptyShaped];
3317         }
3318         else
3319         {
3320             size_t length = sizeof(*rects) * count;
3321             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3322             {
3323                 NSBezierPath* path;
3324                 unsigned int i;
3326                 path = [NSBezierPath bezierPath];
3327                 for (i = 0; i < count; i++)
3328                     [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3329                 window.shape = path;
3330                 window.shapeData = [NSData dataWithBytes:rects length:length];
3331                 [window checkEmptyShaped];
3332             }
3333         }
3334     });
3336     [pool release];
3339 /***********************************************************************
3340  *              macdrv_set_window_alpha
3341  */
3342 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3344     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3345     WineWindow* window = (WineWindow*)w;
3347     [window setAlphaValue:alpha];
3349     [pool release];
3352 /***********************************************************************
3353  *              macdrv_set_window_color_key
3354  */
3355 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3356                                  CGFloat keyBlue)
3358     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3359     WineWindow* window = (WineWindow*)w;
3361     OnMainThread(^{
3362         window.colorKeyed       = TRUE;
3363         window.colorKeyRed      = keyRed;
3364         window.colorKeyGreen    = keyGreen;
3365         window.colorKeyBlue     = keyBlue;
3366         [window checkTransparency];
3367     });
3369     [pool release];
3372 /***********************************************************************
3373  *              macdrv_clear_window_color_key
3374  */
3375 void macdrv_clear_window_color_key(macdrv_window w)
3377     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3378     WineWindow* window = (WineWindow*)w;
3380     OnMainThread(^{
3381         window.colorKeyed = FALSE;
3382         [window checkTransparency];
3383     });
3385     [pool release];
3388 /***********************************************************************
3389  *              macdrv_window_use_per_pixel_alpha
3390  */
3391 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3393     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3394     WineWindow* window = (WineWindow*)w;
3396     OnMainThread(^{
3397         window.usePerPixelAlpha = use_per_pixel_alpha;
3398         [window checkTransparency];
3399     });
3401     [pool release];
3404 /***********************************************************************
3405  *              macdrv_give_cocoa_window_focus
3407  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3408  * orders it front and, if its frame was not within the desktop bounds,
3409  * Cocoa will typically move it on-screen.
3410  */
3411 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3413     WineWindow* window = (WineWindow*)w;
3415     OnMainThread(^{
3416         [window makeFocused:activate];
3417     });
3420 /***********************************************************************
3421  *              macdrv_set_window_min_max_sizes
3423  * Sets the window's minimum and maximum content sizes.
3424  */
3425 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3427     WineWindow* window = (WineWindow*)w;
3429     OnMainThread(^{
3430         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3431     });
3434 /***********************************************************************
3435  *              macdrv_create_view
3437  * Creates and returns a view with the specified frame rect.  The
3438  * caller is responsible for calling macdrv_dispose_view() on the view
3439  * when it is done with it.
3440  */
3441 macdrv_view macdrv_create_view(CGRect rect)
3443     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3444     __block WineContentView* view;
3446     if (CGRectIsNull(rect)) rect = CGRectZero;
3448     OnMainThread(^{
3449         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3451         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3452         [view setAutoresizesSubviews:NO];
3453         [view setHidden:YES];
3454         [nc addObserver:view
3455                selector:@selector(updateGLContexts)
3456                    name:NSViewGlobalFrameDidChangeNotification
3457                  object:view];
3458         [nc addObserver:view
3459                selector:@selector(updateGLContexts)
3460                    name:NSApplicationDidChangeScreenParametersNotification
3461                  object:NSApp];
3462     });
3464     [pool release];
3465     return (macdrv_view)view;
3468 /***********************************************************************
3469  *              macdrv_dispose_view
3471  * Destroys a view previously returned by macdrv_create_view.
3472  */
3473 void macdrv_dispose_view(macdrv_view v)
3475     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3476     WineContentView* view = (WineContentView*)v;
3478     OnMainThread(^{
3479         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3480         WineWindow* window = (WineWindow*)[view window];
3482         [nc removeObserver:view
3483                       name:NSViewGlobalFrameDidChangeNotification
3484                     object:view];
3485         [nc removeObserver:view
3486                       name:NSApplicationDidChangeScreenParametersNotification
3487                     object:NSApp];
3488         [view removeFromSuperview];
3489         [view release];
3490         [window updateForGLSubviews];
3491     });
3493     [pool release];
3496 /***********************************************************************
3497  *              macdrv_set_view_frame
3498  */
3499 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3501     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3502     WineContentView* view = (WineContentView*)v;
3504     if (CGRectIsNull(rect)) rect = CGRectZero;
3506     OnMainThreadAsync(^{
3507         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3508         NSRect oldFrame = [view frame];
3510         if (!NSEqualRects(oldFrame, newFrame))
3511         {
3512             [[view superview] setNeedsDisplayInRect:oldFrame];
3513             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3514                 [view setFrameSize:newFrame.size];
3515             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3516                 [view setFrameOrigin:newFrame.origin];
3517             else
3518                 [view setFrame:newFrame];
3519             [view setNeedsDisplay:YES];
3521             if (retina_enabled)
3522             {
3523                 int backing_size[2] = { 0 };
3524                 [view wine_setBackingSize:backing_size];
3525             }
3526             [(WineWindow*)[view window] updateForGLSubviews];
3527         }
3528     });
3530     [pool release];
3533 /***********************************************************************
3534  *              macdrv_set_view_superview
3536  * Move a view to a new superview and position it relative to its
3537  * siblings.  If p is non-NULL, the view is ordered behind it.
3538  * Otherwise, the view is ordered above n.  If s is NULL, use the
3539  * content view of w as the new superview.
3540  */
3541 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3543     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3544     WineContentView* view = (WineContentView*)v;
3545     WineContentView* superview = (WineContentView*)s;
3546     WineWindow* window = (WineWindow*)w;
3547     WineContentView* prev = (WineContentView*)p;
3548     WineContentView* next = (WineContentView*)n;
3550     if (!superview)
3551         superview = [window contentView];
3553     OnMainThreadAsync(^{
3554         if (superview == [view superview])
3555         {
3556             NSArray* subviews = [superview subviews];
3557             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3558             if (!prev && !next && index == [subviews count] - 1)
3559                 return;
3560             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3561                 return;
3562             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3563                 return;
3564         }
3566         WineWindow* oldWindow = (WineWindow*)[view window];
3567         WineWindow* newWindow = (WineWindow*)[superview window];
3569 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3570         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3571             [view removeFromSuperview];
3572 #endif
3573         if (prev)
3574             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3575         else
3576             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3578         if (oldWindow != newWindow)
3579         {
3580             [oldWindow updateForGLSubviews];
3581             [newWindow updateForGLSubviews];
3582         }
3583     });
3585     [pool release];
3588 /***********************************************************************
3589  *              macdrv_set_view_hidden
3590  */
3591 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3593     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3594     WineContentView* view = (WineContentView*)v;
3596     OnMainThreadAsync(^{
3597         [view setHidden:hidden];
3598         [(WineWindow*)view.window updateForGLSubviews];
3599     });
3601     [pool release];
3604 /***********************************************************************
3605  *              macdrv_add_view_opengl_context
3607  * Add an OpenGL context to the list being tracked for each view.
3608  */
3609 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3611     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3612     WineContentView* view = (WineContentView*)v;
3613     WineOpenGLContext *context = (WineOpenGLContext*)c;
3615     OnMainThread(^{
3616         [view addGLContext:context];
3617     });
3619     [pool release];
3622 /***********************************************************************
3623  *              macdrv_remove_view_opengl_context
3625  * Add an OpenGL context to the list being tracked for each view.
3626  */
3627 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3629     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3630     WineContentView* view = (WineContentView*)v;
3631     WineOpenGLContext *context = (WineOpenGLContext*)c;
3633     OnMainThreadAsync(^{
3634         [view removeGLContext:context];
3635     });
3637     [pool release];
3640 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3642     WineContentView* view = (WineContentView*)v;
3644     if (![view isKindOfClass:[WineContentView class]])
3645         return FALSE;
3647     [view wine_getBackingSize:backing_size];
3648     return TRUE;
3651 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3653     WineContentView* view = (WineContentView*)v;
3655     if ([view isKindOfClass:[WineContentView class]])
3656         [view wine_setBackingSize:backing_size];
3659 /***********************************************************************
3660  *              macdrv_window_background_color
3662  * Returns the standard Mac window background color as a 32-bit value of
3663  * the form 0x00rrggbb.
3664  */
3665 uint32_t macdrv_window_background_color(void)
3667     static uint32_t result;
3668     static dispatch_once_t once;
3670     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3671     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3672     // of it is to draw with it.
3673     dispatch_once(&once, ^{
3674         OnMainThread(^{
3675             unsigned char rgbx[4];
3676             unsigned char *planes = rgbx;
3677             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3678                                                                                pixelsWide:1
3679                                                                                pixelsHigh:1
3680                                                                             bitsPerSample:8
3681                                                                           samplesPerPixel:3
3682                                                                                  hasAlpha:NO
3683                                                                                  isPlanar:NO
3684                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3685                                                                              bitmapFormat:0
3686                                                                               bytesPerRow:4
3687                                                                              bitsPerPixel:32];
3688             [NSGraphicsContext saveGraphicsState];
3689             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3690             [[NSColor windowBackgroundColor] set];
3691             NSRectFill(NSMakeRect(0, 0, 1, 1));
3692             [NSGraphicsContext restoreGraphicsState];
3693             [bitmap release];
3694             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3695         });
3696     });
3698     return result;
3701 /***********************************************************************
3702  *              macdrv_send_text_input_event
3703  */
3704 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3706     OnMainThreadAsync(^{
3707         BOOL ret;
3708         macdrv_event* event;
3709         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3710         if (![window isKindOfClass:[WineWindow class]])
3711         {
3712             window = (WineWindow*)[NSApp mainWindow];
3713             if (![window isKindOfClass:[WineWindow class]])
3714                 window = [[WineApplicationController sharedController] frontWineWindow];
3715         }
3717         if (window)
3718         {
3719             NSUInteger localFlags = flags;
3720             CGEventRef c;
3721             NSEvent* event;
3723             window.imeData = data;
3724             fix_device_modifiers_by_generic(&localFlags);
3726             // An NSEvent created with +keyEventWithType:... is internally marked
3727             // as synthetic and doesn't get sent through input methods.  But one
3728             // created from a CGEvent doesn't have that problem.
3729             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3730             CGEventSetFlags(c, localFlags);
3731             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3732             event = [NSEvent eventWithCGEvent:c];
3733             CFRelease(c);
3735             window.commandDone = FALSE;
3736             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3737         }
3738         else
3739             ret = FALSE;
3741         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3742         event->sent_text_input.handled = ret;
3743         event->sent_text_input.done = done;
3744         [[window queue] postEvent:event];
3745         macdrv_release_event(event);
3746     });