ddraw/tests: Remove p8_primary_test.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blobd3b3c8981dedcaec2df0f3f809b743b04c0d7dd7
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 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
47 enum {
48     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
52 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
54     NSUInteger style_mask;
56     if (wf->title_bar)
57     {
58         style_mask = NSTitledWindowMask;
59         if (wf->close_button) style_mask |= NSClosableWindowMask;
60         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
61         if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
62         if (wf->utility) style_mask |= NSUtilityWindowMask;
63     }
64     else style_mask = NSBorderlessWindowMask;
66     return style_mask;
70 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
72     NSScreen* screen;
73     for (screen in screens)
74     {
75         if (NSIntersectsRect(frame, [screen frame]))
76             return TRUE;
77     }
78     return FALSE;
82 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
84     for (NSScreen* screen in screens)
85     {
86         if (NSContainsRect(rect, [screen frame]))
87             return screen;
88     }
89     return nil;
93 /* We rely on the supposedly device-dependent modifier flags to distinguish the
94    keys on the left side of the keyboard from those on the right.  Some event
95    sources don't set those device-depdendent flags.  If we see a device-independent
96    flag for a modifier without either corresponding device-dependent flag, assume
97    the left one. */
98 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
100     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
101         *modifiers |= NX_DEVICELCMDKEYMASK;
102     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
103         *modifiers |= NX_DEVICELSHIFTKEYMASK;
104     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
105         *modifiers |= NX_DEVICELCTLKEYMASK;
106     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
107         *modifiers |= NX_DEVICELALTKEYMASK;
110 /* As we manipulate individual bits of a modifier mask, we can end up with
111    inconsistent sets of flags.  In particular, we might set or clear one of the
112    left/right-specific bits, but not the corresponding non-side-specific bit.
113    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
114    otherwise clear it. */
115 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
117     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
118         *modifiers |= NX_COMMANDMASK;
119     else
120         *modifiers &= ~NX_COMMANDMASK;
121     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
122         *modifiers |= NX_SHIFTMASK;
123     else
124         *modifiers &= ~NX_SHIFTMASK;
125     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
126         *modifiers |= NX_CONTROLMASK;
127     else
128         *modifiers &= ~NX_CONTROLMASK;
129     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
130         *modifiers |= NX_ALTERNATEMASK;
131     else
132         *modifiers &= ~NX_ALTERNATEMASK;
135 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
137     fix_device_modifiers_by_generic(&modifiers);
138     if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
139     {
140         modifiers |= NX_DEVICELCMDKEYMASK;
141         modifiers &= ~NX_DEVICELALTKEYMASK;
142     }
143     if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
144     {
145         modifiers |= NX_DEVICERCMDKEYMASK;
146         modifiers &= ~NX_DEVICERALTKEYMASK;
147     }
148     fix_generic_modifiers_by_device(&modifiers);
150     return modifiers;
154 @interface NSWindow (WineAccessPrivateMethods)
155     - (id) _displayChanged;
156 @end
159 @interface WineDisplayLink : NSObject
161     CGDirectDisplayID _displayID;
162     CVDisplayLinkRef _link;
163     NSMutableSet* _windows;
165     NSTimeInterval _actualRefreshPeriod;
166     NSTimeInterval _nominalRefreshPeriod;
169     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
171     - (void) addWindow:(WineWindow*)window;
172     - (void) removeWindow:(WineWindow*)window;
174     - (NSTimeInterval) refreshPeriod;
176     - (void) start;
178 @end
180 @implementation WineDisplayLink
182 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
184     - (id) initWithDisplayID:(CGDirectDisplayID)displayID
185     {
186         self = [super init];
187         if (self)
188         {
189             CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
190             if (status == kCVReturnSuccess && !_link)
191                 status = kCVReturnError;
192             if (status == kCVReturnSuccess)
193                 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
194             if (status != kCVReturnSuccess)
195             {
196                 [self release];
197                 return nil;
198             }
200             _displayID = displayID;
201             _windows = [[NSMutableSet alloc] init];
202         }
203         return self;
204     }
206     - (void) dealloc
207     {
208         if (_link)
209         {
210             CVDisplayLinkStop(_link);
211             CVDisplayLinkRelease(_link);
212         }
213         [_windows release];
214         [super dealloc];
215     }
217     - (void) addWindow:(WineWindow*)window
218     {
219         @synchronized(self) {
220             BOOL needsStart = !_windows.count;
221             [_windows addObject:window];
222             if (needsStart)
223                 CVDisplayLinkStart(_link);
224         }
225     }
227     - (void) removeWindow:(WineWindow*)window
228     {
229         @synchronized(self) {
230             BOOL wasRunning = _windows.count > 0;
231             [_windows removeObject:window];
232             if (wasRunning && !_windows.count)
233                 CVDisplayLinkStop(_link);
234         }
235     }
237     - (void) fire
238     {
239         NSSet* windows;
240         @synchronized(self) {
241             windows = [_windows copy];
242         }
243         dispatch_async(dispatch_get_main_queue(), ^{
244             BOOL anyDisplayed = FALSE;
245             for (WineWindow* window in windows)
246             {
247                 if ([window viewsNeedDisplay])
248                 {
249                     [window displayIfNeeded];
250                     anyDisplayed = YES;
251                 }
252             }
253             if (!anyDisplayed)
254                 CVDisplayLinkStop(_link);
255         });
256         [windows release];
257     }
259     - (NSTimeInterval) refreshPeriod
260     {
261         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
262             return _actualRefreshPeriod;
264         if (_nominalRefreshPeriod)
265             return _nominalRefreshPeriod;
267         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
268         if (time.flags & kCVTimeIsIndefinite)
269             return 1.0 / 60.0;
270         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
271         return _nominalRefreshPeriod;
272     }
274     - (void) start
275     {
276         CVDisplayLinkStart(_link);
277     }
279 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
281     WineDisplayLink* link = displayLinkContext;
282     [link fire];
283     return kCVReturnSuccess;
286 @end
289 @interface WineContentView : NSView <NSTextInputClient>
291     NSMutableArray* glContexts;
292     NSMutableArray* pendingGlContexts;
293     BOOL _cachedHasGLDescendant;
294     BOOL _cachedHasGLDescendantValid;
295     BOOL clearedGlSurface;
297     NSMutableAttributedString* markedText;
298     NSRange markedTextSelection;
300     int backingSize[2];
303     - (void) addGLContext:(WineOpenGLContext*)context;
304     - (void) removeGLContext:(WineOpenGLContext*)context;
305     - (void) updateGLContexts;
307     - (void) wine_getBackingSize:(int*)outBackingSize;
308     - (void) wine_setBackingSize:(const int*)newBackingSize;
310 @end
313 @interface WineWindow ()
315 @property (readwrite, nonatomic) BOOL disabled;
316 @property (readwrite, nonatomic) BOOL noActivate;
317 @property (readwrite, nonatomic) BOOL floating;
318 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
319 @property (retain, nonatomic) NSWindow* latentParentWindow;
321 @property (nonatomic) void* hwnd;
322 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
324 @property (nonatomic) void* surface;
325 @property (nonatomic) pthread_mutex_t* surface_mutex;
327 @property (copy, nonatomic) NSBezierPath* shape;
328 @property (copy, nonatomic) NSData* shapeData;
329 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
330 @property (readonly, nonatomic) BOOL needsTransparency;
332 @property (nonatomic) BOOL colorKeyed;
333 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
334 @property (nonatomic) BOOL usePerPixelAlpha;
336 @property (assign, nonatomic) void* imeData;
337 @property (nonatomic) BOOL commandDone;
339 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
341     - (void) updateColorSpace;
342     - (void) updateForGLSubviews;
344     - (BOOL) becameEligibleParentOrChild;
345     - (void) becameIneligibleChild;
347 @end
350 @implementation WineContentView
352     - (void) dealloc
353     {
354         [markedText release];
355         [glContexts release];
356         [pendingGlContexts release];
357         [super dealloc];
358     }
360     - (BOOL) isFlipped
361     {
362         return YES;
363     }
365     - (void) drawRect:(NSRect)rect
366     {
367         WineWindow* window = (WineWindow*)[self window];
369         for (WineOpenGLContext* context in pendingGlContexts)
370         {
371             if (!clearedGlSurface)
372             {
373                 context.shouldClearToBlack = TRUE;
374                 clearedGlSurface = TRUE;
375             }
376             context.needsUpdate = TRUE;
377         }
378         [glContexts addObjectsFromArray:pendingGlContexts];
379         [pendingGlContexts removeAllObjects];
381         if ([window contentView] != self)
382             return;
384         if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
385         {
386             [[NSColor clearColor] setFill];
387             NSRectFill(rect);
389             [window.shape addClip];
391             [[NSColor windowBackgroundColor] setFill];
392             NSRectFill(rect);
393         }
395         if (window.surface && window.surface_mutex &&
396             !pthread_mutex_lock(window.surface_mutex))
397         {
398             const CGRect* rects;
399             int count;
401             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
402             {
403                 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
404                 CGContextRef context;
405                 int i;
407                 [window.shape addClip];
409                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
410                 CGContextSetBlendMode(context, kCGBlendModeCopy);
411                 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
413                 for (i = 0; i < count; i++)
414                 {
415                     CGRect imageRect;
416                     CGImageRef image;
418                     imageRect = CGRectIntersection(rects[i], dirtyRect);
419                     image = create_surface_image(window.surface, &imageRect, FALSE);
421                     if (image)
422                     {
423                         if (window.colorKeyed)
424                         {
425                             CGImageRef maskedImage;
426                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
427                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
428                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
429                             maskedImage = CGImageCreateWithMaskingColors(image, components);
430                             if (maskedImage)
431                             {
432                                 CGImageRelease(image);
433                                 image = maskedImage;
434                             }
435                         }
437                         CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
439                         CGImageRelease(image);
440                     }
441                 }
442             }
444             pthread_mutex_unlock(window.surface_mutex);
445         }
447         // If the window may be transparent, then we have to invalidate the
448         // shadow every time we draw.  Also, if this is the first time we've
449         // drawn since changing from transparent to opaque.
450         if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
451         {
452             window.shapeChangedSinceLastDraw = FALSE;
453             [window invalidateShadow];
454         }
455     }
457     - (void) addGLContext:(WineOpenGLContext*)context
458     {
459         BOOL hadContext = [self hasGLContext];
460         if (!glContexts)
461             glContexts = [[NSMutableArray alloc] init];
462         if (!pendingGlContexts)
463             pendingGlContexts = [[NSMutableArray alloc] init];
465         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
466         {
467             [glContexts addObject:context];
468             if (!clearedGlSurface)
469             {
470                 context.shouldClearToBlack = TRUE;
471                 clearedGlSurface = TRUE;
472             }
473             context.needsUpdate = TRUE;
474         }
475         else
476         {
477             [pendingGlContexts addObject:context];
478             [self setNeedsDisplay:YES];
479         }
481         if (!hadContext)
482             [self invalidateHasGLDescendant];
483         [(WineWindow*)[self window] updateForGLSubviews];
484     }
486     - (void) removeGLContext:(WineOpenGLContext*)context
487     {
488         BOOL hadContext = [self hasGLContext];
489         [glContexts removeObjectIdenticalTo:context];
490         [pendingGlContexts removeObjectIdenticalTo:context];
491         if (hadContext && ![self hasGLContext])
492             [self invalidateHasGLDescendant];
493         [(WineWindow*)[self window] updateForGLSubviews];
494     }
496     - (void) updateGLContexts
497     {
498         for (WineOpenGLContext* context in glContexts)
499             context.needsUpdate = TRUE;
500     }
502     - (BOOL) hasGLContext
503     {
504         return [glContexts count] || [pendingGlContexts count];
505     }
507     - (BOOL) _hasGLDescendant
508     {
509         if ([self isHidden])
510             return NO;
511         if ([self hasGLContext])
512             return YES;
513         for (WineContentView* view in [self subviews])
514         {
515             if ([view hasGLDescendant])
516                 return YES;
517         }
518         return NO;
519     }
521     - (BOOL) hasGLDescendant
522     {
523         if (!_cachedHasGLDescendantValid)
524         {
525             _cachedHasGLDescendant = [self _hasGLDescendant];
526             _cachedHasGLDescendantValid = YES;
527         }
528         return _cachedHasGLDescendant;
529     }
531     - (void) invalidateHasGLDescendant
532     {
533         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
534         _cachedHasGLDescendantValid = NO;
535         if (invalidateAncestors && self != [[self window] contentView])
536         {
537             WineContentView* superview = (WineContentView*)[self superview];
538             if ([superview isKindOfClass:[WineContentView class]])
539                 [superview invalidateHasGLDescendant];
540         }
541     }
543     - (void) wine_getBackingSize:(int*)outBackingSize
544     {
545         @synchronized(self) {
546             memcpy(outBackingSize, backingSize, sizeof(backingSize));
547         }
548     }
549     - (void) wine_setBackingSize:(const int*)newBackingSize
550     {
551         @synchronized(self) {
552             memcpy(backingSize, newBackingSize, sizeof(backingSize));
553         }
554     }
556     - (void) setRetinaMode:(int)mode
557     {
558         double scale = mode ? 0.5 : 2.0;
559         NSRect frame = self.frame;
560         frame.origin.x *= scale;
561         frame.origin.y *= scale;
562         frame.size.width *= scale;
563         frame.size.height *= scale;
564         [self setFrame:frame];
565         [self updateGLContexts];
567         for (WineContentView* subview in [self subviews])
568         {
569             if ([subview isKindOfClass:[WineContentView class]])
570                 [subview setRetinaMode:mode];
571         }
572     }
574     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
575     {
576         return YES;
577     }
579     - (BOOL) preservesContentDuringLiveResize
580     {
581         // Returning YES from this tells Cocoa to keep our view's content during
582         // a Cocoa-driven resize.  In theory, we're also supposed to override
583         // -setFrameSize: to mark exposed sections as needing redisplay, but
584         // user32 will take care of that in a roundabout way.  This way, we don't
585         // redraw until the window surface is flushed.
586         //
587         // This doesn't do anything when we resize the window ourselves.
588         return YES;
589     }
591     - (BOOL)acceptsFirstResponder
592     {
593         return [[self window] contentView] == self;
594     }
596     - (BOOL) mouseDownCanMoveWindow
597     {
598         return NO;
599     }
601     - (void) completeText:(NSString*)text
602     {
603         macdrv_event* event;
604         WineWindow* window = (WineWindow*)[self window];
606         event = macdrv_create_event(IM_SET_TEXT, window);
607         event->im_set_text.data = [window imeData];
608         event->im_set_text.text = (CFStringRef)[text copy];
609         event->im_set_text.complete = TRUE;
611         [[window queue] postEvent:event];
613         macdrv_release_event(event);
615         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
616         markedTextSelection = NSMakeRange(0, 0);
617         [[self inputContext] discardMarkedText];
618     }
620     - (NSFocusRingType) focusRingType
621     {
622         return NSFocusRingTypeNone;
623     }
625     - (void) didAddSubview:(NSView*)subview
626     {
627         if ([subview isKindOfClass:[WineContentView class]])
628         {
629             WineContentView* view = (WineContentView*)subview;
630             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
631                 [self invalidateHasGLDescendant];
632         }
633         [super didAddSubview:subview];
634     }
636     - (void) willRemoveSubview:(NSView*)subview
637     {
638         if ([subview isKindOfClass:[WineContentView class]])
639         {
640             WineContentView* view = (WineContentView*)subview;
641             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
642                 [self invalidateHasGLDescendant];
643         }
644         [super willRemoveSubview:subview];
645     }
647     - (void) setHidden:(BOOL)hidden
648     {
649         [super setHidden:hidden];
650         [self invalidateHasGLDescendant];
651     }
653     /*
654      * ---------- NSTextInputClient methods ----------
655      */
656     - (NSTextInputContext*) inputContext
657     {
658         if (!markedText)
659             markedText = [[NSMutableAttributedString alloc] init];
660         return [super inputContext];
661     }
663     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
664     {
665         if ([string isKindOfClass:[NSAttributedString class]])
666             string = [string string];
668         if ([string isKindOfClass:[NSString class]])
669             [self completeText:string];
670     }
672     - (void) doCommandBySelector:(SEL)aSelector
673     {
674         [(WineWindow*)[self window] setCommandDone:TRUE];
675     }
677     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
678     {
679         if ([string isKindOfClass:[NSAttributedString class]])
680             string = [string string];
682         if ([string isKindOfClass:[NSString class]])
683         {
684             macdrv_event* event;
685             WineWindow* window = (WineWindow*)[self window];
687             if (replacementRange.location == NSNotFound)
688                 replacementRange = NSMakeRange(0, [markedText length]);
690             [markedText replaceCharactersInRange:replacementRange withString:string];
691             markedTextSelection = selectedRange;
692             markedTextSelection.location += replacementRange.location;
694             event = macdrv_create_event(IM_SET_TEXT, window);
695             event->im_set_text.data = [window imeData];
696             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
697             event->im_set_text.complete = FALSE;
698             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
700             [[window queue] postEvent:event];
702             macdrv_release_event(event);
704             [[self inputContext] invalidateCharacterCoordinates];
705         }
706     }
708     - (void) unmarkText
709     {
710         [self completeText:nil];
711     }
713     - (NSRange) selectedRange
714     {
715         return markedTextSelection;
716     }
718     - (NSRange) markedRange
719     {
720         NSRange range = NSMakeRange(0, [markedText length]);
721         if (!range.length)
722             range.location = NSNotFound;
723         return range;
724     }
726     - (BOOL) hasMarkedText
727     {
728         return [markedText length] > 0;
729     }
731     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
732     {
733         if (aRange.location >= [markedText length])
734             return nil;
736         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
737         if (actualRange)
738             *actualRange = aRange;
739         return [markedText attributedSubstringFromRange:aRange];
740     }
742     - (NSArray*) validAttributesForMarkedText
743     {
744         return [NSArray array];
745     }
747     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
748     {
749         macdrv_query* query;
750         WineWindow* window = (WineWindow*)[self window];
751         NSRect ret;
753         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
755         query = macdrv_create_query();
756         query->type = QUERY_IME_CHAR_RECT;
757         query->window = (macdrv_window)[window retain];
758         query->ime_char_rect.data = [window imeData];
759         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
761         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
762         {
763             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
764             ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
765             [[WineApplicationController sharedController] flipRect:&ret];
766         }
767         else
768             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
770         macdrv_release_query(query);
772         if (actualRange)
773             *actualRange = aRange;
774         return ret;
775     }
777     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
778     {
779         return NSNotFound;
780     }
782     - (NSInteger) windowLevel
783     {
784         return [[self window] level];
785     }
787 @end
790 @implementation WineWindow
792     static WineWindow* causing_becomeKeyWindow;
794     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
795     @synthesize surface, surface_mutex;
796     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
797     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
798     @synthesize usePerPixelAlpha;
799     @synthesize imeData, commandDone;
801     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
802                                  windowFrame:(NSRect)window_frame
803                                         hwnd:(void*)hwnd
804                                        queue:(WineEventQueue*)queue
805     {
806         WineWindow* window;
807         WineContentView* contentView;
808         NSTrackingArea* trackingArea;
809         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
811         [[WineApplicationController sharedController] flipRect:&window_frame];
813         window = [[[self alloc] initWithContentRect:window_frame
814                                           styleMask:style_mask_for_features(wf)
815                                             backing:NSBackingStoreBuffered
816                                               defer:YES] autorelease];
818         if (!window) return nil;
820         /* Standardize windows to eliminate differences between titled and
821            borderless windows and between NSWindow and NSPanel. */
822         [window setHidesOnDeactivate:NO];
823         [window setReleasedWhenClosed:NO];
825         [window setOneShot:YES];
826         [window disableCursorRects];
827         [window setShowsResizeIndicator:NO];
828         [window setHasShadow:wf->shadow];
829         [window setAcceptsMouseMovedEvents:YES];
830         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
831         [window setDelegate:window];
832         [window setAutodisplay:NO];
833         window.hwnd = hwnd;
834         window.queue = queue;
835         window->savedContentMinSize = NSZeroSize;
836         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
837         window->resizable = wf->resizable;
838         window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
840         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
841                                                                   (NSString*)kUTTypeContent,
842                                                                   nil]];
844         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
845         if (!contentView)
846             return nil;
847         [contentView setAutoresizesSubviews:NO];
849         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
850            because they give us mouse moves in the background. */
851         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
852                                                      options:(NSTrackingMouseMoved |
853                                                               NSTrackingActiveAlways |
854                                                               NSTrackingInVisibleRect)
855                                                        owner:window
856                                                     userInfo:nil] autorelease];
857         if (!trackingArea)
858             return nil;
859         [contentView addTrackingArea:trackingArea];
861         [window setContentView:contentView];
862         [window setInitialFirstResponder:contentView];
864         [nc addObserver:window
865                selector:@selector(updateFullscreen)
866                    name:NSApplicationDidChangeScreenParametersNotification
867                  object:NSApp];
868         [window updateFullscreen];
870         [nc addObserver:window
871                selector:@selector(applicationWillHide)
872                    name:NSApplicationWillHideNotification
873                  object:NSApp];
874         [nc addObserver:window
875                selector:@selector(applicationDidUnhide)
876                    name:NSApplicationDidUnhideNotification
877                  object:NSApp];
879         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
880                                                               selector:@selector(checkWineDisplayLink)
881                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
882                                                                 object:[NSWorkspace sharedWorkspace]];
884         [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
886         return window;
887     }
889     - (void) dealloc
890     {
891         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
892         [[NSNotificationCenter defaultCenter] removeObserver:self];
893         [queue release];
894         [latentChildWindows release];
895         [latentParentWindow release];
896         [shape release];
897         [shapeData release];
898         [super dealloc];
899     }
901     - (BOOL) preventResizing
902     {
903         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
904         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
905     }
907     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
908     {
909         if (allow_immovable_windows && (disabled || inMaximized))
910             return NO;
911         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
912             return NO;
913         else
914             return YES;
915     }
917     - (void) adjustFeaturesForState
918     {
919         NSUInteger style = [self styleMask];
921         if (style & NSClosableWindowMask)
922             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
923         if (style & NSMiniaturizableWindowMask)
924             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
925         if (style & NSResizableWindowMask)
926             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
927         if ([self respondsToSelector:@selector(toggleFullScreen:)])
928         {
929             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
930                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
931         }
933         if ([self preventResizing])
934         {
935             NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
936             [self setContentMinSize:size];
937             [self setContentMaxSize:size];
938         }
939         else
940         {
941             [self setContentMaxSize:savedContentMaxSize];
942             [self setContentMinSize:savedContentMinSize];
943         }
945         if (allow_immovable_windows || cursor_clipping_locks_windows)
946             [self setMovable:[self allowsMovingWithMaximized:maximized]];
947     }
949     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
950     {
951         if ([self respondsToSelector:@selector(toggleFullScreen:)])
952         {
953             NSUInteger style = [self styleMask];
955             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
956                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized)
957             {
958                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
959                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
960             }
961             else
962             {
963                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
964                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
965                 if (style & NSFullScreenWindowMask)
966                     [super toggleFullScreen:nil];
967             }
968         }
970         if (behavior != [self collectionBehavior])
971         {
972             [self setCollectionBehavior:behavior];
973             [self adjustFeaturesForState];
974         }
975     }
977     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
978     {
979         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
980                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
981         NSUInteger currentStyle = [self styleMask];
982         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
984         if (newStyle != currentStyle)
985         {
986             NSString* title = [[[self title] copy] autorelease];
987             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
988             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
989             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
990             {
991                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
992                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
993                 // just NSTitledWindowMask, the window buttons should disappear rather
994                 // than just being disabled.  But they don't.  Similarly in reverse.
995                 // The workaround is to also toggle NSClosableWindowMask at the same time.
996                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
997             }
998             [self setStyleMask:newStyle];
1000             // -setStyleMask: resets the firstResponder to the window.  Set it
1001             // back to the content view.
1002             if ([[self contentView] acceptsFirstResponder])
1003                 [self makeFirstResponder:[self contentView]];
1005             [self adjustFullScreenBehavior:[self collectionBehavior]];
1007             if ([[self title] length] == 0 && [title length] > 0)
1008                 [self setTitle:title];
1009         }
1011         resizable = wf->resizable;
1012         [self adjustFeaturesForState];
1013         [self setHasShadow:wf->shadow];
1014     }
1016     // Indicates if the window would be visible if the app were not hidden.
1017     - (BOOL) wouldBeVisible
1018     {
1019         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1020     }
1022     - (BOOL) isOrderedIn
1023     {
1024         return [self wouldBeVisible] || [self isMiniaturized];
1025     }
1027     - (NSInteger) minimumLevelForActive:(BOOL)active
1028     {
1029         NSInteger level;
1031         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1032                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1033             level = NSFloatingWindowLevel;
1034         else
1035             level = NSNormalWindowLevel;
1037         if (active)
1038         {
1039             BOOL captured;
1041             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1043             if (captured || fullscreen)
1044             {
1045                 if (captured)
1046                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1047                 else
1048                     level = NSStatusWindowLevel + 1;
1050                 if (self.floating)
1051                     level++;
1052             }
1053         }
1055         return level;
1056     }
1058     - (void) postDidUnminimizeEvent
1059     {
1060         macdrv_event* event;
1062         /* Coalesce events by discarding any previous ones still in the queue. */
1063         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1064                                forWindow:self];
1066         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1067         [queue postEvent:event];
1068         macdrv_release_event(event);
1069     }
1071     - (void) sendResizeStartQuery
1072     {
1073         macdrv_query* query = macdrv_create_query();
1074         query->type = QUERY_RESIZE_START;
1075         query->window = (macdrv_window)[self retain];
1077         [self.queue query:query timeout:0.3];
1078         macdrv_release_query(query);
1079     }
1081     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1082     {
1083         NSWindowCollectionBehavior behavior;
1085         self.disabled = state->disabled;
1086         self.noActivate = state->no_activate;
1088         if (self.floating != state->floating)
1089         {
1090             self.floating = state->floating;
1091             if (state->floating)
1092             {
1093                 // Became floating.  If child of non-floating window, make that
1094                 // relationship latent.
1095                 WineWindow* parent = (WineWindow*)[self parentWindow];
1096                 if (parent && !parent.floating)
1097                     [self becameIneligibleChild];
1098             }
1099             else
1100             {
1101                 // Became non-floating.  If parent of floating children, make that
1102                 // relationship latent.
1103                 WineWindow* child;
1104                 for (child in [self childWineWindows])
1105                 {
1106                     if (child.floating)
1107                         [child becameIneligibleChild];
1108                 }
1109             }
1111             // Check our latent relationships.  If floating status was the only
1112             // reason they were latent, then make them active.
1113             if ([self isVisible])
1114                 [self becameEligibleParentOrChild];
1116             [[WineApplicationController sharedController] adjustWindowLevels];
1117         }
1119         if (state->minimized_valid)
1120         {
1121             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1123             pendingMinimize = FALSE;
1124             if (state->minimized && ![self isMiniaturized])
1125             {
1126                 if ([self wouldBeVisible])
1127                 {
1128                     if ([self styleMask] & NSFullScreenWindowMask)
1129                     {
1130                         [self postDidUnminimizeEvent];
1131                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1132                     }
1133                     else
1134                     {
1135                         [super miniaturize:nil];
1136                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1137                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1138                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1139                     }
1140                 }
1141                 else
1142                     pendingMinimize = TRUE;
1143             }
1144             else if (!state->minimized && [self isMiniaturized])
1145             {
1146                 ignore_windowDeminiaturize = TRUE;
1147                 [self deminiaturize:nil];
1148                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1149             }
1151             if (discard)
1152                 [queue discardEventsMatchingMask:discard forWindow:self];
1153         }
1155         if (state->maximized != maximized)
1156         {
1157             maximized = state->maximized;
1158             [self adjustFeaturesForState];
1160             if (!maximized && [self inLiveResize])
1161                 [self sendResizeStartQuery];
1162         }
1164         behavior = NSWindowCollectionBehaviorDefault;
1165         if (state->excluded_by_expose)
1166             behavior |= NSWindowCollectionBehaviorTransient;
1167         else
1168             behavior |= NSWindowCollectionBehaviorManaged;
1169         if (state->excluded_by_cycle)
1170         {
1171             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1172             if ([self isOrderedIn])
1173                 [NSApp removeWindowsItem:self];
1174         }
1175         else
1176         {
1177             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1178             if ([self isOrderedIn])
1179                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1180         }
1181         [self adjustFullScreenBehavior:behavior];
1182     }
1184     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1185     {
1186         BOOL reordered = FALSE;
1188         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1189         {
1190             if ([self level] > [child level])
1191                 [child setLevel:[self level]];
1192             [self addChildWindow:child ordered:NSWindowAbove];
1193             [child checkWineDisplayLink];
1194             [latentChildWindows removeObjectIdenticalTo:child];
1195             child.latentParentWindow = nil;
1196             reordered = TRUE;
1197         }
1198         else
1199         {
1200             if (!latentChildWindows)
1201                 latentChildWindows = [[NSMutableArray alloc] init];
1202             if (![latentChildWindows containsObject:child])
1203                 [latentChildWindows addObject:child];
1204             child.latentParentWindow = self;
1205         }
1207         return reordered;
1208     }
1210     - (BOOL) addChildWineWindow:(WineWindow*)child
1211     {
1212         return [self addChildWineWindow:child assumeVisible:FALSE];
1213     }
1215     - (void) removeChildWineWindow:(WineWindow*)child
1216     {
1217         [self removeChildWindow:child];
1218         if (child.latentParentWindow == self)
1219             child.latentParentWindow = nil;
1220         [latentChildWindows removeObjectIdenticalTo:child];
1221     }
1223     - (BOOL) becameEligibleParentOrChild
1224     {
1225         BOOL reordered = FALSE;
1226         NSUInteger count;
1228         if (latentParentWindow.floating || !self.floating)
1229         {
1230             // If we aren't visible currently, we assume that we should be and soon
1231             // will be.  So, if the latent parent is visible that's enough to assume
1232             // we can establish the parent-child relationship in Cocoa.  That will
1233             // actually make us visible, which is fine.
1234             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1235                 reordered = TRUE;
1236         }
1238         // Here, though, we may not actually be visible yet and adding a child
1239         // won't make us visible.  The caller will have to call this method
1240         // again after actually making us visible.
1241         if ([self isVisible] && (count = [latentChildWindows count]))
1242         {
1243             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1244             NSUInteger i;
1246             for (i = 0; i < count; i++)
1247             {
1248                 WineWindow* child = [latentChildWindows objectAtIndex:i];
1249                 if ([child isVisible] && (self.floating || !child.floating))
1250                 {
1251                     if (child.latentParentWindow == self)
1252                     {
1253                         if ([self level] > [child level])
1254                             [child setLevel:[self level]];
1255                         [self addChildWindow:child ordered:NSWindowAbove];
1256                         child.latentParentWindow = nil;
1257                         reordered = TRUE;
1258                     }
1259                     else
1260                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1261                     [indexesToRemove addIndex:i];
1262                 }
1263             }
1265             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1266         }
1268         return reordered;
1269     }
1271     - (void) becameIneligibleChild
1272     {
1273         WineWindow* parent = (WineWindow*)[self parentWindow];
1274         if (parent)
1275         {
1276             if (!parent->latentChildWindows)
1277                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1278             [parent->latentChildWindows insertObject:self atIndex:0];
1279             self.latentParentWindow = parent;
1280             [parent removeChildWindow:self];
1281         }
1282     }
1284     - (void) becameIneligibleParentOrChild
1285     {
1286         NSArray* childWindows = [self childWineWindows];
1288         [self becameIneligibleChild];
1290         if ([childWindows count])
1291         {
1292             WineWindow* child;
1294             for (child in childWindows)
1295             {
1296                 child.latentParentWindow = self;
1297                 [self removeChildWindow:child];
1298             }
1300             if (latentChildWindows)
1301                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1302             else
1303                 latentChildWindows = [childWindows mutableCopy];
1304         }
1305     }
1307     // Determine if, among Wine windows, this window is directly above or below
1308     // a given other Wine window with no other Wine window intervening.
1309     // Intervening non-Wine windows are ignored.
1310     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1311     {
1312         NSNumber* windowNumber;
1313         NSNumber* otherWindowNumber;
1314         NSArray* windowNumbers;
1315         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1317         if (![self isVisible] || ![otherWindow isVisible])
1318             return FALSE;
1320         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1321         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1322         windowNumbers = [[self class] windowNumbersWithOptions:0];
1323         windowIndex = [windowNumbers indexOfObject:windowNumber];
1324         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1326         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1327             return FALSE;
1329         if (orderingMode == NSWindowAbove)
1330         {
1331             lowIndex = windowIndex;
1332             highIndex = otherWindowIndex;
1333         }
1334         else if (orderingMode == NSWindowBelow)
1335         {
1336             lowIndex = otherWindowIndex;
1337             highIndex = windowIndex;
1338         }
1339         else
1340             return FALSE;
1342         if (highIndex <= lowIndex)
1343             return FALSE;
1345         for (i = lowIndex + 1; i < highIndex; i++)
1346         {
1347             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1348             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1349             if ([interveningWindow isKindOfClass:[WineWindow class]])
1350                 return FALSE;
1351         }
1353         return TRUE;
1354     }
1356     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1357     {
1358         NSMutableArray* windowNumbers;
1359         NSNumber* childWindowNumber;
1360         NSUInteger otherIndex, limit;
1361         NSArray* origChildren;
1362         NSMutableArray* children;
1364         // Get the z-order from the window server and modify it to reflect the
1365         // requested window ordering.
1366         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1367         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1368         [windowNumbers removeObject:childWindowNumber];
1369         otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1370         [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1372         // Get our child windows and sort them in the reverse of the desired
1373         // z-order (back-to-front).
1374         origChildren = [self childWineWindows];
1375         children = [[origChildren mutableCopy] autorelease];
1376         [children sortWithOptions:NSSortStable
1377                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1378             NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1379             NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1380             NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1381             NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1382             if (index1 == NSNotFound)
1383             {
1384                 if (index2 == NSNotFound)
1385                     return NSOrderedSame;
1386                 else
1387                     return NSOrderedAscending;
1388             }
1389             else if (index2 == NSNotFound)
1390                 return NSOrderedDescending;
1391             else if (index1 < index2)
1392                 return NSOrderedDescending;
1393             else if (index2 < index1)
1394                 return NSOrderedAscending;
1396             return NSOrderedSame;
1397         }];
1399         // If the current and desired children arrays match up to a point, leave
1400         // those matching children alone.
1401         limit = MIN([origChildren count], [children count]);
1402         for (otherIndex = 0; otherIndex < limit; otherIndex++)
1403         {
1404             if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1405                 break;
1406         }
1407         [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1409         // Remove all of the child windows and re-add them back-to-front so they
1410         // are in the desired order.
1411         for (other in children)
1412             [self removeChildWindow:other];
1413         for (other in children)
1414             [self addChildWindow:other ordered:NSWindowAbove];
1415     }
1417     /* Returns whether or not the window was ordered in, which depends on if
1418        its frame intersects any screen. */
1419     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1420     {
1421         WineApplicationController* controller = [WineApplicationController sharedController];
1422         if (![self isMiniaturized])
1423         {
1424             BOOL needAdjustWindowLevels = FALSE;
1425             BOOL wasVisible;
1427             [controller transformProcessToForeground];
1428             [NSApp unhide:nil];
1429             wasVisible = [self isVisible];
1431             if (activate)
1432                 [NSApp activateIgnoringOtherApps:YES];
1434             NSDisableScreenUpdates();
1436             if ([self becameEligibleParentOrChild])
1437                 needAdjustWindowLevels = TRUE;
1439             if (prev || next)
1440             {
1441                 WineWindow* other = [prev isVisible] ? prev : next;
1442                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1444                 if (![self isOrdered:orderingMode relativeTo:other])
1445                 {
1446                     WineWindow* parent = (WineWindow*)[self parentWindow];
1447                     WineWindow* otherParent = (WineWindow*)[other parentWindow];
1449                     // This window level may not be right for this window based
1450                     // on floating-ness, fullscreen-ness, etc.  But we set it
1451                     // temporarily to allow us to order the windows properly.
1452                     // Then the levels get fixed by -adjustWindowLevels.
1453                     if ([self level] != [other level])
1454                         [self setLevel:[other level]];
1455                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1456                     [self checkWineDisplayLink];
1458                     // The above call to -[NSWindow orderWindow:relativeTo:] won't
1459                     // reorder windows which are both children of the same parent
1460                     // relative to each other, so do that separately.
1461                     if (parent && parent == otherParent)
1462                         [parent order:orderingMode childWindow:self relativeTo:other];
1464                     needAdjustWindowLevels = TRUE;
1465                 }
1466             }
1467             else
1468             {
1469                 // Again, temporarily set level to make sure we can order to
1470                 // the right place.
1471                 next = [controller frontWineWindow];
1472                 if (next && [self level] < [next level])
1473                     [self setLevel:[next level]];
1474                 [self orderFront:nil];
1475                 [self checkWineDisplayLink];
1476                 needAdjustWindowLevels = TRUE;
1477             }
1479             if ([self becameEligibleParentOrChild])
1480                 needAdjustWindowLevels = TRUE;
1482             if (needAdjustWindowLevels)
1483             {
1484                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1485                     [controller updateFullscreenWindows];
1486                 [controller adjustWindowLevels];
1487             }
1489             if (pendingMinimize)
1490             {
1491                 [super miniaturize:nil];
1492                 pendingMinimize = FALSE;
1493             }
1495             NSEnableScreenUpdates();
1497             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1498                Generate a frame-changed event just in case.  The back end will ignore
1499                it if nothing actually changed. */
1500             [self windowDidResize:nil];
1502             if (![self isExcludedFromWindowsMenu])
1503                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1504         }
1505     }
1507     - (void) doOrderOut
1508     {
1509         WineApplicationController* controller = [WineApplicationController sharedController];
1510         BOOL wasVisible = [self isVisible];
1511         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1513         if ([self isMiniaturized])
1514             pendingMinimize = TRUE;
1516         WineWindow* parent = (WineWindow*)self.parentWindow;
1517         if ([parent isKindOfClass:[WineWindow class]])
1518             [parent grabDockIconSnapshotFromWindow:self force:NO];
1520         [self becameIneligibleParentOrChild];
1521         if ([self isMiniaturized])
1522         {
1523             fakingClose = TRUE;
1524             [self close];
1525             fakingClose = FALSE;
1526         }
1527         else
1528             [self orderOut:nil];
1529         [self checkWineDisplayLink];
1530         savedVisibleState = FALSE;
1531         if (wasVisible && wasOnActiveSpace && fullscreen)
1532             [controller updateFullscreenWindows];
1533         [controller adjustWindowLevels];
1534         [NSApp removeWindowsItem:self];
1536         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1537                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1538                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1539                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1540                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1541                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1542                                forWindow:self];
1543     }
1545     - (void) updateFullscreen
1546     {
1547         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1548         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1550         if (nowFullscreen != fullscreen)
1551         {
1552             WineApplicationController* controller = [WineApplicationController sharedController];
1554             fullscreen = nowFullscreen;
1555             if ([self isVisible] && [self isOnActiveSpace])
1556                 [controller updateFullscreenWindows];
1558             [controller adjustWindowLevels];
1559         }
1560     }
1562     - (void) setFrameAndWineFrame:(NSRect)frame
1563     {
1564         [self setFrame:frame display:YES];
1566         wineFrame = frame;
1567         roundedWineFrame = self.frame;
1568         CGFloat junk;
1569 #if CGFLOAT_IS_DOUBLE
1570         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1571              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1572             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1573             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1574             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1575             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1576             roundedWineFrame = wineFrame;
1577 #else
1578         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1579              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1580             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1581             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1582             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1583             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1584             roundedWineFrame = wineFrame;
1585 #endif
1586     }
1588     - (void) setFrameFromWine:(NSRect)contentRect
1589     {
1590         /* Origin is (left, top) in a top-down space.  Need to convert it to
1591            (left, bottom) in a bottom-up space. */
1592         [[WineApplicationController sharedController] flipRect:&contentRect];
1594         /* The back end is establishing a new window size and position.  It's
1595            not interested in any stale events regarding those that may be sitting
1596            in the queue. */
1597         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1598                                forWindow:self];
1600         if (!NSIsEmptyRect(contentRect))
1601         {
1602             NSRect frame, oldFrame;
1604             oldFrame = self.wine_fractionalFrame;
1605             frame = [self frameRectForContentRect:contentRect];
1606             if (!NSEqualRects(frame, oldFrame))
1607             {
1608                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1609                 BOOL needEnableScreenUpdates = FALSE;
1611                 if ([self preventResizing])
1612                 {
1613                     // Allow the following calls to -setFrame:display: to work even
1614                     // if they would violate the content size constraints. This
1615                     // shouldn't be necessary since the content size constraints are
1616                     // documented to not constrain that method, but it seems to be.
1617                     [self setContentMinSize:NSZeroSize];
1618                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1619                 }
1621                 if (equalSizes && [[self childWineWindows] count])
1622                 {
1623                     // If we change the window frame such that the origin moves
1624                     // but the size doesn't change, then Cocoa moves child
1625                     // windows with the parent.  We don't want that so we fake
1626                     // a change of the size and then change it back.
1627                     NSRect bogusFrame = frame;
1628                     bogusFrame.size.width++;
1630                     NSDisableScreenUpdates();
1631                     needEnableScreenUpdates = TRUE;
1633                     ignore_windowResize = TRUE;
1634                     [self setFrame:bogusFrame display:NO];
1635                     ignore_windowResize = FALSE;
1636                 }
1638                 [self setFrameAndWineFrame:frame];
1639                 if ([self preventResizing])
1640                 {
1641                     [self setContentMinSize:contentRect.size];
1642                     [self setContentMaxSize:contentRect.size];
1643                 }
1645                 if (needEnableScreenUpdates)
1646                     NSEnableScreenUpdates();
1648                 if (!equalSizes)
1649                     [self updateColorSpace];
1651                 if (!enteringFullScreen &&
1652                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1653                     nonFullscreenFrame = frame;
1655                 [self updateFullscreen];
1657                 if ([self isOrderedIn])
1658                 {
1659                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1660                        event.  The back end will ignore it if nothing actually changed. */
1661                     [self windowDidResize:nil];
1662                 }
1663             }
1664         }
1665     }
1667     - (NSRect) wine_fractionalFrame
1668     {
1669         NSRect frame = self.frame;
1670         if (NSEqualRects(frame, roundedWineFrame))
1671             frame = wineFrame;
1672         return frame;
1673     }
1675     - (void) setMacDrvParentWindow:(WineWindow*)parent
1676     {
1677         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1678         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1679         {
1680             [oldParent removeChildWineWindow:self];
1681             [latentParentWindow removeChildWineWindow:self];
1682             if ([parent addChildWineWindow:self])
1683                 [[WineApplicationController sharedController] adjustWindowLevels];
1684         }
1685     }
1687     - (void) setDisabled:(BOOL)newValue
1688     {
1689         if (disabled != newValue)
1690         {
1691             disabled = newValue;
1692             [self adjustFeaturesForState];
1693         }
1694     }
1696     - (BOOL) needsTransparency
1697     {
1698         return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1699                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1700     }
1702     - (void) checkTransparency
1703     {
1704         if (![self isOpaque] && !self.needsTransparency)
1705         {
1706             self.shapeChangedSinceLastDraw = TRUE;
1707             [[self contentView] setNeedsDisplay:YES];
1708             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1709             [self setOpaque:YES];
1710         }
1711         else if ([self isOpaque] && self.needsTransparency)
1712         {
1713             self.shapeChangedSinceLastDraw = TRUE;
1714             [[self contentView] setNeedsDisplay:YES];
1715             [self setBackgroundColor:[NSColor clearColor]];
1716             [self setOpaque:NO];
1717         }
1718     }
1720     - (void) setShape:(NSBezierPath*)newShape
1721     {
1722         if (shape == newShape) return;
1724         if (shape)
1725         {
1726             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1727             [shape release];
1728         }
1729         if (newShape)
1730             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1732         shape = [newShape copy];
1733         self.shapeChangedSinceLastDraw = TRUE;
1735         [self checkTransparency];
1736     }
1738     - (void) makeFocused:(BOOL)activate
1739     {
1740         if (activate)
1741         {
1742             [[WineApplicationController sharedController] transformProcessToForeground];
1743             [NSApp activateIgnoringOtherApps:YES];
1744         }
1746         causing_becomeKeyWindow = self;
1747         [self makeKeyWindow];
1748         causing_becomeKeyWindow = nil;
1750         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1751                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1752                                forWindow:self];
1753     }
1755     - (void) postKey:(uint16_t)keyCode
1756              pressed:(BOOL)pressed
1757            modifiers:(NSUInteger)modifiers
1758                event:(NSEvent*)theEvent
1759     {
1760         macdrv_event* event;
1761         CGEventRef cgevent;
1762         WineApplicationController* controller = [WineApplicationController sharedController];
1764         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1765         event->key.keycode   = keyCode;
1766         event->key.modifiers = modifiers;
1767         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1769         if ((cgevent = [theEvent CGEvent]))
1770         {
1771             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1772                                                         kCGKeyboardEventKeyboardType);
1773             if (keyboardType != controller.keyboardType)
1774             {
1775                 controller.keyboardType = keyboardType;
1776                 [controller keyboardSelectionDidChange];
1777             }
1778         }
1780         [queue postEvent:event];
1782         macdrv_release_event(event);
1784         [controller noteKey:keyCode pressed:pressed];
1785     }
1787     - (void) postKeyEvent:(NSEvent *)theEvent
1788     {
1789         [self flagsChanged:theEvent];
1790         [self postKey:[theEvent keyCode]
1791               pressed:[theEvent type] == NSKeyDown
1792             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1793                 event:theEvent];
1794     }
1796     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1797     {
1798         savedContentMinSize = minSize;
1799         savedContentMaxSize = maxSize;
1800         if (![self preventResizing])
1801         {
1802             [self setContentMinSize:minSize];
1803             [self setContentMaxSize:maxSize];
1804         }
1805     }
1807     - (WineWindow*) ancestorWineWindow
1808     {
1809         WineWindow* ancestor = self;
1810         for (;;)
1811         {
1812             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1813             if ([parent isKindOfClass:[WineWindow class]])
1814                 ancestor = parent;
1815             else
1816                 break;
1817         }
1818         return ancestor;
1819     }
1821     - (void) postBroughtForwardEvent
1822     {
1823         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1824         [queue postEvent:event];
1825         macdrv_release_event(event);
1826     }
1828     - (void) updateForCursorClipping
1829     {
1830         [self adjustFeaturesForState];
1831     }
1833     - (void) endWindowDragging
1834     {
1835         if (draggingPhase)
1836         {
1837             if (draggingPhase == 3)
1838             {
1839                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
1840                 [queue postEvent:event];
1841                 macdrv_release_event(event);
1842             }
1844             draggingPhase = 0;
1845             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
1846         }
1847     }
1849     - (NSMutableDictionary*) displayIDToDisplayLinkMap
1850     {
1851         static NSMutableDictionary* displayIDToDisplayLinkMap;
1852         if (!displayIDToDisplayLinkMap)
1853         {
1854             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
1856             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
1857                                                               object:NSApp
1858                                                                queue:nil
1859                                                           usingBlock:^(NSNotification *note){
1860                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
1861                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
1862                 [badDisplayIDs minusSet:validDisplayIDs];
1863                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
1864             }];
1865         }
1866         return displayIDToDisplayLinkMap;
1867     }
1869     - (WineDisplayLink*) wineDisplayLink
1870     {
1871         if (!_lastDisplayID)
1872             return nil;
1874         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
1875         return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
1876     }
1878     - (void) checkWineDisplayLink
1879     {
1880         NSScreen* screen = self.screen;
1881         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
1882             screen = nil;
1883 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
1884         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
1885             screen = nil;
1886 #endif
1888         NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
1889         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
1890         if (displayID == _lastDisplayID)
1891             return;
1893         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
1895         if (_lastDisplayID)
1896         {
1897             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
1898             [link removeWindow:self];
1899         }
1900         if (displayID)
1901         {
1902             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
1903             if (!link)
1904             {
1905                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
1906                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
1907             }
1908             [link addWindow:self];
1909             [self displayIfNeeded];
1910         }
1911         _lastDisplayID = displayID;
1912     }
1914     - (BOOL) isEmptyShaped
1915     {
1916         return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
1917     }
1919     - (BOOL) canProvideSnapshot
1920     {
1921         return (self.windowNumber > 0 && ![self isEmptyShaped]);
1922     }
1924     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
1925     {
1926         if (![self isEmptyShaped])
1927             return;
1929         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
1930         if (!force && now < lastDockIconSnapshot + 1)
1931             return;
1933         if (window)
1934         {
1935             if (![window canProvideSnapshot])
1936                 return;
1937         }
1938         else
1939         {
1940             CGFloat bestArea;
1941             for (WineWindow* childWindow in self.childWindows)
1942             {
1943                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
1944                     continue;
1946                 NSSize size = childWindow.frame.size;
1947                 CGFloat area = size.width * size.height;
1948                 if (!window || area > bestArea)
1949                 {
1950                     window = childWindow;
1951                     bestArea = area;
1952                 }
1953             }
1955             if (!window)
1956                 return;
1957         }
1959         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
1960         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
1961         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
1962         CFRelease(windowIDs);
1963         if (!windowImage)
1964             return;
1966         NSImage* appImage = [NSApp applicationIconImage];
1967         if (!appImage)
1968             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
1970         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
1971         [dockIcon lockFocus];
1973         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
1975         CGRect rect = CGRectMake(8, 8, 240, 240);
1976         size_t width = CGImageGetWidth(windowImage);
1977         size_t height = CGImageGetHeight(windowImage);
1978         if (width > height)
1979         {
1980             rect.size.height *= height / (double)width;
1981             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
1982         }
1983         else if (width != height)
1984         {
1985             rect.size.width *= width / (double)height;
1986             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
1987         }
1989         CGContextDrawImage(cgcontext, rect, windowImage);
1990         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
1991                     fromRect:NSZeroRect
1992                    operation:NSCompositeSourceOver
1993                     fraction:1
1994               respectFlipped:YES
1995                        hints:nil];
1997         [dockIcon unlockFocus];
1999         CGImageRelease(windowImage);
2001         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2002         if (![imageView isKindOfClass:[NSImageView class]])
2003         {
2004             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2005             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2006             self.dockTile.contentView = imageView;
2007         }
2008         imageView.image = dockIcon;
2009         [self.dockTile display];
2010         lastDockIconSnapshot = now;
2011     }
2013     - (void) checkEmptyShaped
2014     {
2015         if (self.dockTile.contentView && ![self isEmptyShaped])
2016         {
2017             self.dockTile.contentView = nil;
2018             lastDockIconSnapshot = 0;
2019         }
2020         [self checkWineDisplayLink];
2021     }
2024     /*
2025      * ---------- NSWindow method overrides ----------
2026      */
2027     - (BOOL) canBecomeKeyWindow
2028     {
2029         if (causing_becomeKeyWindow == self) return YES;
2030         if (self.disabled || self.noActivate) return NO;
2031         return [self isKeyWindow];
2032     }
2034     - (BOOL) canBecomeMainWindow
2035     {
2036         return [self canBecomeKeyWindow];
2037     }
2039     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2040     {
2041         // If a window is sized to completely cover a screen, then it's in
2042         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2043         // it.
2044         NSArray* screens = [NSScreen screens];
2045         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2046         if (!screen_covered_by_rect(contentRect, screens) &&
2047             frame_intersects_screens(frameRect, screens))
2048             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2049         return frameRect;
2050     }
2052     // This private method of NSWindow is called as Cocoa reacts to the display
2053     // configuration changing.  Among other things, it adjusts the window's
2054     // frame based on how the screen(s) changed size.  That tells Wine that the
2055     // window has been moved.  We don't want that.  Rather, we want to make
2056     // sure that the WinAPI notion of the window position is maintained/
2057     // restored, possibly undoing or overriding Cocoa's adjustment.
2058     //
2059     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2060     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2061     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2062     // reassert its notion of the position.  That call won't get processed
2063     // until after this method returns, so it will override whatever this
2064     // method does to the window position.  It will also discard any pending
2065     // WINDOW_FRAME_CHANGED events.
2066     //
2067     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2068     // adjust the window's position due to a display change is to hook into
2069     // this private method.  This private method has remained stable from 10.6
2070     // through 10.11.  If it does change, the most likely thing is that it
2071     // will be removed and no longer called and this fix will simply stop
2072     // working.  The only real danger would be if Apple changed the return type
2073     // to a struct or floating-point type, which would change the calling
2074     // convention.
2075     - (id) _displayChanged
2076     {
2077         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2078         [queue postEvent:event];
2079         macdrv_release_event(event);
2081         return [super _displayChanged];
2082     }
2084     - (BOOL) isExcludedFromWindowsMenu
2085     {
2086         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2087     }
2089     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2090     {
2091         BOOL ret = [super validateMenuItem:menuItem];
2093         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2094             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2095         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2096             ret = NO;
2098         return ret;
2099     }
2101     /* We don't call this.  It's the action method of the items in the Window menu. */
2102     - (void) makeKeyAndOrderFront:(id)sender
2103     {
2104         if ([self isMiniaturized])
2105             [self deminiaturize:nil];
2106         [self orderBelow:nil orAbove:nil activate:NO];
2107         [[self ancestorWineWindow] postBroughtForwardEvent];
2109         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2110             [[WineApplicationController sharedController] windowGotFocus:self];
2111     }
2113     - (void) sendEvent:(NSEvent*)event
2114     {
2115         NSEventType type = event.type;
2117         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2118            interface control.  For example, Control-Tab switches focus among
2119            views.  We want to bypass that feature, so directly route key-down
2120            events to -keyDown:. */
2121         if (type == NSKeyDown)
2122             [[self firstResponder] keyDown:event];
2123         else
2124         {
2125             if (!draggingPhase && maximized && ![self isMovable] &&
2126                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2127                 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2128             {
2129                 NSRect titleBar = self.frame;
2130                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2131                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2132                 titleBar.origin.y = NSMaxY(contentRect);
2134                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2136                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2137                 {
2138                     static const NSWindowButton buttons[] = {
2139                         NSWindowCloseButton,
2140                         NSWindowMiniaturizeButton,
2141                         NSWindowZoomButton,
2142                         NSWindowFullScreenButton,
2143                     };
2144                     BOOL hitButton = NO;
2145                     int i;
2147                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2148                     {
2149                         NSButton* button;
2151                         if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2152                             continue;
2154                         button = [self standardWindowButton:buttons[i]];
2155                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2156                         {
2157                             hitButton = YES;
2158                             break;
2159                         }
2160                     }
2162                     if (!hitButton)
2163                     {
2164                         draggingPhase = 1;
2165                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2166                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2167                     }
2168                 }
2169             }
2170             else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2171             {
2172                 if ([self isMovable])
2173                 {
2174                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2175                     NSPoint newTopLeft = dragWindowStartPosition;
2177                     newTopLeft.x += point.x - dragStartPosition.x;
2178                     newTopLeft.y += point.y - dragStartPosition.y;
2180                     if (draggingPhase == 2)
2181                     {
2182                         macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2183                         [queue postEvent:event];
2184                         macdrv_release_event(event);
2186                         draggingPhase = 3;
2187                     }
2189                     [self setFrameTopLeftPoint:newTopLeft];
2190                 }
2191                 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2192                 {
2193                     macdrv_event* event;
2194                     NSRect frame = [self contentRectForFrameRect:self.frame];
2196                     [[WineApplicationController sharedController] flipRect:&frame];
2198                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2199                     event->window_restore_requested.keep_frame = TRUE;
2200                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2201                     [queue postEvent:event];
2202                     macdrv_release_event(event);
2204                     draggingPhase = 2;
2205                 }
2207                 if (type == NSLeftMouseUp)
2208                     [self endWindowDragging];
2209             }
2211             [super sendEvent:event];
2212         }
2213     }
2215     - (void) miniaturize:(id)sender
2216     {
2217         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2218         [queue postEvent:event];
2219         macdrv_release_event(event);
2221         WineWindow* parent = (WineWindow*)self.parentWindow;
2222         if ([parent isKindOfClass:[WineWindow class]])
2223             [parent grabDockIconSnapshotFromWindow:self force:YES];
2224     }
2226     - (void) toggleFullScreen:(id)sender
2227     {
2228         if (!self.disabled && !maximized)
2229             [super toggleFullScreen:sender];
2230     }
2232     - (void) setViewsNeedDisplay:(BOOL)value
2233     {
2234         if (value && ![self viewsNeedDisplay])
2235         {
2236             WineDisplayLink* link = [self wineDisplayLink];
2237             if (link)
2238             {
2239                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2240                 if (_lastDisplayTime + [link refreshPeriod] < now)
2241                     [self setAutodisplay:YES];
2242                 else
2243                 {
2244                     [link start];
2245                     _lastDisplayTime = now;
2246                 }
2247             }
2248         }
2249         [super setViewsNeedDisplay:value];
2250     }
2252     - (void) display
2253     {
2254         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2255         [super display];
2256         [self setAutodisplay:NO];
2257     }
2259     - (void) displayIfNeeded
2260     {
2261         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2262         [super displayIfNeeded];
2263         [self setAutodisplay:NO];
2264     }
2266     - (NSArray*) childWineWindows
2267     {
2268         NSArray* childWindows = self.childWindows;
2269         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2270             return [child isKindOfClass:[WineWindow class]];
2271         }];
2272         return [childWindows objectsAtIndexes:indexes];
2273     }
2275     // We normally use the generic/calibrated RGB color space for the window,
2276     // rather than the device color space, to avoid expensive color conversion
2277     // which slows down drawing.  However, for windows displaying OpenGL, having
2278     // a different color space than the screen greatly reduces frame rates, often
2279     // limiting it to the display refresh rate.
2280     //
2281     // To avoid this, we switch back to the screen color space whenever the
2282     // window is covered by a view with an attached OpenGL context.
2283     - (void) updateColorSpace
2284     {
2285         NSRect contentRect = [[self contentView] frame];
2286         BOOL coveredByGLView = FALSE;
2287         WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2288         if ([view isKindOfClass:[WineContentView class]] && [view hasGLContext])
2289         {
2290             NSRect frame = [view convertRect:[view bounds] toView:nil];
2291             if (NSContainsRect(frame, contentRect))
2292                 coveredByGLView = TRUE;
2293         }
2295         if (coveredByGLView)
2296             [self setColorSpace:nil];
2297         else
2298             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2299     }
2301     - (void) updateForGLSubviews
2302     {
2303         [self updateColorSpace];
2304         if (gl_surface_mode == GL_SURFACE_BEHIND)
2305             [self checkTransparency];
2306     }
2308     - (void) setRetinaMode:(int)mode
2309     {
2310         NSRect frame;
2311         double scale = mode ? 0.5 : 2.0;
2312         NSAffineTransform* transform = [NSAffineTransform transform];
2314         [transform scaleBy:scale];
2316         if (shape)
2317             [shape transformUsingAffineTransform:transform];
2319         for (WineContentView* subview in [self.contentView subviews])
2320         {
2321             if ([subview isKindOfClass:[WineContentView class]])
2322                 [subview setRetinaMode:mode];
2323         }
2325         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2326         frame.origin.x *= scale;
2327         frame.origin.y *= scale;
2328         frame.size.width *= scale;
2329         frame.size.height *= scale;
2330         frame = [self frameRectForContentRect:frame];
2332         savedContentMinSize = [transform transformSize:savedContentMinSize];
2333         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2334             savedContentMaxSize.width *= scale;
2335         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2336             savedContentMaxSize.height *= scale;
2338         self.contentMinSize = [transform transformSize:self.contentMinSize];
2339         NSSize temp = self.contentMaxSize;
2340         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2341             temp.width *= scale;
2342         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2343             temp.height *= scale;
2344         self.contentMaxSize = temp;
2346         ignore_windowResize = TRUE;
2347         [self setFrameAndWineFrame:frame];
2348         ignore_windowResize = FALSE;
2349     }
2352     /*
2353      * ---------- NSResponder method overrides ----------
2354      */
2355     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
2357     - (void) flagsChanged:(NSEvent *)theEvent
2358     {
2359         static const struct {
2360             NSUInteger  mask;
2361             uint16_t    keycode;
2362         } modifiers[] = {
2363             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2364             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2365             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2366             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2367             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2368             { NX_DEVICELALTKEYMASK,     kVK_Option },
2369             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2370             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2371             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2372         };
2374         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2375         NSUInteger changed;
2376         int i, last_changed;
2378         fix_device_modifiers_by_generic(&modifierFlags);
2379         changed = modifierFlags ^ lastModifierFlags;
2381         last_changed = -1;
2382         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2383             if (changed & modifiers[i].mask)
2384                 last_changed = i;
2386         for (i = 0; i <= last_changed; i++)
2387         {
2388             if (changed & modifiers[i].mask)
2389             {
2390                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2392                 if (i == last_changed)
2393                     lastModifierFlags = modifierFlags;
2394                 else
2395                 {
2396                     lastModifierFlags ^= modifiers[i].mask;
2397                     fix_generic_modifiers_by_device(&lastModifierFlags);
2398                 }
2400                 // Caps lock generates one event for each press-release action.
2401                 // We need to simulate a pair of events for each actual event.
2402                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2403                 {
2404                     [self postKey:modifiers[i].keycode
2405                           pressed:TRUE
2406                         modifiers:lastModifierFlags
2407                             event:(NSEvent*)theEvent];
2408                     pressed = FALSE;
2409                 }
2411                 [self postKey:modifiers[i].keycode
2412                       pressed:pressed
2413                     modifiers:lastModifierFlags
2414                         event:(NSEvent*)theEvent];
2415             }
2416         }
2417     }
2419     - (void) applicationWillHide
2420     {
2421         savedVisibleState = [self isVisible];
2422     }
2424     - (void) applicationDidUnhide
2425     {
2426         if ([self isVisible])
2427             [self becameEligibleParentOrChild];
2428     }
2431     /*
2432      * ---------- NSWindowDelegate methods ----------
2433      */
2434     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2435     {
2436         macdrv_query* query;
2437         NSSize size;
2439         query = macdrv_create_query();
2440         query->type = QUERY_MIN_MAX_INFO;
2441         query->window = (macdrv_window)[self retain];
2442         [self.queue query:query timeout:0.5];
2443         macdrv_release_query(query);
2445         size = [self contentMaxSize];
2446         if (proposedSize.width < size.width)
2447             size.width = proposedSize.width;
2448         if (proposedSize.height < size.height)
2449             size.height = proposedSize.height;
2450         return size;
2451     }
2453     - (void)windowDidBecomeKey:(NSNotification *)notification
2454     {
2455         WineApplicationController* controller = [WineApplicationController sharedController];
2456         NSEvent* event = [controller lastFlagsChanged];
2457         if (event)
2458             [self flagsChanged:event];
2460         if (causing_becomeKeyWindow == self) return;
2462         [controller windowGotFocus:self];
2463     }
2465     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2466     {
2467         [self checkWineDisplayLink];
2468     }
2470     - (void) windowDidChangeScreen:(NSNotification*)notification
2471     {
2472         [self checkWineDisplayLink];
2473     }
2475     - (void)windowDidDeminiaturize:(NSNotification *)notification
2476     {
2477         WineApplicationController* controller = [WineApplicationController sharedController];
2479         if (!ignore_windowDeminiaturize)
2480             [self postDidUnminimizeEvent];
2481         ignore_windowDeminiaturize = FALSE;
2483         [self becameEligibleParentOrChild];
2485         if (fullscreen && [self isOnActiveSpace])
2486             [controller updateFullscreenWindows];
2487         [controller adjustWindowLevels];
2489         if (![self parentWindow])
2490             [self postBroughtForwardEvent];
2492         if (!self.disabled && !self.noActivate)
2493         {
2494             causing_becomeKeyWindow = self;
2495             [self makeKeyWindow];
2496             causing_becomeKeyWindow = nil;
2497             [controller windowGotFocus:self];
2498         }
2500         [self windowDidResize:notification];
2501         [self checkWineDisplayLink];
2502     }
2504     - (void) windowDidEndLiveResize:(NSNotification *)notification
2505     {
2506         if (!maximized)
2507         {
2508             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2509             [queue postEvent:event];
2510             macdrv_release_event(event);
2511         }
2512     }
2514     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2515     {
2516         enteringFullScreen = FALSE;
2517         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2518     }
2520     - (void) windowDidExitFullScreen:(NSNotification*)notification
2521     {
2522         exitingFullScreen = FALSE;
2523         [self setFrameAndWineFrame:nonFullscreenFrame];
2524         [self windowDidResize:nil];
2525     }
2527     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2528     {
2529         enteringFullScreen = FALSE;
2530         enteredFullScreenTime = 0;
2531     }
2533     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2534     {
2535         exitingFullScreen = FALSE;
2536         [self windowDidResize:nil];
2537     }
2539     - (void)windowDidMiniaturize:(NSNotification *)notification
2540     {
2541         if (fullscreen && [self isOnActiveSpace])
2542             [[WineApplicationController sharedController] updateFullscreenWindows];
2543         [self checkWineDisplayLink];
2544     }
2546     - (void)windowDidMove:(NSNotification *)notification
2547     {
2548         [self windowDidResize:notification];
2549     }
2551     - (void)windowDidResignKey:(NSNotification *)notification
2552     {
2553         macdrv_event* event;
2555         if (causing_becomeKeyWindow) return;
2557         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2558         [queue postEvent:event];
2559         macdrv_release_event(event);
2560     }
2562     - (void)windowDidResize:(NSNotification *)notification
2563     {
2564         macdrv_event* event;
2565         NSRect frame = self.wine_fractionalFrame;
2567         if ([self inLiveResize])
2568         {
2569             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2570                 resizingFromLeft = TRUE;
2571             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2572                 resizingFromTop = TRUE;
2573         }
2575         frame = [self contentRectForFrameRect:frame];
2577         if (ignore_windowResize || exitingFullScreen) return;
2579         if ([self preventResizing])
2580         {
2581             [self setContentMinSize:frame.size];
2582             [self setContentMaxSize:frame.size];
2583         }
2585         [[WineApplicationController sharedController] flipRect:&frame];
2587         /* Coalesce events by discarding any previous ones still in the queue. */
2588         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2589                                forWindow:self];
2591         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2592         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2593         event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
2594         event->window_frame_changed.in_resize = [self inLiveResize];
2595         [queue postEvent:event];
2596         macdrv_release_event(event);
2598         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2599         [self updateFullscreen];
2600     }
2602     - (BOOL)windowShouldClose:(id)sender
2603     {
2604         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2605         [queue postEvent:event];
2606         macdrv_release_event(event);
2607         return NO;
2608     }
2610     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2611     {
2612         if (maximized)
2613         {
2614             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2615             [queue postEvent:event];
2616             macdrv_release_event(event);
2617             return NO;
2618         }
2619         else if (!resizable)
2620         {
2621             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2622             [queue postEvent:event];
2623             macdrv_release_event(event);
2624             return NO;
2625         }
2627         return YES;
2628     }
2630     - (void) windowWillClose:(NSNotification*)notification
2631     {
2632         WineWindow* child;
2634         if (fakingClose) return;
2635         if (latentParentWindow)
2636         {
2637             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2638             self.latentParentWindow = nil;
2639         }
2641         for (child in latentChildWindows)
2642         {
2643             if (child.latentParentWindow == self)
2644                 child.latentParentWindow = nil;
2645         }
2646         [latentChildWindows removeAllObjects];
2647     }
2649     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2650     {
2651         enteringFullScreen = TRUE;
2652         nonFullscreenFrame = self.wine_fractionalFrame;
2653     }
2655     - (void) windowWillExitFullScreen:(NSNotification*)notification
2656     {
2657         exitingFullScreen = TRUE;
2658     }
2660     - (void)windowWillMiniaturize:(NSNotification *)notification
2661     {
2662         [self becameIneligibleParentOrChild];
2663         [self grabDockIconSnapshotFromWindow:nil force:NO];
2664     }
2666     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2667     {
2668         if ([self inLiveResize])
2669         {
2670             if (maximized)
2671                 return self.wine_fractionalFrame.size;
2673             NSRect rect;
2674             macdrv_query* query;
2676             rect = [self frame];
2677             if (resizingFromLeft)
2678                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2679             if (!resizingFromTop)
2680                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2681             rect.size = frameSize;
2682             rect = [self contentRectForFrameRect:rect];
2683             [[WineApplicationController sharedController] flipRect:&rect];
2685             query = macdrv_create_query();
2686             query->type = QUERY_RESIZE_SIZE;
2687             query->window = (macdrv_window)[self retain];
2688             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2689             query->resize_size.from_left = resizingFromLeft;
2690             query->resize_size.from_top = resizingFromTop;
2692             if ([self.queue query:query timeout:0.1])
2693             {
2694                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2695                 rect = [self frameRectForContentRect:rect];
2696                 frameSize = rect.size;
2697             }
2699             macdrv_release_query(query);
2700         }
2702         return frameSize;
2703     }
2705     - (void) windowWillStartLiveResize:(NSNotification *)notification
2706     {
2707         [self endWindowDragging];
2709         if (maximized)
2710         {
2711             macdrv_event* event;
2712             NSRect frame = [self contentRectForFrameRect:self.frame];
2714             [[WineApplicationController sharedController] flipRect:&frame];
2716             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2717             event->window_restore_requested.keep_frame = TRUE;
2718             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2719             [queue postEvent:event];
2720             macdrv_release_event(event);
2721         }
2722         else
2723             [self sendResizeStartQuery];
2725         frameAtResizeStart = [self frame];
2726         resizingFromLeft = resizingFromTop = FALSE;
2727     }
2729     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2730     {
2731         macdrv_query* query;
2732         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2733         NSSize maxSize;
2735         query = macdrv_create_query();
2736         query->type = QUERY_MIN_MAX_INFO;
2737         query->window = (macdrv_window)[self retain];
2738         [self.queue query:query timeout:0.5];
2739         macdrv_release_query(query);
2741         currentContentRect = [self contentRectForFrameRect:[self frame]];
2742         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2744         maxSize = [self contentMaxSize];
2745         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2746         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2748         // Try to keep the top-left corner where it is.
2749         newContentRect.origin.x = NSMinX(currentContentRect);
2750         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2752         // If that pushes the bottom or right off the screen, pull it up and to the left.
2753         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2754         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2755             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2756         if (NSMinY(newContentRect) < NSMinY(screenRect))
2757             newContentRect.origin.y = NSMinY(screenRect);
2759         // If that pushes the top or left off the screen, push it down and the right
2760         // again.  Do this last because the top-left corner is more important than the
2761         // bottom-right.
2762         if (NSMinX(newContentRect) < NSMinX(screenRect))
2763             newContentRect.origin.x = NSMinX(screenRect);
2764         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2765             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2767         return [self frameRectForContentRect:newContentRect];
2768     }
2771     /*
2772      * ---------- NSPasteboardOwner methods ----------
2773      */
2774     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2775     {
2776         macdrv_query* query = macdrv_create_query();
2777         query->type = QUERY_PASTEBOARD_DATA;
2778         query->window = (macdrv_window)[self retain];
2779         query->pasteboard_data.type = (CFStringRef)[type copy];
2781         [self.queue query:query timeout:3];
2782         macdrv_release_query(query);
2783     }
2786     /*
2787      * ---------- NSDraggingDestination methods ----------
2788      */
2789     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2790     {
2791         return [self draggingUpdated:sender];
2792     }
2794     - (void) draggingExited:(id <NSDraggingInfo>)sender
2795     {
2796         // This isn't really a query.  We don't need any response.  However, it
2797         // has to be processed in a similar manner as the other drag-and-drop
2798         // queries in order to maintain the proper order of operations.
2799         macdrv_query* query = macdrv_create_query();
2800         query->type = QUERY_DRAG_EXITED;
2801         query->window = (macdrv_window)[self retain];
2803         [self.queue query:query timeout:0.1];
2804         macdrv_release_query(query);
2805     }
2807     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2808     {
2809         NSDragOperation ret;
2810         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2811         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
2812         NSPasteboard* pb = [sender draggingPasteboard];
2814         macdrv_query* query = macdrv_create_query();
2815         query->type = QUERY_DRAG_OPERATION;
2816         query->window = (macdrv_window)[self retain];
2817         query->drag_operation.x = floor(cgpt.x);
2818         query->drag_operation.y = floor(cgpt.y);
2819         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2820         query->drag_operation.accepted_op = NSDragOperationNone;
2821         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2823         [self.queue query:query timeout:3];
2824         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2825         macdrv_release_query(query);
2827         return ret;
2828     }
2830     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2831     {
2832         BOOL ret;
2833         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2834         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
2835         NSPasteboard* pb = [sender draggingPasteboard];
2837         macdrv_query* query = macdrv_create_query();
2838         query->type = QUERY_DRAG_DROP;
2839         query->window = (macdrv_window)[self retain];
2840         query->drag_drop.x = floor(cgpt.x);
2841         query->drag_drop.y = floor(cgpt.y);
2842         query->drag_drop.op = [sender draggingSourceOperationMask];
2843         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2845         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
2846         ret = query->status;
2847         macdrv_release_query(query);
2849         return ret;
2850     }
2852     - (BOOL) wantsPeriodicDraggingUpdates
2853     {
2854         return NO;
2855     }
2857 @end
2860 /***********************************************************************
2861  *              macdrv_create_cocoa_window
2863  * Create a Cocoa window with the given content frame and features (e.g.
2864  * title bar, close box, etc.).
2865  */
2866 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2867         CGRect frame, void* hwnd, macdrv_event_queue queue)
2869     __block WineWindow* window;
2871     OnMainThread(^{
2872         window = [[WineWindow createWindowWithFeatures:wf
2873                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
2874                                                   hwnd:hwnd
2875                                                  queue:(WineEventQueue*)queue] retain];
2876     });
2878     return (macdrv_window)window;
2881 /***********************************************************************
2882  *              macdrv_destroy_cocoa_window
2884  * Destroy a Cocoa window.
2885  */
2886 void macdrv_destroy_cocoa_window(macdrv_window w)
2888     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2889     WineWindow* window = (WineWindow*)w;
2891     OnMainThread(^{
2892         [window doOrderOut];
2893         [window close];
2894     });
2895     [window.queue discardEventsMatchingMask:-1 forWindow:window];
2896     [window release];
2898     [pool release];
2901 /***********************************************************************
2902  *              macdrv_get_window_hwnd
2904  * Get the hwnd that was set for the window at creation.
2905  */
2906 void* macdrv_get_window_hwnd(macdrv_window w)
2908     WineWindow* window = (WineWindow*)w;
2909     return window.hwnd;
2912 /***********************************************************************
2913  *              macdrv_set_cocoa_window_features
2915  * Update a Cocoa window's features.
2916  */
2917 void macdrv_set_cocoa_window_features(macdrv_window w,
2918         const struct macdrv_window_features* wf)
2920     WineWindow* window = (WineWindow*)w;
2922     OnMainThread(^{
2923         [window setWindowFeatures:wf];
2924     });
2927 /***********************************************************************
2928  *              macdrv_set_cocoa_window_state
2930  * Update a Cocoa window's state.
2931  */
2932 void macdrv_set_cocoa_window_state(macdrv_window w,
2933         const struct macdrv_window_state* state)
2935     WineWindow* window = (WineWindow*)w;
2937     OnMainThread(^{
2938         [window setMacDrvState:state];
2939     });
2942 /***********************************************************************
2943  *              macdrv_set_cocoa_window_title
2945  * Set a Cocoa window's title.
2946  */
2947 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2948         size_t length)
2950     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2951     WineWindow* window = (WineWindow*)w;
2952     NSString* titleString;
2954     if (title)
2955         titleString = [NSString stringWithCharacters:title length:length];
2956     else
2957         titleString = @"";
2958     OnMainThreadAsync(^{
2959         [window setTitle:titleString];
2960         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2961             [NSApp changeWindowsItem:window title:titleString filename:NO];
2962     });
2964     [pool release];
2967 /***********************************************************************
2968  *              macdrv_order_cocoa_window
2970  * Reorder a Cocoa window relative to other windows.  If prev is
2971  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
2972  * it is ordered above that window.  Otherwise, it is ordered to the
2973  * front.
2974  */
2975 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2976         macdrv_window n, int activate)
2978     WineWindow* window = (WineWindow*)w;
2979     WineWindow* prev = (WineWindow*)p;
2980     WineWindow* next = (WineWindow*)n;
2982     OnMainThreadAsync(^{
2983         [window orderBelow:prev
2984                    orAbove:next
2985                   activate:activate];
2986     });
2987     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2988                                   forWindow:window];
2989     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2990                                 forWindow:next];
2993 /***********************************************************************
2994  *              macdrv_hide_cocoa_window
2996  * Hides a Cocoa window.
2997  */
2998 void macdrv_hide_cocoa_window(macdrv_window w)
3000     WineWindow* window = (WineWindow*)w;
3002     OnMainThread(^{
3003         [window doOrderOut];
3004     });
3007 /***********************************************************************
3008  *              macdrv_set_cocoa_window_frame
3010  * Move a Cocoa window.
3011  */
3012 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3014     WineWindow* window = (WineWindow*)w;
3016     OnMainThread(^{
3017         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3018     });
3021 /***********************************************************************
3022  *              macdrv_get_cocoa_window_frame
3024  * Gets the frame of a Cocoa window.
3025  */
3026 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3028     WineWindow* window = (WineWindow*)w;
3030     OnMainThread(^{
3031         NSRect frame;
3033         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3034         [[WineApplicationController sharedController] flipRect:&frame];
3035         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3036     });
3039 /***********************************************************************
3040  *              macdrv_set_cocoa_parent_window
3042  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3043  * the parent window.
3044  */
3045 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3047     WineWindow* window = (WineWindow*)w;
3049     OnMainThread(^{
3050         [window setMacDrvParentWindow:(WineWindow*)parent];
3051     });
3054 /***********************************************************************
3055  *              macdrv_set_window_surface
3056  */
3057 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3059     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3060     WineWindow* window = (WineWindow*)w;
3062     OnMainThread(^{
3063         window.surface = surface;
3064         window.surface_mutex = mutex;
3065     });
3067     [pool release];
3070 /***********************************************************************
3071  *              macdrv_window_needs_display
3073  * Mark a window as needing display in a specified rect (in non-client
3074  * area coordinates).
3075  */
3076 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3078     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3079     WineWindow* window = (WineWindow*)w;
3081     OnMainThreadAsync(^{
3082         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3083     });
3085     [pool release];
3088 /***********************************************************************
3089  *              macdrv_set_window_shape
3091  * Sets the shape of a Cocoa window from an array of rectangles.  If
3092  * rects is NULL, resets the window's shape to its frame.
3093  */
3094 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3096     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3097     WineWindow* window = (WineWindow*)w;
3099     OnMainThread(^{
3100         if (!rects || !count)
3101         {
3102             window.shape = nil;
3103             window.shapeData = nil;
3104             [window checkEmptyShaped];
3105         }
3106         else
3107         {
3108             size_t length = sizeof(*rects) * count;
3109             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3110             {
3111                 NSBezierPath* path;
3112                 unsigned int i;
3114                 path = [NSBezierPath bezierPath];
3115                 for (i = 0; i < count; i++)
3116                     [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3117                 window.shape = path;
3118                 window.shapeData = [NSData dataWithBytes:rects length:length];
3119                 [window checkEmptyShaped];
3120             }
3121         }
3122     });
3124     [pool release];
3127 /***********************************************************************
3128  *              macdrv_set_window_alpha
3129  */
3130 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3132     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3133     WineWindow* window = (WineWindow*)w;
3135     [window setAlphaValue:alpha];
3137     [pool release];
3140 /***********************************************************************
3141  *              macdrv_set_window_color_key
3142  */
3143 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3144                                  CGFloat keyBlue)
3146     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3147     WineWindow* window = (WineWindow*)w;
3149     OnMainThread(^{
3150         window.colorKeyed       = TRUE;
3151         window.colorKeyRed      = keyRed;
3152         window.colorKeyGreen    = keyGreen;
3153         window.colorKeyBlue     = keyBlue;
3154         [window checkTransparency];
3155     });
3157     [pool release];
3160 /***********************************************************************
3161  *              macdrv_clear_window_color_key
3162  */
3163 void macdrv_clear_window_color_key(macdrv_window w)
3165     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3166     WineWindow* window = (WineWindow*)w;
3168     OnMainThread(^{
3169         window.colorKeyed = FALSE;
3170         [window checkTransparency];
3171     });
3173     [pool release];
3176 /***********************************************************************
3177  *              macdrv_window_use_per_pixel_alpha
3178  */
3179 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3181     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3182     WineWindow* window = (WineWindow*)w;
3184     OnMainThread(^{
3185         window.usePerPixelAlpha = use_per_pixel_alpha;
3186         [window checkTransparency];
3187     });
3189     [pool release];
3192 /***********************************************************************
3193  *              macdrv_give_cocoa_window_focus
3195  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3196  * orders it front and, if its frame was not within the desktop bounds,
3197  * Cocoa will typically move it on-screen.
3198  */
3199 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3201     WineWindow* window = (WineWindow*)w;
3203     OnMainThread(^{
3204         [window makeFocused:activate];
3205     });
3208 /***********************************************************************
3209  *              macdrv_set_window_min_max_sizes
3211  * Sets the window's minimum and maximum content sizes.
3212  */
3213 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3215     WineWindow* window = (WineWindow*)w;
3217     OnMainThread(^{
3218         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3219     });
3222 /***********************************************************************
3223  *              macdrv_create_view
3225  * Creates and returns a view with the specified frame rect.  The
3226  * caller is responsible for calling macdrv_dispose_view() on the view
3227  * when it is done with it.
3228  */
3229 macdrv_view macdrv_create_view(CGRect rect)
3231     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3232     __block WineContentView* view;
3234     if (CGRectIsNull(rect)) rect = CGRectZero;
3236     OnMainThread(^{
3237         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3239         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3240         [view setAutoresizesSubviews:NO];
3241         [view setHidden:YES];
3242         [nc addObserver:view
3243                selector:@selector(updateGLContexts)
3244                    name:NSViewGlobalFrameDidChangeNotification
3245                  object:view];
3246         [nc addObserver:view
3247                selector:@selector(updateGLContexts)
3248                    name:NSApplicationDidChangeScreenParametersNotification
3249                  object:NSApp];
3250     });
3252     [pool release];
3253     return (macdrv_view)view;
3256 /***********************************************************************
3257  *              macdrv_dispose_view
3259  * Destroys a view previously returned by macdrv_create_view.
3260  */
3261 void macdrv_dispose_view(macdrv_view v)
3263     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3264     WineContentView* view = (WineContentView*)v;
3266     OnMainThread(^{
3267         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3268         WineWindow* window = (WineWindow*)[view window];
3270         [nc removeObserver:view
3271                       name:NSViewGlobalFrameDidChangeNotification
3272                     object:view];
3273         [nc removeObserver:view
3274                       name:NSApplicationDidChangeScreenParametersNotification
3275                     object:NSApp];
3276         [view removeFromSuperview];
3277         [view release];
3278         [window updateForGLSubviews];
3279     });
3281     [pool release];
3284 /***********************************************************************
3285  *              macdrv_set_view_frame
3286  */
3287 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3289     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3290     WineContentView* view = (WineContentView*)v;
3292     if (CGRectIsNull(rect)) rect = CGRectZero;
3294     OnMainThread(^{
3295         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3296         NSRect oldFrame = [view frame];
3298         if (!NSEqualRects(oldFrame, newFrame))
3299         {
3300             [[view superview] setNeedsDisplayInRect:oldFrame];
3301             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3302                 [view setFrameSize:newFrame.size];
3303             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3304                 [view setFrameOrigin:newFrame.origin];
3305             else
3306                 [view setFrame:newFrame];
3307             [view setNeedsDisplay:YES];
3309             if (retina_enabled)
3310             {
3311                 int backing_size[2] = { 0 };
3312                 [view wine_setBackingSize:backing_size];
3313             }
3314             [(WineWindow*)[view window] updateForGLSubviews];
3315         }
3316     });
3318     [pool release];
3321 /***********************************************************************
3322  *              macdrv_set_view_superview
3324  * Move a view to a new superview and position it relative to its
3325  * siblings.  If p is non-NULL, the view is ordered behind it.
3326  * Otherwise, the view is ordered above n.  If s is NULL, use the
3327  * content view of w as the new superview.
3328  */
3329 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3331     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3332     WineContentView* view = (WineContentView*)v;
3333     WineContentView* superview = (WineContentView*)s;
3334     WineWindow* window = (WineWindow*)w;
3335     WineContentView* prev = (WineContentView*)p;
3336     WineContentView* next = (WineContentView*)n;
3338     if (!superview)
3339         superview = [window contentView];
3341     OnMainThread(^{
3342         if (superview == [view superview])
3343         {
3344             NSArray* subviews = [superview subviews];
3345             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3346             if (!prev && !next && index == [subviews count] - 1)
3347                 return;
3348             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3349                 return;
3350             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3351                 return;
3352         }
3354         WineWindow* oldWindow = (WineWindow*)[view window];
3355         WineWindow* newWindow = (WineWindow*)[superview window];
3357         if (prev)
3358             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3359         else
3360             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3362         if (oldWindow != newWindow)
3363         {
3364             [oldWindow updateForGLSubviews];
3365             [newWindow updateForGLSubviews];
3366         }
3367     });
3369     [pool release];
3372 /***********************************************************************
3373  *              macdrv_set_view_hidden
3374  */
3375 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3377     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3378     WineContentView* view = (WineContentView*)v;
3380     OnMainThread(^{
3381         [view setHidden:hidden];
3382     });
3384     [pool release];
3387 /***********************************************************************
3388  *              macdrv_add_view_opengl_context
3390  * Add an OpenGL context to the list being tracked for each view.
3391  */
3392 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3394     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3395     WineContentView* view = (WineContentView*)v;
3396     WineOpenGLContext *context = (WineOpenGLContext*)c;
3398     OnMainThread(^{
3399         [view addGLContext:context];
3400     });
3402     [pool release];
3405 /***********************************************************************
3406  *              macdrv_remove_view_opengl_context
3408  * Add an OpenGL context to the list being tracked for each view.
3409  */
3410 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3412     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3413     WineContentView* view = (WineContentView*)v;
3414     WineOpenGLContext *context = (WineOpenGLContext*)c;
3416     OnMainThreadAsync(^{
3417         [view removeGLContext:context];
3418     });
3420     [pool release];
3423 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3425     WineContentView* view = (WineContentView*)v;
3427     if (![view isKindOfClass:[WineContentView class]])
3428         return FALSE;
3430     [view wine_getBackingSize:backing_size];
3431     return TRUE;
3434 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3436     WineContentView* view = (WineContentView*)v;
3438     if ([view isKindOfClass:[WineContentView class]])
3439         [view wine_setBackingSize:backing_size];
3442 /***********************************************************************
3443  *              macdrv_window_background_color
3445  * Returns the standard Mac window background color as a 32-bit value of
3446  * the form 0x00rrggbb.
3447  */
3448 uint32_t macdrv_window_background_color(void)
3450     static uint32_t result;
3451     static dispatch_once_t once;
3453     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3454     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3455     // of it is to draw with it.
3456     dispatch_once(&once, ^{
3457         OnMainThread(^{
3458             unsigned char rgbx[4];
3459             unsigned char *planes = rgbx;
3460             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3461                                                                                pixelsWide:1
3462                                                                                pixelsHigh:1
3463                                                                             bitsPerSample:8
3464                                                                           samplesPerPixel:3
3465                                                                                  hasAlpha:NO
3466                                                                                  isPlanar:NO
3467                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3468                                                                              bitmapFormat:0
3469                                                                               bytesPerRow:4
3470                                                                              bitsPerPixel:32];
3471             [NSGraphicsContext saveGraphicsState];
3472             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3473             [[NSColor windowBackgroundColor] set];
3474             NSRectFill(NSMakeRect(0, 0, 1, 1));
3475             [NSGraphicsContext restoreGraphicsState];
3476             [bitmap release];
3477             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3478         });
3479     });
3481     return result;
3484 /***********************************************************************
3485  *              macdrv_send_text_input_event
3486  */
3487 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3489     OnMainThreadAsync(^{
3490         BOOL ret;
3491         macdrv_event* event;
3492         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3493         if (![window isKindOfClass:[WineWindow class]])
3494         {
3495             window = (WineWindow*)[NSApp mainWindow];
3496             if (![window isKindOfClass:[WineWindow class]])
3497                 window = [[WineApplicationController sharedController] frontWineWindow];
3498         }
3500         if (window)
3501         {
3502             NSUInteger localFlags = flags;
3503             CGEventRef c;
3504             NSEvent* event;
3506             window.imeData = data;
3507             fix_device_modifiers_by_generic(&localFlags);
3509             // An NSEvent created with +keyEventWithType:... is internally marked
3510             // as synthetic and doesn't get sent through input methods.  But one
3511             // created from a CGEvent doesn't have that problem.
3512             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3513             CGEventSetFlags(c, localFlags);
3514             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3515             event = [NSEvent eventWithCGEvent:c];
3516             CFRelease(c);
3518             window.commandDone = FALSE;
3519             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3520         }
3521         else
3522             ret = FALSE;
3524         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3525         event->sent_text_input.handled = ret;
3526         event->sent_text_input.done = done;
3527         [[window queue] postEvent:event];
3528         macdrv_release_event(event);
3529     });