quartz/tests: Make testpin_{AddRef,Release}() static.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blobe405cda2d24747b2cf20d3eefecfac8bc1e641a0
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 #include "config.h"
23 #import <Carbon/Carbon.h>
24 #import <CoreVideo/CoreVideo.h>
25 #ifdef HAVE_METAL_METAL_H
26 #import <Metal/Metal.h>
27 #import <QuartzCore/QuartzCore.h>
28 #endif
30 #import "cocoa_window.h"
32 #include "macdrv_cocoa.h"
33 #import "cocoa_app.h"
34 #import "cocoa_event.h"
35 #import "cocoa_opengl.h"
38 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
39 enum {
40     NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
41     NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
42     NSWindowFullScreenButton = 7,
43     NSFullScreenWindowMask = 1 << 14,
46 @interface NSWindow (WineFullScreenExtensions)
47     - (void) toggleFullScreen:(id)sender;
48 @end
49 #endif
52 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
53 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
54 enum {
55     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
57 #endif
60 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
62     NSUInteger style_mask;
64     if (wf->title_bar)
65     {
66         style_mask = NSTitledWindowMask;
67         if (wf->close_button) style_mask |= NSClosableWindowMask;
68         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
69         if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
70         if (wf->utility) style_mask |= NSUtilityWindowMask;
71     }
72     else style_mask = NSBorderlessWindowMask;
74     return style_mask;
78 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
80     NSScreen* screen;
81     for (screen in screens)
82     {
83         if (NSIntersectsRect(frame, [screen frame]))
84             return TRUE;
85     }
86     return FALSE;
90 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
92     for (NSScreen* screen in screens)
93     {
94         if (NSContainsRect(rect, [screen frame]))
95             return screen;
96     }
97     return nil;
101 /* We rely on the supposedly device-dependent modifier flags to distinguish the
102    keys on the left side of the keyboard from those on the right.  Some event
103    sources don't set those device-depdendent flags.  If we see a device-independent
104    flag for a modifier without either corresponding device-dependent flag, assume
105    the left one. */
106 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
108     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
109         *modifiers |= NX_DEVICELCMDKEYMASK;
110     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
111         *modifiers |= NX_DEVICELSHIFTKEYMASK;
112     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
113         *modifiers |= NX_DEVICELCTLKEYMASK;
114     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
115         *modifiers |= NX_DEVICELALTKEYMASK;
118 /* As we manipulate individual bits of a modifier mask, we can end up with
119    inconsistent sets of flags.  In particular, we might set or clear one of the
120    left/right-specific bits, but not the corresponding non-side-specific bit.
121    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
122    otherwise clear it. */
123 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
125     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
126         *modifiers |= NX_COMMANDMASK;
127     else
128         *modifiers &= ~NX_COMMANDMASK;
129     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
130         *modifiers |= NX_SHIFTMASK;
131     else
132         *modifiers &= ~NX_SHIFTMASK;
133     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
134         *modifiers |= NX_CONTROLMASK;
135     else
136         *modifiers &= ~NX_CONTROLMASK;
137     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
138         *modifiers |= NX_ALTERNATEMASK;
139     else
140         *modifiers &= ~NX_ALTERNATEMASK;
143 static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
145     fix_device_modifiers_by_generic(&modifiers);
146     NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
147                                              NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
149     // The MACDRV keyboard driver translates Command keys to Alt. If the
150     // Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
151     // Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
152     if (modifiers & NX_DEVICELALTKEYMASK)
153         new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
154     if (modifiers & NX_DEVICERALTKEYMASK)
155         new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
157     if (modifiers & NX_DEVICELCMDKEYMASK)
158         new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
159     if (modifiers & NX_DEVICERCMDKEYMASK)
160         new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
162     fix_generic_modifiers_by_device(&new_modifiers);
163     return new_modifiers;
167 @interface NSWindow (WineAccessPrivateMethods)
168     - (id) _displayChanged;
169 @end
172 @interface WineDisplayLink : NSObject
174     CGDirectDisplayID _displayID;
175     CVDisplayLinkRef _link;
176     NSMutableSet* _windows;
178     NSTimeInterval _actualRefreshPeriod;
179     NSTimeInterval _nominalRefreshPeriod;
181     NSTimeInterval _lastDisplayTime;
184     - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
186     - (void) addWindow:(WineWindow*)window;
187     - (void) removeWindow:(WineWindow*)window;
189     - (NSTimeInterval) refreshPeriod;
191     - (void) start;
193 @end
195 @implementation WineDisplayLink
197 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
199     - (id) initWithDisplayID:(CGDirectDisplayID)displayID
200     {
201         self = [super init];
202         if (self)
203         {
204             CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
205             if (status == kCVReturnSuccess && !_link)
206                 status = kCVReturnError;
207             if (status == kCVReturnSuccess)
208                 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
209             if (status != kCVReturnSuccess)
210             {
211                 [self release];
212                 return nil;
213             }
215             _displayID = displayID;
216             _windows = [[NSMutableSet alloc] init];
217         }
218         return self;
219     }
221     - (void) dealloc
222     {
223         if (_link)
224         {
225             CVDisplayLinkStop(_link);
226             CVDisplayLinkRelease(_link);
227         }
228         [_windows release];
229         [super dealloc];
230     }
232     - (void) addWindow:(WineWindow*)window
233     {
234         BOOL firstWindow;
235         @synchronized(self) {
236             firstWindow = !_windows.count;
237             [_windows addObject:window];
238         }
239         if (firstWindow || !CVDisplayLinkIsRunning(_link))
240             [self start];
241     }
243     - (void) removeWindow:(WineWindow*)window
244     {
245         BOOL lastWindow = FALSE;
246         @synchronized(self) {
247             BOOL hadWindows = _windows.count > 0;
248             [_windows removeObject:window];
249             if (hadWindows && !_windows.count)
250                 lastWindow = TRUE;
251         }
252         if (lastWindow && CVDisplayLinkIsRunning(_link))
253             CVDisplayLinkStop(_link);
254     }
256     - (void) fire
257     {
258         NSSet* windows;
259         @synchronized(self) {
260             windows = [_windows copy];
261         }
262         dispatch_async(dispatch_get_main_queue(), ^{
263             BOOL anyDisplayed = FALSE;
264             for (WineWindow* window in windows)
265             {
266                 if ([window viewsNeedDisplay])
267                 {
268                     [window displayIfNeeded];
269                     anyDisplayed = YES;
270                 }
271             }
273             NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
274             if (anyDisplayed)
275                 _lastDisplayTime = now;
276             else if (_lastDisplayTime + 2.0 < now)
277                 CVDisplayLinkStop(_link);
278         });
279         [windows release];
280     }
282     - (NSTimeInterval) refreshPeriod
283     {
284         if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
285             return _actualRefreshPeriod;
287         if (_nominalRefreshPeriod)
288             return _nominalRefreshPeriod;
290         CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
291         if (time.flags & kCVTimeIsIndefinite)
292             return 1.0 / 60.0;
293         _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
294         return _nominalRefreshPeriod;
295     }
297     - (void) start
298     {
299         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
300         CVDisplayLinkStart(_link);
301     }
303 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
305     WineDisplayLink* link = displayLinkContext;
306     [link fire];
307     return kCVReturnSuccess;
310 @end
313 @interface WineBaseView : NSView
314 @end
317 #ifdef HAVE_METAL_METAL_H
318 @interface WineMetalView : WineBaseView
320     id<MTLDevice> _device;
323     - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
325 @end
326 #endif
329 @interface WineContentView : WineBaseView <NSTextInputClient>
331     NSMutableArray* glContexts;
332     NSMutableArray* pendingGlContexts;
333     BOOL _everHadGLContext;
334     BOOL _cachedHasGLDescendant;
335     BOOL _cachedHasGLDescendantValid;
336     BOOL clearedGlSurface;
338     NSMutableAttributedString* markedText;
339     NSRange markedTextSelection;
341     int backingSize[2];
343 #ifdef HAVE_METAL_METAL_H
344     WineMetalView *_metalView;
345 #endif
348 @property (readonly, nonatomic) BOOL everHadGLContext;
350     - (void) addGLContext:(WineOpenGLContext*)context;
351     - (void) removeGLContext:(WineOpenGLContext*)context;
352     - (void) updateGLContexts;
354     - (void) wine_getBackingSize:(int*)outBackingSize;
355     - (void) wine_setBackingSize:(const int*)newBackingSize;
357 #ifdef HAVE_METAL_METAL_H
358     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
359 #endif
361 @end
364 @interface WineWindow ()
366 @property (readwrite, nonatomic) BOOL disabled;
367 @property (readwrite, nonatomic) BOOL noActivate;
368 @property (readwrite, nonatomic) BOOL floating;
369 @property (readwrite, nonatomic) BOOL drawnSinceShown;
370 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
371 @property (retain, nonatomic) NSWindow* latentParentWindow;
373 @property (nonatomic) void* hwnd;
374 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
376 @property (nonatomic) void* surface;
377 @property (nonatomic) pthread_mutex_t* surface_mutex;
379 @property (copy, nonatomic) NSBezierPath* shape;
380 @property (copy, nonatomic) NSData* shapeData;
381 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
382 @property (readonly, nonatomic) BOOL needsTransparency;
384 @property (nonatomic) BOOL colorKeyed;
385 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
386 @property (nonatomic) BOOL usePerPixelAlpha;
388 @property (assign, nonatomic) void* imeData;
389 @property (nonatomic) BOOL commandDone;
391 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
393     - (void) updateForGLSubviews;
395     - (BOOL) becameEligibleParentOrChild;
396     - (void) becameIneligibleChild;
398     - (void) windowDidDrawContent;
400 @end
403 @implementation WineBaseView
405     - (void) setRetinaMode:(int)mode
406     {
407         for (WineBaseView* subview in [self subviews])
408         {
409             if ([subview isKindOfClass:[WineBaseView class]])
410                 [subview setRetinaMode:mode];
411         }
412     }
414     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
415     {
416         return YES;
417     }
419     - (BOOL) preservesContentDuringLiveResize
420     {
421         // Returning YES from this tells Cocoa to keep our view's content during
422         // a Cocoa-driven resize.  In theory, we're also supposed to override
423         // -setFrameSize: to mark exposed sections as needing redisplay, but
424         // user32 will take care of that in a roundabout way.  This way, we don't
425         // redraw until the window surface is flushed.
426         //
427         // This doesn't do anything when we resize the window ourselves.
428         return YES;
429     }
431     - (BOOL)acceptsFirstResponder
432     {
433         return [[self window] contentView] == self;
434     }
436     - (BOOL) mouseDownCanMoveWindow
437     {
438         return NO;
439     }
441     - (NSFocusRingType) focusRingType
442     {
443         return NSFocusRingTypeNone;
444     }
446 @end
449 @implementation WineContentView
451 @synthesize everHadGLContext = _everHadGLContext;
453     - (void) dealloc
454     {
455         [markedText release];
456         [glContexts release];
457         [pendingGlContexts release];
458         [super dealloc];
459     }
461     - (BOOL) isFlipped
462     {
463         return YES;
464     }
466     - (void) drawRect:(NSRect)rect
467     {
468         WineWindow* window = (WineWindow*)[self window];
470         for (WineOpenGLContext* context in pendingGlContexts)
471         {
472             if (!clearedGlSurface)
473             {
474                 context.shouldClearToBlack = TRUE;
475                 clearedGlSurface = TRUE;
476             }
477             context.needsUpdate = TRUE;
478         }
479         [glContexts addObjectsFromArray:pendingGlContexts];
480         [pendingGlContexts removeAllObjects];
482         if ([window contentView] != self)
483             return;
485         if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
486         {
487             [[NSColor clearColor] setFill];
488             NSRectFill(rect);
490             [window.shape addClip];
492             [[NSColor windowBackgroundColor] setFill];
493             NSRectFill(rect);
494         }
496         if (window.surface && window.surface_mutex &&
497             !pthread_mutex_lock(window.surface_mutex))
498         {
499             const CGRect* rects;
500             int count;
502             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
503             {
504                 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
505                 CGContextRef context;
506                 int i;
508                 [window.shape addClip];
510                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
511                 CGContextSetBlendMode(context, kCGBlendModeCopy);
512                 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
514                 for (i = 0; i < count; i++)
515                 {
516                     CGRect imageRect;
517                     CGImageRef image;
519                     imageRect = CGRectIntersection(rects[i], dirtyRect);
520                     image = create_surface_image(window.surface, &imageRect, FALSE);
522                     if (image)
523                     {
524                         if (window.colorKeyed)
525                         {
526                             CGImageRef maskedImage;
527                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
528                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
529                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
530                             maskedImage = CGImageCreateWithMaskingColors(image, components);
531                             if (maskedImage)
532                             {
533                                 CGImageRelease(image);
534                                 image = maskedImage;
535                             }
536                         }
538                         CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
540                         CGImageRelease(image);
541                     }
542                 }
544                 [window windowDidDrawContent];
545             }
547             pthread_mutex_unlock(window.surface_mutex);
548         }
550         // If the window may be transparent, then we have to invalidate the
551         // shadow every time we draw.  Also, if this is the first time we've
552         // drawn since changing from transparent to opaque.
553         if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
554         {
555             window.shapeChangedSinceLastDraw = FALSE;
556             [window invalidateShadow];
557         }
558     }
560     - (void) addGLContext:(WineOpenGLContext*)context
561     {
562         BOOL hadContext = _everHadGLContext;
563         if (!glContexts)
564             glContexts = [[NSMutableArray alloc] init];
565         if (!pendingGlContexts)
566             pendingGlContexts = [[NSMutableArray alloc] init];
568         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
569         {
570             [glContexts addObject:context];
571             if (!clearedGlSurface)
572             {
573                 context.shouldClearToBlack = TRUE;
574                 clearedGlSurface = TRUE;
575             }
576             context.needsUpdate = TRUE;
577         }
578         else
579         {
580             [pendingGlContexts addObject:context];
581             [self setNeedsDisplay:YES];
582         }
584         _everHadGLContext = YES;
585         if (!hadContext)
586             [self invalidateHasGLDescendant];
587         [(WineWindow*)[self window] updateForGLSubviews];
588     }
590     - (void) removeGLContext:(WineOpenGLContext*)context
591     {
592         [glContexts removeObjectIdenticalTo:context];
593         [pendingGlContexts removeObjectIdenticalTo:context];
594         [(WineWindow*)[self window] updateForGLSubviews];
595     }
597     - (void) updateGLContexts:(BOOL)reattach
598     {
599         for (WineOpenGLContext* context in glContexts)
600         {
601             context.needsUpdate = TRUE;
602             if (reattach)
603                 context.needsReattach = TRUE;
604         }
605     }
607     - (void) updateGLContexts
608     {
609         [self updateGLContexts:NO];
610     }
612     - (BOOL) _hasGLDescendant
613     {
614         if ([self isHidden])
615             return NO;
616         if (_everHadGLContext)
617             return YES;
618         for (WineContentView* view in [self subviews])
619         {
620             if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
621                 return YES;
622         }
623         return NO;
624     }
626     - (BOOL) hasGLDescendant
627     {
628         if (!_cachedHasGLDescendantValid)
629         {
630             _cachedHasGLDescendant = [self _hasGLDescendant];
631             _cachedHasGLDescendantValid = YES;
632         }
633         return _cachedHasGLDescendant;
634     }
636     - (void) invalidateHasGLDescendant
637     {
638         BOOL invalidateAncestors = _cachedHasGLDescendantValid;
639         _cachedHasGLDescendantValid = NO;
640         if (invalidateAncestors && self != [[self window] contentView])
641         {
642             WineContentView* superview = (WineContentView*)[self superview];
643             if ([superview isKindOfClass:[WineContentView class]])
644                 [superview invalidateHasGLDescendant];
645         }
646     }
648     - (void) wine_getBackingSize:(int*)outBackingSize
649     {
650         @synchronized(self) {
651             memcpy(outBackingSize, backingSize, sizeof(backingSize));
652         }
653     }
654     - (void) wine_setBackingSize:(const int*)newBackingSize
655     {
656         @synchronized(self) {
657             memcpy(backingSize, newBackingSize, sizeof(backingSize));
658         }
659     }
661 #ifdef HAVE_METAL_METAL_H
662     - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
663     {
664         if (_metalView) return _metalView;
666         WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
667         [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
668         [self setAutoresizesSubviews:YES];
669         [self addSubview:view positioned:NSWindowBelow relativeTo:nil];
670         _metalView = view;
672         [(WineWindow*)self.window windowDidDrawContent];
674         return _metalView;
675     }
676 #endif
678     - (void) setRetinaMode:(int)mode
679     {
680         double scale = mode ? 0.5 : 2.0;
681         NSRect frame = self.frame;
682         frame.origin.x *= scale;
683         frame.origin.y *= scale;
684         frame.size.width *= scale;
685         frame.size.height *= scale;
686         [self setFrame:frame];
687         [self updateGLContexts];
689         [super setRetinaMode:mode];
690     }
692     - (void) viewDidHide
693     {
694         [super viewDidHide];
695         [self invalidateHasGLDescendant];
696     }
698     - (void) viewDidUnhide
699     {
700         [super viewDidUnhide];
701         [self updateGLContexts:YES];
702         [self invalidateHasGLDescendant];
703     }
705     - (void) completeText:(NSString*)text
706     {
707         macdrv_event* event;
708         WineWindow* window = (WineWindow*)[self window];
710         event = macdrv_create_event(IM_SET_TEXT, window);
711         event->im_set_text.data = [window imeData];
712         event->im_set_text.text = (CFStringRef)[text copy];
713         event->im_set_text.complete = TRUE;
715         [[window queue] postEvent:event];
717         macdrv_release_event(event);
719         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
720         markedTextSelection = NSMakeRange(0, 0);
721         [[self inputContext] discardMarkedText];
722     }
724     - (void) didAddSubview:(NSView*)subview
725     {
726         if ([subview isKindOfClass:[WineContentView class]])
727         {
728             WineContentView* view = (WineContentView*)subview;
729             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
730                 [self invalidateHasGLDescendant];
731         }
732         [super didAddSubview:subview];
733     }
735     - (void) willRemoveSubview:(NSView*)subview
736     {
737         if ([subview isKindOfClass:[WineContentView class]])
738         {
739             WineContentView* view = (WineContentView*)subview;
740             if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
741                 [self invalidateHasGLDescendant];
742         }
743 #ifdef HAVE_METAL_METAL_H
744         if (subview == _metalView)
745             _metalView = nil;
746 #endif
747         [super willRemoveSubview:subview];
748     }
750     /*
751      * ---------- NSTextInputClient methods ----------
752      */
753     - (NSTextInputContext*) inputContext
754     {
755         if (!markedText)
756             markedText = [[NSMutableAttributedString alloc] init];
757         return [super inputContext];
758     }
760     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
761     {
762         if ([string isKindOfClass:[NSAttributedString class]])
763             string = [string string];
765         if ([string isKindOfClass:[NSString class]])
766             [self completeText:string];
767     }
769     - (void) doCommandBySelector:(SEL)aSelector
770     {
771         [(WineWindow*)[self window] setCommandDone:TRUE];
772     }
774     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
775     {
776         if ([string isKindOfClass:[NSAttributedString class]])
777             string = [string string];
779         if ([string isKindOfClass:[NSString class]])
780         {
781             macdrv_event* event;
782             WineWindow* window = (WineWindow*)[self window];
784             if (replacementRange.location == NSNotFound)
785                 replacementRange = NSMakeRange(0, [markedText length]);
787             [markedText replaceCharactersInRange:replacementRange withString:string];
788             markedTextSelection = selectedRange;
789             markedTextSelection.location += replacementRange.location;
791             event = macdrv_create_event(IM_SET_TEXT, window);
792             event->im_set_text.data = [window imeData];
793             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
794             event->im_set_text.complete = FALSE;
795             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
797             [[window queue] postEvent:event];
799             macdrv_release_event(event);
801             [[self inputContext] invalidateCharacterCoordinates];
802         }
803     }
805     - (void) unmarkText
806     {
807         [self completeText:nil];
808     }
810     - (NSRange) selectedRange
811     {
812         return markedTextSelection;
813     }
815     - (NSRange) markedRange
816     {
817         NSRange range = NSMakeRange(0, [markedText length]);
818         if (!range.length)
819             range.location = NSNotFound;
820         return range;
821     }
823     - (BOOL) hasMarkedText
824     {
825         return [markedText length] > 0;
826     }
828     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
829     {
830         if (aRange.location >= [markedText length])
831             return nil;
833         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
834         if (actualRange)
835             *actualRange = aRange;
836         return [markedText attributedSubstringFromRange:aRange];
837     }
839     - (NSArray*) validAttributesForMarkedText
840     {
841         return [NSArray array];
842     }
844     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
845     {
846         macdrv_query* query;
847         WineWindow* window = (WineWindow*)[self window];
848         NSRect ret;
850         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
852         query = macdrv_create_query();
853         query->type = QUERY_IME_CHAR_RECT;
854         query->window = (macdrv_window)[window retain];
855         query->ime_char_rect.data = [window imeData];
856         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
858         if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
859         {
860             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
861             ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
862             [[WineApplicationController sharedController] flipRect:&ret];
863         }
864         else
865             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
867         macdrv_release_query(query);
869         if (actualRange)
870             *actualRange = aRange;
871         return ret;
872     }
874     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
875     {
876         return NSNotFound;
877     }
879     - (NSInteger) windowLevel
880     {
881         return [[self window] level];
882     }
884 @end
887 #ifdef HAVE_METAL_METAL_H
888 @implementation WineMetalView
890     - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
891     {
892         self = [super initWithFrame:frame];
893         if (self)
894         {
895             _device = [device retain];
896             self.wantsLayer = YES;
897             self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
898         }
899         return self;
900     }
902     - (void) dealloc
903     {
904         [_device release];
905         [super dealloc];
906     }
908     - (void) setRetinaMode:(int)mode
909     {
910         self.layer.contentsScale = mode ? 2.0 : 1.0;
911         [super setRetinaMode:mode];
912     }
914     - (CALayer*) makeBackingLayer
915     {
916         CAMetalLayer *layer = [CAMetalLayer layer];
917         layer.device = _device;
918         layer.framebufferOnly = YES;
919         layer.magnificationFilter = kCAFilterNearest;
920         layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
921         layer.contentsScale = retina_on ? 2.0 : 1.0;
922         return layer;
923     }
925     - (BOOL) isOpaque
926     {
927         return YES;
928     }
930 @end
931 #endif
934 @implementation WineWindow
936     static WineWindow* causing_becomeKeyWindow;
938     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
939     @synthesize drawnSinceShown;
940     @synthesize surface, surface_mutex;
941     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
942     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
943     @synthesize usePerPixelAlpha;
944     @synthesize imeData, commandDone;
946     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
947                                  windowFrame:(NSRect)window_frame
948                                         hwnd:(void*)hwnd
949                                        queue:(WineEventQueue*)queue
950     {
951         WineWindow* window;
952         WineContentView* contentView;
953         NSTrackingArea* trackingArea;
954         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
956         [[WineApplicationController sharedController] flipRect:&window_frame];
958         window = [[[self alloc] initWithContentRect:window_frame
959                                           styleMask:style_mask_for_features(wf)
960                                             backing:NSBackingStoreBuffered
961                                               defer:YES] autorelease];
963         if (!window) return nil;
965         /* Standardize windows to eliminate differences between titled and
966            borderless windows and between NSWindow and NSPanel. */
967         [window setHidesOnDeactivate:NO];
968         [window setReleasedWhenClosed:NO];
970         [window setOneShot:YES];
971         [window disableCursorRects];
972         [window setShowsResizeIndicator:NO];
973         [window setHasShadow:wf->shadow];
974         [window setAcceptsMouseMovedEvents:YES];
975         [window setDelegate:window];
976         [window setBackgroundColor:[NSColor clearColor]];
977         [window setOpaque:NO];
978         window.hwnd = hwnd;
979         window.queue = queue;
980         window->savedContentMinSize = NSZeroSize;
981         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
982         window->resizable = wf->resizable;
983         window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
985         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
986                                                                   (NSString*)kUTTypeContent,
987                                                                   nil]];
989         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
990         if (!contentView)
991             return nil;
992         [contentView setAutoresizesSubviews:NO];
994         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
995            because they give us mouse moves in the background. */
996         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
997                                                      options:(NSTrackingMouseMoved |
998                                                               NSTrackingActiveAlways |
999                                                               NSTrackingInVisibleRect)
1000                                                        owner:window
1001                                                     userInfo:nil] autorelease];
1002         if (!trackingArea)
1003             return nil;
1004         [contentView addTrackingArea:trackingArea];
1006         [window setContentView:contentView];
1007         [window setInitialFirstResponder:contentView];
1009         [nc addObserver:window
1010                selector:@selector(updateFullscreen)
1011                    name:NSApplicationDidChangeScreenParametersNotification
1012                  object:NSApp];
1013         [window updateFullscreen];
1015         [nc addObserver:window
1016                selector:@selector(applicationWillHide)
1017                    name:NSApplicationWillHideNotification
1018                  object:NSApp];
1019         [nc addObserver:window
1020                selector:@selector(applicationDidUnhide)
1021                    name:NSApplicationDidUnhideNotification
1022                  object:NSApp];
1024         [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1025                                                               selector:@selector(checkWineDisplayLink)
1026                                                                   name:NSWorkspaceActiveSpaceDidChangeNotification
1027                                                                 object:[NSWorkspace sharedWorkspace]];
1029         [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1031         return window;
1032     }
1034     - (void) dealloc
1035     {
1036         [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1037         [[NSNotificationCenter defaultCenter] removeObserver:self];
1038         [queue release];
1039         [latentChildWindows release];
1040         [latentParentWindow release];
1041         [shape release];
1042         [shapeData release];
1043         [super dealloc];
1044     }
1046     - (BOOL) preventResizing
1047     {
1048         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1049         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
1050     }
1052     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1053     {
1054         if (allow_immovable_windows && (disabled || inMaximized))
1055             return NO;
1056         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1057             return NO;
1058         else
1059             return YES;
1060     }
1062     - (void) adjustFeaturesForState
1063     {
1064         NSUInteger style = [self styleMask];
1066         if (style & NSClosableWindowMask)
1067             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1068         if (style & NSMiniaturizableWindowMask)
1069             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1070         if (style & NSResizableWindowMask)
1071             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1072         if ([self respondsToSelector:@selector(toggleFullScreen:)])
1073         {
1074             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1075                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1076         }
1078         if ([self preventResizing])
1079         {
1080             NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1081             [self setContentMinSize:size];
1082             [self setContentMaxSize:size];
1083         }
1084         else
1085         {
1086             [self setContentMaxSize:savedContentMaxSize];
1087             [self setContentMinSize:savedContentMinSize];
1088         }
1090         if (allow_immovable_windows || cursor_clipping_locks_windows)
1091             [self setMovable:[self allowsMovingWithMaximized:maximized]];
1092     }
1094     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1095     {
1096         if ([self respondsToSelector:@selector(toggleFullScreen:)])
1097         {
1098             NSUInteger style = [self styleMask];
1100             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1101                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
1102                 !(self.parentWindow || self.latentParentWindow))
1103             {
1104                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1105                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1106             }
1107             else
1108             {
1109                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1110                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1111                 if (style & NSFullScreenWindowMask)
1112                     [super toggleFullScreen:nil];
1113             }
1114         }
1116         if (behavior != [self collectionBehavior])
1117         {
1118             [self setCollectionBehavior:behavior];
1119             [self adjustFeaturesForState];
1120         }
1121     }
1123     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1124     {
1125         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1126                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1127         NSUInteger currentStyle = [self styleMask];
1128         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1130         if (newStyle != currentStyle)
1131         {
1132             NSString* title = [[[self title] copy] autorelease];
1133             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1134             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1135             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1136             {
1137                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1138                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1139                 // just NSTitledWindowMask, the window buttons should disappear rather
1140                 // than just being disabled.  But they don't.  Similarly in reverse.
1141                 // The workaround is to also toggle NSClosableWindowMask at the same time.
1142                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1143             }
1144             [self setStyleMask:newStyle];
1146             // -setStyleMask: resets the firstResponder to the window.  Set it
1147             // back to the content view.
1148             if ([[self contentView] acceptsFirstResponder])
1149                 [self makeFirstResponder:[self contentView]];
1151             [self adjustFullScreenBehavior:[self collectionBehavior]];
1153             if ([[self title] length] == 0 && [title length] > 0)
1154                 [self setTitle:title];
1155         }
1157         resizable = wf->resizable;
1158         [self adjustFeaturesForState];
1159         [self setHasShadow:wf->shadow];
1160     }
1162     // Indicates if the window would be visible if the app were not hidden.
1163     - (BOOL) wouldBeVisible
1164     {
1165         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1166     }
1168     - (BOOL) isOrderedIn
1169     {
1170         return [self wouldBeVisible] || [self isMiniaturized];
1171     }
1173     - (NSInteger) minimumLevelForActive:(BOOL)active
1174     {
1175         NSInteger level;
1177         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1178                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1179             level = NSFloatingWindowLevel;
1180         else
1181             level = NSNormalWindowLevel;
1183         if (active)
1184         {
1185             BOOL captured;
1187             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1189             if (captured || fullscreen)
1190             {
1191                 if (captured)
1192                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1193                 else
1194                     level = NSStatusWindowLevel + 1;
1196                 if (self.floating)
1197                     level++;
1198             }
1199         }
1201         return level;
1202     }
1204     - (void) postDidUnminimizeEvent
1205     {
1206         macdrv_event* event;
1208         /* Coalesce events by discarding any previous ones still in the queue. */
1209         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1210                                forWindow:self];
1212         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1213         [queue postEvent:event];
1214         macdrv_release_event(event);
1215     }
1217     - (void) sendResizeStartQuery
1218     {
1219         macdrv_query* query = macdrv_create_query();
1220         query->type = QUERY_RESIZE_START;
1221         query->window = (macdrv_window)[self retain];
1223         [self.queue query:query timeout:0.3];
1224         macdrv_release_query(query);
1225     }
1227     - (void) setMacDrvState:(const struct macdrv_window_state*)state
1228     {
1229         NSWindowCollectionBehavior behavior;
1231         self.disabled = state->disabled;
1232         self.noActivate = state->no_activate;
1234         if (self.floating != state->floating)
1235         {
1236             self.floating = state->floating;
1237             if (state->floating)
1238             {
1239                 // Became floating.  If child of non-floating window, make that
1240                 // relationship latent.
1241                 WineWindow* parent = (WineWindow*)[self parentWindow];
1242                 if (parent && !parent.floating)
1243                     [self becameIneligibleChild];
1244             }
1245             else
1246             {
1247                 // Became non-floating.  If parent of floating children, make that
1248                 // relationship latent.
1249                 WineWindow* child;
1250                 for (child in [self childWineWindows])
1251                 {
1252                     if (child.floating)
1253                         [child becameIneligibleChild];
1254                 }
1255             }
1257             // Check our latent relationships.  If floating status was the only
1258             // reason they were latent, then make them active.
1259             if ([self isVisible])
1260                 [self becameEligibleParentOrChild];
1262             [[WineApplicationController sharedController] adjustWindowLevels];
1263         }
1265         if (state->minimized_valid)
1266         {
1267             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1269             pendingMinimize = FALSE;
1270             if (state->minimized && ![self isMiniaturized])
1271             {
1272                 if ([self wouldBeVisible])
1273                 {
1274                     if ([self styleMask] & NSFullScreenWindowMask)
1275                     {
1276                         [self postDidUnminimizeEvent];
1277                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1278                     }
1279                     else
1280                     {
1281                         [super miniaturize:nil];
1282                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1283                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
1284                                    event_mask_for_type(WINDOW_LOST_FOCUS);
1285                     }
1286                 }
1287                 else
1288                     pendingMinimize = TRUE;
1289             }
1290             else if (!state->minimized && [self isMiniaturized])
1291             {
1292                 ignore_windowDeminiaturize = TRUE;
1293                 [self deminiaturize:nil];
1294                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1295             }
1297             if (discard)
1298                 [queue discardEventsMatchingMask:discard forWindow:self];
1299         }
1301         if (state->maximized != maximized)
1302         {
1303             maximized = state->maximized;
1304             [self adjustFeaturesForState];
1306             if (!maximized && [self inLiveResize])
1307                 [self sendResizeStartQuery];
1308         }
1310         behavior = NSWindowCollectionBehaviorDefault;
1311         if (state->excluded_by_expose)
1312             behavior |= NSWindowCollectionBehaviorTransient;
1313         else
1314             behavior |= NSWindowCollectionBehaviorManaged;
1315         if (state->excluded_by_cycle)
1316         {
1317             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1318             if ([self isOrderedIn])
1319                 [NSApp removeWindowsItem:self];
1320         }
1321         else
1322         {
1323             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1324             if ([self isOrderedIn])
1325                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1326         }
1327         [self adjustFullScreenBehavior:behavior];
1328     }
1330     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1331     {
1332         BOOL reordered = FALSE;
1334         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1335         {
1336             if ([self level] > [child level])
1337                 [child setLevel:[self level]];
1338             if (![child isVisible])
1339                 [child setAutodisplay:YES];
1340             [self addChildWindow:child ordered:NSWindowAbove];
1341             [child checkWineDisplayLink];
1342             [latentChildWindows removeObjectIdenticalTo:child];
1343             child.latentParentWindow = nil;
1344             reordered = TRUE;
1345         }
1346         else
1347         {
1348             if (!latentChildWindows)
1349                 latentChildWindows = [[NSMutableArray alloc] init];
1350             if (![latentChildWindows containsObject:child])
1351                 [latentChildWindows addObject:child];
1352             child.latentParentWindow = self;
1353         }
1355         return reordered;
1356     }
1358     - (BOOL) addChildWineWindow:(WineWindow*)child
1359     {
1360         return [self addChildWineWindow:child assumeVisible:FALSE];
1361     }
1363     - (void) removeChildWineWindow:(WineWindow*)child
1364     {
1365         [self removeChildWindow:child];
1366         if (child.latentParentWindow == self)
1367             child.latentParentWindow = nil;
1368         [latentChildWindows removeObjectIdenticalTo:child];
1369     }
1371     - (void) setChildWineWindows:(NSArray*)childWindows
1372     {
1373         NSArray* origChildren;
1374         NSUInteger count, start, limit, i;
1376         origChildren = self.childWineWindows;
1378         // If the current and desired children arrays match up to a point, leave
1379         // those matching children alone.
1380         count = childWindows.count;
1381         limit = MIN(origChildren.count, count);
1382         for (start = 0; start < limit; start++)
1383         {
1384             if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1385                 break;
1386         }
1388         // Remove all of the child windows and re-add them back-to-front so they
1389         // are in the desired order.
1390         for (i = start; i < count; i++)
1391         {
1392             WineWindow* child = [childWindows objectAtIndex:i];
1393             [self removeChildWindow:child];
1394         }
1395         for (i = start; i < count; i++)
1396         {
1397             WineWindow* child = [childWindows objectAtIndex:i];
1398             [self addChildWindow:child ordered:NSWindowAbove];
1399         }
1400     }
1402     static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1403     {
1404         NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1405         NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1406         NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1407         NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1408         if (index1 == NSNotFound)
1409         {
1410             if (index2 == NSNotFound)
1411                 return NSOrderedSame;
1412             else
1413                 return NSOrderedAscending;
1414         }
1415         else if (index2 == NSNotFound)
1416             return NSOrderedDescending;
1417         else if (index1 < index2)
1418             return NSOrderedDescending;
1419         else if (index2 < index1)
1420             return NSOrderedAscending;
1422         return NSOrderedSame;
1423     }
1425     - (BOOL) becameEligibleParentOrChild
1426     {
1427         BOOL reordered = FALSE;
1428         NSUInteger count;
1430         if (latentParentWindow.floating || !self.floating)
1431         {
1432             // If we aren't visible currently, we assume that we should be and soon
1433             // will be.  So, if the latent parent is visible that's enough to assume
1434             // we can establish the parent-child relationship in Cocoa.  That will
1435             // actually make us visible, which is fine.
1436             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1437                 reordered = TRUE;
1438         }
1440         // Here, though, we may not actually be visible yet and adding a child
1441         // won't make us visible.  The caller will have to call this method
1442         // again after actually making us visible.
1443         if ([self isVisible] && (count = [latentChildWindows count]))
1444         {
1445             NSMutableArray* windowNumbers;
1446             NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1447             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1448             NSUInteger i;
1450             windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1452             for (i = 0; i < count; i++)
1453             {
1454                 WineWindow* child = [latentChildWindows objectAtIndex:i];
1455                 if ([child isVisible] && (self.floating || !child.floating))
1456                 {
1457                     if (child.latentParentWindow == self)
1458                     {
1459                         if ([self level] > [child level])
1460                             [child setLevel:[self level]];
1461                         [childWindows addObject:child];
1462                         child.latentParentWindow = nil;
1463                         reordered = TRUE;
1464                     }
1465                     else
1466                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1467                     [indexesToRemove addIndex:i];
1468                 }
1469             }
1471             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1473             [childWindows sortWithOptions:NSSortStable
1474                           usingComparator:^NSComparisonResult(id obj1, id obj2){
1475                 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1476             }];
1477             [self setChildWineWindows:childWindows];
1478         }
1480         return reordered;
1481     }
1483     - (void) becameIneligibleChild
1484     {
1485         WineWindow* parent = (WineWindow*)[self parentWindow];
1486         if (parent)
1487         {
1488             if (!parent->latentChildWindows)
1489                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1490             [parent->latentChildWindows insertObject:self atIndex:0];
1491             self.latentParentWindow = parent;
1492             [parent removeChildWindow:self];
1493         }
1494     }
1496     - (void) becameIneligibleParentOrChild
1497     {
1498         NSArray* childWindows = [self childWineWindows];
1500         [self becameIneligibleChild];
1502         if ([childWindows count])
1503         {
1504             WineWindow* child;
1506             for (child in childWindows)
1507             {
1508                 child.latentParentWindow = self;
1509                 [self removeChildWindow:child];
1510             }
1512             if (latentChildWindows)
1513                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1514             else
1515                 latentChildWindows = [childWindows mutableCopy];
1516         }
1517     }
1519     // Determine if, among Wine windows, this window is directly above or below
1520     // a given other Wine window with no other Wine window intervening.
1521     // Intervening non-Wine windows are ignored.
1522     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1523     {
1524         NSNumber* windowNumber;
1525         NSNumber* otherWindowNumber;
1526         NSArray* windowNumbers;
1527         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1529         if (![self isVisible] || ![otherWindow isVisible])
1530             return FALSE;
1532         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1533         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1534         windowNumbers = [[self class] windowNumbersWithOptions:0];
1535         windowIndex = [windowNumbers indexOfObject:windowNumber];
1536         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1538         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1539             return FALSE;
1541         if (orderingMode == NSWindowAbove)
1542         {
1543             lowIndex = windowIndex;
1544             highIndex = otherWindowIndex;
1545         }
1546         else if (orderingMode == NSWindowBelow)
1547         {
1548             lowIndex = otherWindowIndex;
1549             highIndex = windowIndex;
1550         }
1551         else
1552             return FALSE;
1554         if (highIndex <= lowIndex)
1555             return FALSE;
1557         for (i = lowIndex + 1; i < highIndex; i++)
1558         {
1559             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1560             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1561             if ([interveningWindow isKindOfClass:[WineWindow class]])
1562                 return FALSE;
1563         }
1565         return TRUE;
1566     }
1568     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1569     {
1570         NSMutableArray* windowNumbers;
1571         NSNumber* childWindowNumber;
1572         NSUInteger otherIndex;
1573         NSArray* origChildren;
1574         NSMutableArray* children;
1576         // Get the z-order from the window server and modify it to reflect the
1577         // requested window ordering.
1578         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1579         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1580         [windowNumbers removeObject:childWindowNumber];
1581         if (other)
1582         {
1583             otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1584             [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1585         }
1586         else if (mode == NSWindowAbove)
1587             [windowNumbers insertObject:childWindowNumber atIndex:0];
1588         else
1589             [windowNumbers addObject:childWindowNumber];
1591         // Get our child windows and sort them in the reverse of the desired
1592         // z-order (back-to-front).
1593         origChildren = [self childWineWindows];
1594         children = [[origChildren mutableCopy] autorelease];
1595         [children sortWithOptions:NSSortStable
1596                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1597             return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1598         }];
1600         [self setChildWineWindows:children];
1601     }
1603     // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1604     // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1605     //      (non-nil, non-nil)  there is a level in the window tree where the two windows have sibling ancestors
1606     //                          if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1607     //                          otherwise, the two ancestors are each roots of disjoint window trees
1608     //      (nil, non-nil)      the other window is a descendent of self and *ancestorOfOther is the direct child
1609     //      (non-nil, nil)      self is a descendent of other and *ancestor is the direct child
1610     - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1611     {
1612         NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1613         WineWindow* child;
1614         WineWindow* parent;
1615         for (child = other;
1616              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1617              child = parent)
1618         {
1619             if (parent == self)
1620             {
1621                 *ancestor = nil;
1622                 *ancestorOfOther = child;
1623                 return;
1624             }
1626             [otherAncestors addObject:parent];
1627         }
1629         for (child = self;
1630              (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1631              child = parent)
1632         {
1633             NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1634             if (index != NSNotFound)
1635             {
1636                 *ancestor = child;
1637                 if (index == 0)
1638                     *ancestorOfOther = nil;
1639                 else
1640                     *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1641                 return;
1642             }
1643         }
1645         *ancestor = child;
1646         *ancestorOfOther = otherAncestors.lastObject;;
1647     }
1649     /* Returns whether or not the window was ordered in, which depends on if
1650        its frame intersects any screen. */
1651     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1652     {
1653         WineApplicationController* controller = [WineApplicationController sharedController];
1654         if (![self isMiniaturized])
1655         {
1656             BOOL needAdjustWindowLevels = FALSE;
1657             BOOL wasVisible;
1658             WineWindow* parent;
1659             WineWindow* child;
1661             [controller transformProcessToForeground];
1662             if ([NSApp isHidden])
1663                 [NSApp unhide:nil];
1664             wasVisible = [self isVisible];
1666             if (activate)
1667                 [NSApp activateIgnoringOtherApps:YES];
1669             NSDisableScreenUpdates();
1671             if ([self becameEligibleParentOrChild])
1672                 needAdjustWindowLevels = TRUE;
1674             if (prev || next)
1675             {
1676                 WineWindow* other = [prev isVisible] ? prev : next;
1677                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1679                 if (![self isOrdered:orderingMode relativeTo:other])
1680                 {
1681                     WineWindow* ancestor;
1682                     WineWindow* ancestorOfOther;
1684                     [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1685                     if (ancestor)
1686                     {
1687                         [self setAutodisplay:YES];
1688                         if (ancestorOfOther)
1689                         {
1690                             // This window level may not be right for this window based
1691                             // on floating-ness, fullscreen-ness, etc.  But we set it
1692                             // temporarily to allow us to order the windows properly.
1693                             // Then the levels get fixed by -adjustWindowLevels.
1694                             if ([ancestor level] != [ancestorOfOther level])
1695                                 [ancestor setLevel:[ancestorOfOther level]];
1697                             parent = (WineWindow*)ancestor.parentWindow;
1698                             if ([parent isKindOfClass:[WineWindow class]])
1699                                 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1700                             else
1701                                 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1702                         }
1704                         if (!ancestorOfOther || ancestor != self)
1705                         {
1706                             for (child = self;
1707                                  (parent = (WineWindow*)child.parentWindow);
1708                                  child = parent)
1709                             {
1710                                 if ([parent isKindOfClass:[WineWindow class]])
1711                                     [parent order:-orderingMode childWindow:child relativeTo:nil];
1712                                 if (parent == ancestor)
1713                                     break;
1714                             }
1715                         }
1717                         [self checkWineDisplayLink];
1718                         needAdjustWindowLevels = TRUE;
1719                     }
1720                 }
1721             }
1722             else
1723             {
1724                 for (child = self;
1725                      (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1726                      child = parent)
1727                 {
1728                     [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1729                 }
1731                 // Again, temporarily set level to make sure we can order to
1732                 // the right place.
1733                 next = [controller frontWineWindow];
1734                 if (next && [self level] < [next level])
1735                     [self setLevel:[next level]];
1736                 [self setAutodisplay:YES];
1737                 [self orderFront:nil];
1738                 [self checkWineDisplayLink];
1739                 needAdjustWindowLevels = TRUE;
1740             }
1741             pendingOrderOut = FALSE;
1743             if ([self becameEligibleParentOrChild])
1744                 needAdjustWindowLevels = TRUE;
1746             if (needAdjustWindowLevels)
1747             {
1748                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1749                     [controller updateFullscreenWindows];
1750                 [controller adjustWindowLevels];
1751             }
1753             if (pendingMinimize)
1754             {
1755                 [super miniaturize:nil];
1756                 pendingMinimize = FALSE;
1757             }
1759             NSEnableScreenUpdates();
1761             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1762                Generate a frame-changed event just in case.  The back end will ignore
1763                it if nothing actually changed. */
1764             [self windowDidResize:nil];
1766             if (![self isExcludedFromWindowsMenu])
1767                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1768         }
1769     }
1771     - (void) doOrderOut
1772     {
1773         WineApplicationController* controller = [WineApplicationController sharedController];
1774         BOOL wasVisible = [self isVisible];
1775         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1777         [self endWindowDragging];
1778         [controller windowWillOrderOut:self];
1780         if (enteringFullScreen || exitingFullScreen)
1781         {
1782             pendingOrderOut = TRUE;
1783             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1784                                              event_mask_for_type(WINDOW_GOT_FOCUS) |
1785                                              event_mask_for_type(WINDOW_LOST_FOCUS) |
1786                                              event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1787                                              event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1788                                              event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1789                                    forWindow:self];
1790             return;
1791         }
1793         pendingOrderOut = FALSE;
1795         if ([self isMiniaturized])
1796             pendingMinimize = TRUE;
1798         WineWindow* parent = (WineWindow*)self.parentWindow;
1799         if ([parent isKindOfClass:[WineWindow class]])
1800             [parent grabDockIconSnapshotFromWindow:self force:NO];
1802         [self becameIneligibleParentOrChild];
1803         if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1804         {
1805             fakingClose = TRUE;
1806             [self close];
1807             fakingClose = FALSE;
1808         }
1809         else
1810             [self orderOut:nil];
1811         [self checkWineDisplayLink];
1812         [self setBackgroundColor:[NSColor clearColor]];
1813         [self setOpaque:NO];
1814         drawnSinceShown = NO;
1815         savedVisibleState = FALSE;
1816         if (wasVisible && wasOnActiveSpace && fullscreen)
1817             [controller updateFullscreenWindows];
1818         [controller adjustWindowLevels];
1819         [NSApp removeWindowsItem:self];
1821         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1822                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1823                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1824                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1825                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1826                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1827                                forWindow:self];
1828     }
1830     - (void) updateFullscreen
1831     {
1832         NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1833         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1835         if (nowFullscreen != fullscreen)
1836         {
1837             WineApplicationController* controller = [WineApplicationController sharedController];
1839             fullscreen = nowFullscreen;
1840             if ([self isVisible] && [self isOnActiveSpace])
1841                 [controller updateFullscreenWindows];
1843             [controller adjustWindowLevels];
1844         }
1845     }
1847     - (void) setFrameAndWineFrame:(NSRect)frame
1848     {
1849         [self setFrame:frame display:YES];
1851         wineFrame = frame;
1852         roundedWineFrame = self.frame;
1853         CGFloat junk;
1854 #if CGFLOAT_IS_DOUBLE
1855         if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1856              !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1857             fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1858             fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1859             fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1860             fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1861             roundedWineFrame = wineFrame;
1862 #else
1863         if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1864              !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1865             fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1866             fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1867             fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1868             fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1869             roundedWineFrame = wineFrame;
1870 #endif
1871     }
1873     - (void) setFrameFromWine:(NSRect)contentRect
1874     {
1875         /* Origin is (left, top) in a top-down space.  Need to convert it to
1876            (left, bottom) in a bottom-up space. */
1877         [[WineApplicationController sharedController] flipRect:&contentRect];
1879         /* The back end is establishing a new window size and position.  It's
1880            not interested in any stale events regarding those that may be sitting
1881            in the queue. */
1882         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1883                                forWindow:self];
1885         if (!NSIsEmptyRect(contentRect))
1886         {
1887             NSRect frame, oldFrame;
1889             oldFrame = self.wine_fractionalFrame;
1890             frame = [self frameRectForContentRect:contentRect];
1891             if (!NSEqualRects(frame, oldFrame))
1892             {
1893                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1894                 BOOL needEnableScreenUpdates = FALSE;
1896                 if ([self preventResizing])
1897                 {
1898                     // Allow the following calls to -setFrame:display: to work even
1899                     // if they would violate the content size constraints. This
1900                     // shouldn't be necessary since the content size constraints are
1901                     // documented to not constrain that method, but it seems to be.
1902                     [self setContentMinSize:NSZeroSize];
1903                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1904                 }
1906                 if (equalSizes && [[self childWineWindows] count])
1907                 {
1908                     // If we change the window frame such that the origin moves
1909                     // but the size doesn't change, then Cocoa moves child
1910                     // windows with the parent.  We don't want that so we fake
1911                     // a change of the size and then change it back.
1912                     NSRect bogusFrame = frame;
1913                     bogusFrame.size.width++;
1915                     NSDisableScreenUpdates();
1916                     needEnableScreenUpdates = TRUE;
1918                     ignore_windowResize = TRUE;
1919                     [self setFrame:bogusFrame display:NO];
1920                     ignore_windowResize = FALSE;
1921                 }
1923                 [self setFrameAndWineFrame:frame];
1924                 if ([self preventResizing])
1925                 {
1926                     [self setContentMinSize:contentRect.size];
1927                     [self setContentMaxSize:contentRect.size];
1928                 }
1930                 if (needEnableScreenUpdates)
1931                     NSEnableScreenUpdates();
1933                 if (!enteringFullScreen &&
1934                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1935                     nonFullscreenFrame = frame;
1937                 [self updateFullscreen];
1939                 if ([self isOrderedIn])
1940                 {
1941                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1942                        event.  The back end will ignore it if nothing actually changed. */
1943                     [self windowDidResize:nil];
1944                 }
1945             }
1946         }
1947     }
1949     - (NSRect) wine_fractionalFrame
1950     {
1951         NSRect frame = self.frame;
1952         if (NSEqualRects(frame, roundedWineFrame))
1953             frame = wineFrame;
1954         return frame;
1955     }
1957     - (void) setMacDrvParentWindow:(WineWindow*)parent
1958     {
1959         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1960         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1961         {
1962             [oldParent removeChildWineWindow:self];
1963             [latentParentWindow removeChildWineWindow:self];
1964             if ([parent addChildWineWindow:self])
1965                 [[WineApplicationController sharedController] adjustWindowLevels];
1966             [self adjustFullScreenBehavior:[self collectionBehavior]];
1967         }
1968     }
1970     - (void) setDisabled:(BOOL)newValue
1971     {
1972         if (disabled != newValue)
1973         {
1974             disabled = newValue;
1975             [self adjustFeaturesForState];
1976         }
1977     }
1979     - (BOOL) needsTransparency
1980     {
1981         return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1982                 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1983     }
1985     - (void) checkTransparency
1986     {
1987         if (![self isOpaque] && !self.needsTransparency)
1988         {
1989             self.shapeChangedSinceLastDraw = TRUE;
1990             [[self contentView] setNeedsDisplay:YES];
1991             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1992             [self setOpaque:YES];
1993         }
1994         else if ([self isOpaque] && self.needsTransparency)
1995         {
1996             self.shapeChangedSinceLastDraw = TRUE;
1997             [[self contentView] setNeedsDisplay:YES];
1998             [self setBackgroundColor:[NSColor clearColor]];
1999             [self setOpaque:NO];
2000         }
2001     }
2003     - (void) setShape:(NSBezierPath*)newShape
2004     {
2005         if (shape == newShape) return;
2007         if (shape)
2008         {
2009             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
2010             [shape release];
2011         }
2012         if (newShape)
2013             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
2015         shape = [newShape copy];
2016         self.shapeChangedSinceLastDraw = TRUE;
2018         [self checkTransparency];
2019     }
2021     - (void) makeFocused:(BOOL)activate
2022     {
2023         if (activate)
2024         {
2025             [[WineApplicationController sharedController] transformProcessToForeground];
2026             [NSApp activateIgnoringOtherApps:YES];
2027         }
2029         causing_becomeKeyWindow = self;
2030         [self makeKeyWindow];
2031         causing_becomeKeyWindow = nil;
2033         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2034                                          event_mask_for_type(WINDOW_LOST_FOCUS)
2035                                forWindow:self];
2036     }
2038     - (void) postKey:(uint16_t)keyCode
2039              pressed:(BOOL)pressed
2040            modifiers:(NSUInteger)modifiers
2041                event:(NSEvent*)theEvent
2042     {
2043         macdrv_event* event;
2044         CGEventRef cgevent;
2045         WineApplicationController* controller = [WineApplicationController sharedController];
2047         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2048         event->key.keycode   = keyCode;
2049         event->key.modifiers = modifiers;
2050         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
2052         if ((cgevent = [theEvent CGEvent]))
2053             controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2055         [queue postEvent:event];
2057         macdrv_release_event(event);
2059         [controller noteKey:keyCode pressed:pressed];
2060     }
2062     - (void) postKeyEvent:(NSEvent *)theEvent
2063     {
2064         [self flagsChanged:theEvent];
2065         [self postKey:[theEvent keyCode]
2066               pressed:[theEvent type] == NSKeyDown
2067             modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2068                 event:theEvent];
2069     }
2071     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2072     {
2073         savedContentMinSize = minSize;
2074         savedContentMaxSize = maxSize;
2075         if (![self preventResizing])
2076         {
2077             [self setContentMinSize:minSize];
2078             [self setContentMaxSize:maxSize];
2079         }
2080     }
2082     - (WineWindow*) ancestorWineWindow
2083     {
2084         WineWindow* ancestor = self;
2085         for (;;)
2086         {
2087             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2088             if ([parent isKindOfClass:[WineWindow class]])
2089                 ancestor = parent;
2090             else
2091                 break;
2092         }
2093         return ancestor;
2094     }
2096     - (void) postBroughtForwardEvent
2097     {
2098         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2099         [queue postEvent:event];
2100         macdrv_release_event(event);
2101     }
2103     - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
2104     {
2105         macdrv_event* event;
2106         NSUInteger style = self.styleMask;
2108         if (isFullscreen)
2109             style |= NSFullScreenWindowMask;
2110         else
2111             style &= ~NSFullScreenWindowMask;
2112         frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2113         [[WineApplicationController sharedController] flipRect:&frame];
2115         /* Coalesce events by discarding any previous ones still in the queue. */
2116         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2117                                forWindow:self];
2119         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2120         event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2121         event->window_frame_changed.fullscreen = isFullscreen;
2122         event->window_frame_changed.in_resize = resizing;
2123         [queue postEvent:event];
2124         macdrv_release_event(event);
2125     }
2127     - (void) updateForCursorClipping
2128     {
2129         [self adjustFeaturesForState];
2130     }
2132     - (void) endWindowDragging
2133     {
2134         if (draggingPhase)
2135         {
2136             if (draggingPhase == 3)
2137             {
2138                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2139                 [queue postEvent:event];
2140                 macdrv_release_event(event);
2141             }
2143             draggingPhase = 0;
2144             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2145         }
2146     }
2148     - (NSMutableDictionary*) displayIDToDisplayLinkMap
2149     {
2150         static NSMutableDictionary* displayIDToDisplayLinkMap;
2151         if (!displayIDToDisplayLinkMap)
2152         {
2153             displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2155             [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2156                                                               object:NSApp
2157                                                                queue:nil
2158                                                           usingBlock:^(NSNotification *note){
2159                 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2160                 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2161                 [badDisplayIDs minusSet:validDisplayIDs];
2162                 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2163             }];
2164         }
2165         return displayIDToDisplayLinkMap;
2166     }
2168     - (WineDisplayLink*) wineDisplayLink
2169     {
2170         if (!_lastDisplayID)
2171             return nil;
2173         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2174         return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2175     }
2177     - (void) checkWineDisplayLink
2178     {
2179         NSScreen* screen = self.screen;
2180         if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2181             screen = nil;
2182 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2183         if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2184             screen = nil;
2185 #endif
2187         NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2188         CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2189         if (displayID == _lastDisplayID)
2190             return;
2192         NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2194         if (_lastDisplayID)
2195         {
2196             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2197             [link removeWindow:self];
2198         }
2199         if (displayID)
2200         {
2201             WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2202             if (!link)
2203             {
2204                 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2205                 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2206             }
2207             [link addWindow:self];
2208             [self displayIfNeeded];
2209         }
2210         _lastDisplayID = displayID;
2211     }
2213     - (BOOL) isEmptyShaped
2214     {
2215         return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2216     }
2218     - (BOOL) canProvideSnapshot
2219     {
2220         return (self.windowNumber > 0 && ![self isEmptyShaped]);
2221     }
2223     - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2224     {
2225         if (![self isEmptyShaped])
2226             return;
2228         NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2229         if (!force && now < lastDockIconSnapshot + 1)
2230             return;
2232         if (window)
2233         {
2234             if (![window canProvideSnapshot])
2235                 return;
2236         }
2237         else
2238         {
2239             CGFloat bestArea;
2240             for (WineWindow* childWindow in self.childWindows)
2241             {
2242                 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2243                     continue;
2245                 NSSize size = childWindow.frame.size;
2246                 CGFloat area = size.width * size.height;
2247                 if (!window || area > bestArea)
2248                 {
2249                     window = childWindow;
2250                     bestArea = area;
2251                 }
2252             }
2254             if (!window)
2255                 return;
2256         }
2258         const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2259         CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2260         CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2261         CFRelease(windowIDs);
2262         if (!windowImage)
2263             return;
2265         NSImage* appImage = [NSApp applicationIconImage];
2266         if (!appImage)
2267             appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2269         NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2270         [dockIcon lockFocus];
2272         CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2274         CGRect rect = CGRectMake(8, 8, 240, 240);
2275         size_t width = CGImageGetWidth(windowImage);
2276         size_t height = CGImageGetHeight(windowImage);
2277         if (width > height)
2278         {
2279             rect.size.height *= height / (double)width;
2280             rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2281         }
2282         else if (width != height)
2283         {
2284             rect.size.width *= width / (double)height;
2285             rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2286         }
2288         CGContextDrawImage(cgcontext, rect, windowImage);
2289         [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2290                     fromRect:NSZeroRect
2291                    operation:NSCompositeSourceOver
2292                     fraction:1
2293               respectFlipped:YES
2294                        hints:nil];
2296         [dockIcon unlockFocus];
2298         CGImageRelease(windowImage);
2300         NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2301         if (![imageView isKindOfClass:[NSImageView class]])
2302         {
2303             imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2304             imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2305             self.dockTile.contentView = imageView;
2306         }
2307         imageView.image = dockIcon;
2308         [self.dockTile display];
2309         lastDockIconSnapshot = now;
2310     }
2312     - (void) checkEmptyShaped
2313     {
2314         if (self.dockTile.contentView && ![self isEmptyShaped])
2315         {
2316             self.dockTile.contentView = nil;
2317             lastDockIconSnapshot = 0;
2318         }
2319         [self checkWineDisplayLink];
2320     }
2323     /*
2324      * ---------- NSWindow method overrides ----------
2325      */
2326     - (BOOL) canBecomeKeyWindow
2327     {
2328         if (causing_becomeKeyWindow == self) return YES;
2329         if (self.disabled || self.noActivate) return NO;
2330         return [self isKeyWindow];
2331     }
2333     - (BOOL) canBecomeMainWindow
2334     {
2335         return [self canBecomeKeyWindow];
2336     }
2338     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2339     {
2340         // If a window is sized to completely cover a screen, then it's in
2341         // full-screen mode.  In that case, we don't allow NSWindow to constrain
2342         // it.
2343         NSArray* screens = [NSScreen screens];
2344         NSRect contentRect = [self contentRectForFrameRect:frameRect];
2345         if (!screen_covered_by_rect(contentRect, screens) &&
2346             frame_intersects_screens(frameRect, screens))
2347             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2348         return frameRect;
2349     }
2351     // This private method of NSWindow is called as Cocoa reacts to the display
2352     // configuration changing.  Among other things, it adjusts the window's
2353     // frame based on how the screen(s) changed size.  That tells Wine that the
2354     // window has been moved.  We don't want that.  Rather, we want to make
2355     // sure that the WinAPI notion of the window position is maintained/
2356     // restored, possibly undoing or overriding Cocoa's adjustment.
2357     //
2358     // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2359     // Cocoa has a chance to adjust the frame, thus preceding any resulting
2360     // WINDOW_FRAME_CHANGED event that may get queued.  The back end will
2361     // reassert its notion of the position.  That call won't get processed
2362     // until after this method returns, so it will override whatever this
2363     // method does to the window position.  It will also discard any pending
2364     // WINDOW_FRAME_CHANGED events.
2365     //
2366     // Unfortunately, the only way I've found to know when Cocoa is _about to_
2367     // adjust the window's position due to a display change is to hook into
2368     // this private method.  This private method has remained stable from 10.6
2369     // through 10.11.  If it does change, the most likely thing is that it
2370     // will be removed and no longer called and this fix will simply stop
2371     // working.  The only real danger would be if Apple changed the return type
2372     // to a struct or floating-point type, which would change the calling
2373     // convention.
2374     - (id) _displayChanged
2375     {
2376         macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2377         [queue postEvent:event];
2378         macdrv_release_event(event);
2380         return [super _displayChanged];
2381     }
2383     - (BOOL) isExcludedFromWindowsMenu
2384     {
2385         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2386     }
2388     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2389     {
2390         BOOL ret = [super validateMenuItem:menuItem];
2392         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2393             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2394         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2395             ret = NO;
2397         return ret;
2398     }
2400     /* We don't call this.  It's the action method of the items in the Window menu. */
2401     - (void) makeKeyAndOrderFront:(id)sender
2402     {
2403         if ([self isMiniaturized])
2404             [self deminiaturize:nil];
2405         [self orderBelow:nil orAbove:nil activate:NO];
2406         [[self ancestorWineWindow] postBroughtForwardEvent];
2408         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2409             [[WineApplicationController sharedController] windowGotFocus:self];
2410     }
2412     - (void) sendEvent:(NSEvent*)event
2413     {
2414         NSEventType type = event.type;
2416         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2417            interface control.  For example, Control-Tab switches focus among
2418            views.  We want to bypass that feature, so directly route key-down
2419            events to -keyDown:. */
2420         if (type == NSKeyDown)
2421             [[self firstResponder] keyDown:event];
2422         else
2423         {
2424             if (!draggingPhase && maximized && ![self isMovable] &&
2425                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2426                 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2427             {
2428                 NSRect titleBar = self.frame;
2429                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2430                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2431                 titleBar.origin.y = NSMaxY(contentRect);
2433                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2435                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2436                 {
2437                     static const NSWindowButton buttons[] = {
2438                         NSWindowCloseButton,
2439                         NSWindowMiniaturizeButton,
2440                         NSWindowZoomButton,
2441                         NSWindowFullScreenButton,
2442                     };
2443                     BOOL hitButton = NO;
2444                     int i;
2446                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2447                     {
2448                         NSButton* button;
2450                         if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2451                             continue;
2453                         button = [self standardWindowButton:buttons[i]];
2454                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2455                         {
2456                             hitButton = YES;
2457                             break;
2458                         }
2459                     }
2461                     if (!hitButton)
2462                     {
2463                         draggingPhase = 1;
2464                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2465                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2466                     }
2467                 }
2468             }
2469             else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2470             {
2471                 if ([self isMovable])
2472                 {
2473                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2474                     NSPoint newTopLeft = dragWindowStartPosition;
2476                     newTopLeft.x += point.x - dragStartPosition.x;
2477                     newTopLeft.y += point.y - dragStartPosition.y;
2479                     if (draggingPhase == 2)
2480                     {
2481                         macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2482                         mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2483                         [queue postEvent:mevent];
2484                         macdrv_release_event(mevent);
2486                         draggingPhase = 3;
2487                     }
2489                     [self setFrameTopLeftPoint:newTopLeft];
2490                 }
2491                 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2492                 {
2493                     macdrv_event* event;
2494                     NSRect frame = [self contentRectForFrameRect:self.frame];
2496                     [[WineApplicationController sharedController] flipRect:&frame];
2498                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2499                     event->window_restore_requested.keep_frame = TRUE;
2500                     event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2501                     [queue postEvent:event];
2502                     macdrv_release_event(event);
2504                     draggingPhase = 2;
2505                 }
2507                 if (type == NSLeftMouseUp)
2508                     [self endWindowDragging];
2509             }
2511             [super sendEvent:event];
2512         }
2513     }
2515     - (void) miniaturize:(id)sender
2516     {
2517         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2518         [queue postEvent:event];
2519         macdrv_release_event(event);
2521         WineWindow* parent = (WineWindow*)self.parentWindow;
2522         if ([parent isKindOfClass:[WineWindow class]])
2523             [parent grabDockIconSnapshotFromWindow:self force:YES];
2524     }
2526     - (void) toggleFullScreen:(id)sender
2527     {
2528         if (!self.disabled && !maximized)
2529             [super toggleFullScreen:sender];
2530     }
2532     - (void) setViewsNeedDisplay:(BOOL)value
2533     {
2534         if (value && ![self viewsNeedDisplay])
2535         {
2536             WineDisplayLink* link = [self wineDisplayLink];
2537             if (link)
2538             {
2539                 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2540                 if (_lastDisplayTime + [link refreshPeriod] < now)
2541                     [self setAutodisplay:YES];
2542                 else
2543                 {
2544                     [link start];
2545                     _lastDisplayTime = now;
2546                 }
2547             }
2548             else
2549                 [self setAutodisplay:YES];
2550         }
2551         [super setViewsNeedDisplay:value];
2552     }
2554     - (void) display
2555     {
2556         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2557         [super display];
2558         if (_lastDisplayID)
2559             [self setAutodisplay:NO];
2560     }
2562     - (void) displayIfNeeded
2563     {
2564         _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2565         [super displayIfNeeded];
2566         if (_lastDisplayID)
2567             [self setAutodisplay:NO];
2568     }
2570     - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2571     {
2572         if (flag)
2573             [self setAutodisplay:YES];
2574         [super setFrame:frameRect display:flag];
2575     }
2577     - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2578     {
2579         if (displayFlag)
2580             [self setAutodisplay:YES];
2581         [super setFrame:frameRect display:displayFlag animate:animateFlag];
2582     }
2584     - (void) windowDidDrawContent
2585     {
2586         if (!drawnSinceShown)
2587         {
2588             drawnSinceShown = YES;
2589             dispatch_async(dispatch_get_main_queue(), ^{
2590                 [self checkTransparency];
2591             });
2592         }
2593     }
2595     - (NSArray*) childWineWindows
2596     {
2597         NSArray* childWindows = self.childWindows;
2598         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2599             return [child isKindOfClass:[WineWindow class]];
2600         }];
2601         return [childWindows objectsAtIndexes:indexes];
2602     }
2604     - (void) updateForGLSubviews
2605     {
2606         if (gl_surface_mode == GL_SURFACE_BEHIND)
2607             [self checkTransparency];
2608     }
2610     - (void) setRetinaMode:(int)mode
2611     {
2612         NSRect frame;
2613         double scale = mode ? 0.5 : 2.0;
2614         NSAffineTransform* transform = [NSAffineTransform transform];
2616         [transform scaleBy:scale];
2618         if (shape)
2619             [shape transformUsingAffineTransform:transform];
2621         for (WineBaseView* subview in [self.contentView subviews])
2622         {
2623             if ([subview isKindOfClass:[WineBaseView class]])
2624                 [subview setRetinaMode:mode];
2625         }
2627         frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2628         frame.origin.x *= scale;
2629         frame.origin.y *= scale;
2630         frame.size.width *= scale;
2631         frame.size.height *= scale;
2632         frame = [self frameRectForContentRect:frame];
2634         savedContentMinSize = [transform transformSize:savedContentMinSize];
2635         if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2636             savedContentMaxSize.width *= scale;
2637         if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2638             savedContentMaxSize.height *= scale;
2640         self.contentMinSize = [transform transformSize:self.contentMinSize];
2641         NSSize temp = self.contentMaxSize;
2642         if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2643             temp.width *= scale;
2644         if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2645             temp.height *= scale;
2646         self.contentMaxSize = temp;
2648         ignore_windowResize = TRUE;
2649         [self setFrameAndWineFrame:frame];
2650         ignore_windowResize = FALSE;
2651     }
2654     /*
2655      * ---------- NSResponder method overrides ----------
2656      */
2657     - (void) keyDown:(NSEvent *)theEvent
2658     {
2659         if ([theEvent isARepeat])
2660         {
2661             if (!allowKeyRepeats)
2662                 return;
2663         }
2664         else
2665             allowKeyRepeats = YES;
2667         [self postKeyEvent:theEvent];
2668     }
2670     - (void) flagsChanged:(NSEvent *)theEvent
2671     {
2672         static const struct {
2673             NSUInteger  mask;
2674             uint16_t    keycode;
2675         } modifiers[] = {
2676             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
2677             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
2678             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
2679             { NX_DEVICELCTLKEYMASK,     kVK_Control },
2680             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
2681             { NX_DEVICELALTKEYMASK,     kVK_Option },
2682             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
2683             { NX_DEVICELCMDKEYMASK,     kVK_Command },
2684             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
2685         };
2687         NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2688         NSUInteger changed;
2689         int i, last_changed;
2691         fix_device_modifiers_by_generic(&modifierFlags);
2692         changed = modifierFlags ^ lastModifierFlags;
2694         last_changed = -1;
2695         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2696             if (changed & modifiers[i].mask)
2697                 last_changed = i;
2699         for (i = 0; i <= last_changed; i++)
2700         {
2701             if (changed & modifiers[i].mask)
2702             {
2703                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2705                 if (pressed)
2706                     allowKeyRepeats = NO;
2708                 if (i == last_changed)
2709                     lastModifierFlags = modifierFlags;
2710                 else
2711                 {
2712                     lastModifierFlags ^= modifiers[i].mask;
2713                     fix_generic_modifiers_by_device(&lastModifierFlags);
2714                 }
2716                 // Caps lock generates one event for each press-release action.
2717                 // We need to simulate a pair of events for each actual event.
2718                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2719                 {
2720                     [self postKey:modifiers[i].keycode
2721                           pressed:TRUE
2722                         modifiers:lastModifierFlags
2723                             event:(NSEvent*)theEvent];
2724                     pressed = FALSE;
2725                 }
2727                 [self postKey:modifiers[i].keycode
2728                       pressed:pressed
2729                     modifiers:lastModifierFlags
2730                         event:(NSEvent*)theEvent];
2731             }
2732         }
2733     }
2735     - (void) applicationWillHide
2736     {
2737         savedVisibleState = [self isVisible];
2738     }
2740     - (void) applicationDidUnhide
2741     {
2742         if ([self isVisible])
2743             [self becameEligibleParentOrChild];
2744     }
2747     /*
2748      * ---------- NSWindowDelegate methods ----------
2749      */
2750     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2751     {
2752         macdrv_query* query;
2753         NSSize size;
2755         query = macdrv_create_query();
2756         query->type = QUERY_MIN_MAX_INFO;
2757         query->window = (macdrv_window)[self retain];
2758         [self.queue query:query timeout:0.5];
2759         macdrv_release_query(query);
2761         size = [self contentMaxSize];
2762         if (proposedSize.width < size.width)
2763             size.width = proposedSize.width;
2764         if (proposedSize.height < size.height)
2765             size.height = proposedSize.height;
2766         return size;
2767     }
2769     - (void)windowDidBecomeKey:(NSNotification *)notification
2770     {
2771         WineApplicationController* controller = [WineApplicationController sharedController];
2772         NSEvent* event = [controller lastFlagsChanged];
2773         if (event)
2774             [self flagsChanged:event];
2776         if (causing_becomeKeyWindow == self) return;
2778         [controller windowGotFocus:self];
2779     }
2781     - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2782     {
2783         [self checkWineDisplayLink];
2784     }
2786     - (void) windowDidChangeScreen:(NSNotification*)notification
2787     {
2788         [self checkWineDisplayLink];
2789     }
2791     - (void)windowDidDeminiaturize:(NSNotification *)notification
2792     {
2793         WineApplicationController* controller = [WineApplicationController sharedController];
2795         if (!ignore_windowDeminiaturize)
2796             [self postDidUnminimizeEvent];
2797         ignore_windowDeminiaturize = FALSE;
2799         [self becameEligibleParentOrChild];
2801         if (fullscreen && [self isOnActiveSpace])
2802             [controller updateFullscreenWindows];
2803         [controller adjustWindowLevels];
2805         if (![self parentWindow])
2806             [self postBroughtForwardEvent];
2808         if (!self.disabled && !self.noActivate)
2809         {
2810             causing_becomeKeyWindow = self;
2811             [self makeKeyWindow];
2812             causing_becomeKeyWindow = nil;
2813             [controller windowGotFocus:self];
2814         }
2816         [self windowDidResize:notification];
2817         [self checkWineDisplayLink];
2818     }
2820     - (void) windowDidEndLiveResize:(NSNotification *)notification
2821     {
2822         if (!maximized)
2823         {
2824             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2825             [queue postEvent:event];
2826             macdrv_release_event(event);
2827         }
2828     }
2830     - (void) windowDidEnterFullScreen:(NSNotification*)notification
2831     {
2832         enteringFullScreen = FALSE;
2833         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2834         if (pendingOrderOut)
2835             [self doOrderOut];
2836     }
2838     - (void) windowDidExitFullScreen:(NSNotification*)notification
2839     {
2840         exitingFullScreen = FALSE;
2841         [self setFrameAndWineFrame:nonFullscreenFrame];
2842         [self windowDidResize:nil];
2843         if (pendingOrderOut)
2844             [self doOrderOut];
2845     }
2847     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2848     {
2849         enteringFullScreen = FALSE;
2850         enteredFullScreenTime = 0;
2851         if (pendingOrderOut)
2852             [self doOrderOut];
2853     }
2855     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2856     {
2857         exitingFullScreen = FALSE;
2858         [self windowDidResize:nil];
2859         if (pendingOrderOut)
2860             [self doOrderOut];
2861     }
2863     - (void)windowDidMiniaturize:(NSNotification *)notification
2864     {
2865         if (fullscreen && [self isOnActiveSpace])
2866             [[WineApplicationController sharedController] updateFullscreenWindows];
2867         [self checkWineDisplayLink];
2868     }
2870     - (void)windowDidMove:(NSNotification *)notification
2871     {
2872         [self windowDidResize:notification];
2873     }
2875     - (void)windowDidResignKey:(NSNotification *)notification
2876     {
2877         macdrv_event* event;
2879         if (causing_becomeKeyWindow) return;
2881         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2882         [queue postEvent:event];
2883         macdrv_release_event(event);
2884     }
2886     - (void)windowDidResize:(NSNotification *)notification
2887     {
2888         NSRect frame = self.wine_fractionalFrame;
2890         if ([self inLiveResize])
2891         {
2892             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2893                 resizingFromLeft = TRUE;
2894             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2895                 resizingFromTop = TRUE;
2896         }
2898         if (ignore_windowResize || exitingFullScreen) return;
2900         if ([self preventResizing])
2901         {
2902             NSRect contentRect = [self contentRectForFrameRect:frame];
2903             [self setContentMinSize:contentRect.size];
2904             [self setContentMaxSize:contentRect.size];
2905         }
2907         [self postWindowFrameChanged:frame
2908                           fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2909                             resizing:[self inLiveResize]];
2911         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2912         [self updateFullscreen];
2913     }
2915     - (BOOL)windowShouldClose:(id)sender
2916     {
2917         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2918         [queue postEvent:event];
2919         macdrv_release_event(event);
2920         return NO;
2921     }
2923     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2924     {
2925         if (maximized)
2926         {
2927             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2928             [queue postEvent:event];
2929             macdrv_release_event(event);
2930             return NO;
2931         }
2932         else if (!resizable)
2933         {
2934             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2935             [queue postEvent:event];
2936             macdrv_release_event(event);
2937             return NO;
2938         }
2940         return YES;
2941     }
2943     - (void) windowWillClose:(NSNotification*)notification
2944     {
2945         WineWindow* child;
2947         if (fakingClose) return;
2948         if (latentParentWindow)
2949         {
2950             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2951             self.latentParentWindow = nil;
2952         }
2954         for (child in latentChildWindows)
2955         {
2956             if (child.latentParentWindow == self)
2957                 child.latentParentWindow = nil;
2958         }
2959         [latentChildWindows removeAllObjects];
2960     }
2962     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2963     {
2964         enteringFullScreen = TRUE;
2965         nonFullscreenFrame = self.wine_fractionalFrame;
2966     }
2968     - (void) windowWillExitFullScreen:(NSNotification*)notification
2969     {
2970         exitingFullScreen = TRUE;
2971         [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2972     }
2974     - (void)windowWillMiniaturize:(NSNotification *)notification
2975     {
2976         [self becameIneligibleParentOrChild];
2977         [self grabDockIconSnapshotFromWindow:nil force:NO];
2978     }
2980     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2981     {
2982         if ([self inLiveResize])
2983         {
2984             if (maximized)
2985                 return self.wine_fractionalFrame.size;
2987             NSRect rect;
2988             macdrv_query* query;
2990             rect = [self frame];
2991             if (resizingFromLeft)
2992                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2993             if (!resizingFromTop)
2994                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2995             rect.size = frameSize;
2996             rect = [self contentRectForFrameRect:rect];
2997             [[WineApplicationController sharedController] flipRect:&rect];
2999             query = macdrv_create_query();
3000             query->type = QUERY_RESIZE_SIZE;
3001             query->window = (macdrv_window)[self retain];
3002             query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3003             query->resize_size.from_left = resizingFromLeft;
3004             query->resize_size.from_top = resizingFromTop;
3006             if ([self.queue query:query timeout:0.1])
3007             {
3008                 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3009                 rect = [self frameRectForContentRect:rect];
3010                 frameSize = rect.size;
3011             }
3013             macdrv_release_query(query);
3014         }
3016         return frameSize;
3017     }
3019     - (void) windowWillStartLiveResize:(NSNotification *)notification
3020     {
3021         [self endWindowDragging];
3023         if (maximized)
3024         {
3025             macdrv_event* event;
3026             NSRect frame = [self contentRectForFrameRect:self.frame];
3028             [[WineApplicationController sharedController] flipRect:&frame];
3030             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3031             event->window_restore_requested.keep_frame = TRUE;
3032             event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3033             [queue postEvent:event];
3034             macdrv_release_event(event);
3035         }
3036         else
3037             [self sendResizeStartQuery];
3039         frameAtResizeStart = [self frame];
3040         resizingFromLeft = resizingFromTop = FALSE;
3041     }
3043     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3044     {
3045         macdrv_query* query;
3046         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3047         NSSize maxSize;
3049         query = macdrv_create_query();
3050         query->type = QUERY_MIN_MAX_INFO;
3051         query->window = (macdrv_window)[self retain];
3052         [self.queue query:query timeout:0.5];
3053         macdrv_release_query(query);
3055         currentContentRect = [self contentRectForFrameRect:[self frame]];
3056         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3058         maxSize = [self contentMaxSize];
3059         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3060         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3062         // Try to keep the top-left corner where it is.
3063         newContentRect.origin.x = NSMinX(currentContentRect);
3064         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3066         // If that pushes the bottom or right off the screen, pull it up and to the left.
3067         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3068         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3069             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3070         if (NSMinY(newContentRect) < NSMinY(screenRect))
3071             newContentRect.origin.y = NSMinY(screenRect);
3073         // If that pushes the top or left off the screen, push it down and the right
3074         // again.  Do this last because the top-left corner is more important than the
3075         // bottom-right.
3076         if (NSMinX(newContentRect) < NSMinX(screenRect))
3077             newContentRect.origin.x = NSMinX(screenRect);
3078         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3079             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3081         return [self frameRectForContentRect:newContentRect];
3082     }
3085     /*
3086      * ---------- NSPasteboardOwner methods ----------
3087      */
3088     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3089     {
3090         macdrv_query* query = macdrv_create_query();
3091         query->type = QUERY_PASTEBOARD_DATA;
3092         query->window = (macdrv_window)[self retain];
3093         query->pasteboard_data.type = (CFStringRef)[type copy];
3095         [self.queue query:query timeout:3];
3096         macdrv_release_query(query);
3097     }
3099     - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3100     {
3101         macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3102         [queue postEvent:event];
3103         macdrv_release_event(event);
3104     }
3107     /*
3108      * ---------- NSDraggingDestination methods ----------
3109      */
3110     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3111     {
3112         return [self draggingUpdated:sender];
3113     }
3115     - (void) draggingExited:(id <NSDraggingInfo>)sender
3116     {
3117         // This isn't really a query.  We don't need any response.  However, it
3118         // has to be processed in a similar manner as the other drag-and-drop
3119         // queries in order to maintain the proper order of operations.
3120         macdrv_query* query = macdrv_create_query();
3121         query->type = QUERY_DRAG_EXITED;
3122         query->window = (macdrv_window)[self retain];
3124         [self.queue query:query timeout:0.1];
3125         macdrv_release_query(query);
3126     }
3128     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3129     {
3130         NSDragOperation ret;
3131         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3132         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3133         NSPasteboard* pb = [sender draggingPasteboard];
3135         macdrv_query* query = macdrv_create_query();
3136         query->type = QUERY_DRAG_OPERATION;
3137         query->window = (macdrv_window)[self retain];
3138         query->drag_operation.x = floor(cgpt.x);
3139         query->drag_operation.y = floor(cgpt.y);
3140         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3141         query->drag_operation.accepted_op = NSDragOperationNone;
3142         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3144         [self.queue query:query timeout:3];
3145         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3146         macdrv_release_query(query);
3148         return ret;
3149     }
3151     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3152     {
3153         BOOL ret;
3154         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3155         CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3156         NSPasteboard* pb = [sender draggingPasteboard];
3158         macdrv_query* query = macdrv_create_query();
3159         query->type = QUERY_DRAG_DROP;
3160         query->window = (macdrv_window)[self retain];
3161         query->drag_drop.x = floor(cgpt.x);
3162         query->drag_drop.y = floor(cgpt.y);
3163         query->drag_drop.op = [sender draggingSourceOperationMask];
3164         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3166         [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3167         ret = query->status;
3168         macdrv_release_query(query);
3170         return ret;
3171     }
3173     - (BOOL) wantsPeriodicDraggingUpdates
3174     {
3175         return NO;
3176     }
3178 @end
3181 /***********************************************************************
3182  *              macdrv_create_cocoa_window
3184  * Create a Cocoa window with the given content frame and features (e.g.
3185  * title bar, close box, etc.).
3186  */
3187 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3188         CGRect frame, void* hwnd, macdrv_event_queue queue)
3190     __block WineWindow* window;
3192     OnMainThread(^{
3193         window = [[WineWindow createWindowWithFeatures:wf
3194                                            windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3195                                                   hwnd:hwnd
3196                                                  queue:(WineEventQueue*)queue] retain];
3197     });
3199     return (macdrv_window)window;
3202 /***********************************************************************
3203  *              macdrv_destroy_cocoa_window
3205  * Destroy a Cocoa window.
3206  */
3207 void macdrv_destroy_cocoa_window(macdrv_window w)
3209     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3210     WineWindow* window = (WineWindow*)w;
3212     OnMainThread(^{
3213         [window doOrderOut];
3214         [window close];
3215     });
3216     [window.queue discardEventsMatchingMask:-1 forWindow:window];
3217     [window release];
3219     [pool release];
3222 /***********************************************************************
3223  *              macdrv_get_window_hwnd
3225  * Get the hwnd that was set for the window at creation.
3226  */
3227 void* macdrv_get_window_hwnd(macdrv_window w)
3229     WineWindow* window = (WineWindow*)w;
3230     return window.hwnd;
3233 /***********************************************************************
3234  *              macdrv_set_cocoa_window_features
3236  * Update a Cocoa window's features.
3237  */
3238 void macdrv_set_cocoa_window_features(macdrv_window w,
3239         const struct macdrv_window_features* wf)
3241     WineWindow* window = (WineWindow*)w;
3243     OnMainThread(^{
3244         [window setWindowFeatures:wf];
3245     });
3248 /***********************************************************************
3249  *              macdrv_set_cocoa_window_state
3251  * Update a Cocoa window's state.
3252  */
3253 void macdrv_set_cocoa_window_state(macdrv_window w,
3254         const struct macdrv_window_state* state)
3256     WineWindow* window = (WineWindow*)w;
3258     OnMainThread(^{
3259         [window setMacDrvState:state];
3260     });
3263 /***********************************************************************
3264  *              macdrv_set_cocoa_window_title
3266  * Set a Cocoa window's title.
3267  */
3268 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3269         size_t length)
3271     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3272     WineWindow* window = (WineWindow*)w;
3273     NSString* titleString;
3275     if (title)
3276         titleString = [NSString stringWithCharacters:title length:length];
3277     else
3278         titleString = @"";
3279     OnMainThreadAsync(^{
3280         [window setTitle:titleString];
3281         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3282             [NSApp changeWindowsItem:window title:titleString filename:NO];
3283     });
3285     [pool release];
3288 /***********************************************************************
3289  *              macdrv_order_cocoa_window
3291  * Reorder a Cocoa window relative to other windows.  If prev is
3292  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
3293  * it is ordered above that window.  Otherwise, it is ordered to the
3294  * front.
3295  */
3296 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3297         macdrv_window n, int activate)
3299     WineWindow* window = (WineWindow*)w;
3300     WineWindow* prev = (WineWindow*)p;
3301     WineWindow* next = (WineWindow*)n;
3303     OnMainThreadAsync(^{
3304         [window orderBelow:prev
3305                    orAbove:next
3306                   activate:activate];
3307     });
3308     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3309                                   forWindow:window];
3310     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3311                                 forWindow:next];
3314 /***********************************************************************
3315  *              macdrv_hide_cocoa_window
3317  * Hides a Cocoa window.
3318  */
3319 void macdrv_hide_cocoa_window(macdrv_window w)
3321     WineWindow* window = (WineWindow*)w;
3323     OnMainThread(^{
3324         [window doOrderOut];
3325     });
3328 /***********************************************************************
3329  *              macdrv_set_cocoa_window_frame
3331  * Move a Cocoa window.
3332  */
3333 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3335     WineWindow* window = (WineWindow*)w;
3337     OnMainThread(^{
3338         [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3339     });
3342 /***********************************************************************
3343  *              macdrv_get_cocoa_window_frame
3345  * Gets the frame of a Cocoa window.
3346  */
3347 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3349     WineWindow* window = (WineWindow*)w;
3351     OnMainThread(^{
3352         NSRect frame;
3354         frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3355         [[WineApplicationController sharedController] flipRect:&frame];
3356         *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3357     });
3360 /***********************************************************************
3361  *              macdrv_set_cocoa_parent_window
3363  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
3364  * the parent window.
3365  */
3366 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3368     WineWindow* window = (WineWindow*)w;
3370     OnMainThread(^{
3371         [window setMacDrvParentWindow:(WineWindow*)parent];
3372     });
3375 /***********************************************************************
3376  *              macdrv_set_window_surface
3377  */
3378 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3380     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3381     WineWindow* window = (WineWindow*)w;
3383     OnMainThread(^{
3384         window.surface = surface;
3385         window.surface_mutex = mutex;
3386     });
3388     [pool release];
3391 /***********************************************************************
3392  *              macdrv_window_needs_display
3394  * Mark a window as needing display in a specified rect (in non-client
3395  * area coordinates).
3396  */
3397 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3399     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3400     WineWindow* window = (WineWindow*)w;
3402     OnMainThreadAsync(^{
3403         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3404     });
3406     [pool release];
3409 /***********************************************************************
3410  *              macdrv_set_window_shape
3412  * Sets the shape of a Cocoa window from an array of rectangles.  If
3413  * rects is NULL, resets the window's shape to its frame.
3414  */
3415 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3417     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3418     WineWindow* window = (WineWindow*)w;
3420     OnMainThread(^{
3421         if (!rects || !count)
3422         {
3423             window.shape = nil;
3424             window.shapeData = nil;
3425             [window checkEmptyShaped];
3426         }
3427         else
3428         {
3429             size_t length = sizeof(*rects) * count;
3430             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3431             {
3432                 NSBezierPath* path;
3433                 unsigned int i;
3435                 path = [NSBezierPath bezierPath];
3436                 for (i = 0; i < count; i++)
3437                     [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3438                 window.shape = path;
3439                 window.shapeData = [NSData dataWithBytes:rects length:length];
3440                 [window checkEmptyShaped];
3441             }
3442         }
3443     });
3445     [pool release];
3448 /***********************************************************************
3449  *              macdrv_set_window_alpha
3450  */
3451 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3453     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3454     WineWindow* window = (WineWindow*)w;
3456     [window setAlphaValue:alpha];
3458     [pool release];
3461 /***********************************************************************
3462  *              macdrv_set_window_color_key
3463  */
3464 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3465                                  CGFloat keyBlue)
3467     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3468     WineWindow* window = (WineWindow*)w;
3470     OnMainThread(^{
3471         window.colorKeyed       = TRUE;
3472         window.colorKeyRed      = keyRed;
3473         window.colorKeyGreen    = keyGreen;
3474         window.colorKeyBlue     = keyBlue;
3475         [window checkTransparency];
3476     });
3478     [pool release];
3481 /***********************************************************************
3482  *              macdrv_clear_window_color_key
3483  */
3484 void macdrv_clear_window_color_key(macdrv_window w)
3486     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3487     WineWindow* window = (WineWindow*)w;
3489     OnMainThread(^{
3490         window.colorKeyed = FALSE;
3491         [window checkTransparency];
3492     });
3494     [pool release];
3497 /***********************************************************************
3498  *              macdrv_window_use_per_pixel_alpha
3499  */
3500 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3502     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3503     WineWindow* window = (WineWindow*)w;
3505     OnMainThread(^{
3506         window.usePerPixelAlpha = use_per_pixel_alpha;
3507         [window checkTransparency];
3508     });
3510     [pool release];
3513 /***********************************************************************
3514  *              macdrv_give_cocoa_window_focus
3516  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
3517  * orders it front and, if its frame was not within the desktop bounds,
3518  * Cocoa will typically move it on-screen.
3519  */
3520 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3522     WineWindow* window = (WineWindow*)w;
3524     OnMainThread(^{
3525         [window makeFocused:activate];
3526     });
3529 /***********************************************************************
3530  *              macdrv_set_window_min_max_sizes
3532  * Sets the window's minimum and maximum content sizes.
3533  */
3534 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3536     WineWindow* window = (WineWindow*)w;
3538     OnMainThread(^{
3539         [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3540     });
3543 /***********************************************************************
3544  *              macdrv_create_view
3546  * Creates and returns a view with the specified frame rect.  The
3547  * caller is responsible for calling macdrv_dispose_view() on the view
3548  * when it is done with it.
3549  */
3550 macdrv_view macdrv_create_view(CGRect rect)
3552     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3553     __block WineContentView* view;
3555     if (CGRectIsNull(rect)) rect = CGRectZero;
3557     OnMainThread(^{
3558         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3560         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3561         [view setAutoresizesSubviews:NO];
3562         [view setAutoresizingMask:NSViewNotSizable];
3563         [view setHidden:YES];
3564         [nc addObserver:view
3565                selector:@selector(updateGLContexts)
3566                    name:NSViewGlobalFrameDidChangeNotification
3567                  object:view];
3568         [nc addObserver:view
3569                selector:@selector(updateGLContexts)
3570                    name:NSApplicationDidChangeScreenParametersNotification
3571                  object:NSApp];
3572     });
3574     [pool release];
3575     return (macdrv_view)view;
3578 /***********************************************************************
3579  *              macdrv_dispose_view
3581  * Destroys a view previously returned by macdrv_create_view.
3582  */
3583 void macdrv_dispose_view(macdrv_view v)
3585     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3586     WineContentView* view = (WineContentView*)v;
3588     OnMainThread(^{
3589         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3590         WineWindow* window = (WineWindow*)[view window];
3592         [nc removeObserver:view
3593                       name:NSViewGlobalFrameDidChangeNotification
3594                     object:view];
3595         [nc removeObserver:view
3596                       name:NSApplicationDidChangeScreenParametersNotification
3597                     object:NSApp];
3598         [view removeFromSuperview];
3599         [view release];
3600         [window updateForGLSubviews];
3601     });
3603     [pool release];
3606 /***********************************************************************
3607  *              macdrv_set_view_frame
3608  */
3609 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3611     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3612     WineContentView* view = (WineContentView*)v;
3614     if (CGRectIsNull(rect)) rect = CGRectZero;
3616     OnMainThreadAsync(^{
3617         NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3618         NSRect oldFrame = [view frame];
3620         if (!NSEqualRects(oldFrame, newFrame))
3621         {
3622             [[view superview] setNeedsDisplayInRect:oldFrame];
3623             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3624                 [view setFrameSize:newFrame.size];
3625             else if (NSEqualSizes(oldFrame.size, newFrame.size))
3626                 [view setFrameOrigin:newFrame.origin];
3627             else
3628                 [view setFrame:newFrame];
3629             [view setNeedsDisplay:YES];
3631             if (retina_enabled)
3632             {
3633                 int backing_size[2] = { 0 };
3634                 [view wine_setBackingSize:backing_size];
3635             }
3636             [(WineWindow*)[view window] updateForGLSubviews];
3637         }
3638     });
3640     [pool release];
3643 /***********************************************************************
3644  *              macdrv_set_view_superview
3646  * Move a view to a new superview and position it relative to its
3647  * siblings.  If p is non-NULL, the view is ordered behind it.
3648  * Otherwise, the view is ordered above n.  If s is NULL, use the
3649  * content view of w as the new superview.
3650  */
3651 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3653     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3654     WineContentView* view = (WineContentView*)v;
3655     WineContentView* superview = (WineContentView*)s;
3656     WineWindow* window = (WineWindow*)w;
3657     WineContentView* prev = (WineContentView*)p;
3658     WineContentView* next = (WineContentView*)n;
3660     if (!superview)
3661         superview = [window contentView];
3663     OnMainThreadAsync(^{
3664         if (superview == [view superview])
3665         {
3666             NSArray* subviews = [superview subviews];
3667             NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3668             if (!prev && !next && index == [subviews count] - 1)
3669                 return;
3670             if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3671                 return;
3672             if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3673                 return;
3674         }
3676         WineWindow* oldWindow = (WineWindow*)[view window];
3677         WineWindow* newWindow = (WineWindow*)[superview window];
3679 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3680         if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3681             [view removeFromSuperview];
3682 #endif
3683         if (prev)
3684             [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3685         else
3686             [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3688         if (oldWindow != newWindow)
3689         {
3690             [oldWindow updateForGLSubviews];
3691             [newWindow updateForGLSubviews];
3692         }
3693     });
3695     [pool release];
3698 /***********************************************************************
3699  *              macdrv_set_view_hidden
3700  */
3701 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3703     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3704     WineContentView* view = (WineContentView*)v;
3706     OnMainThreadAsync(^{
3707         [view setHidden:hidden];
3708         [(WineWindow*)view.window updateForGLSubviews];
3709     });
3711     [pool release];
3714 /***********************************************************************
3715  *              macdrv_add_view_opengl_context
3717  * Add an OpenGL context to the list being tracked for each view.
3718  */
3719 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3721     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3722     WineContentView* view = (WineContentView*)v;
3723     WineOpenGLContext *context = (WineOpenGLContext*)c;
3725     OnMainThread(^{
3726         [view addGLContext:context];
3727     });
3729     [pool release];
3732 /***********************************************************************
3733  *              macdrv_remove_view_opengl_context
3735  * Add an OpenGL context to the list being tracked for each view.
3736  */
3737 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3739     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3740     WineContentView* view = (WineContentView*)v;
3741     WineOpenGLContext *context = (WineOpenGLContext*)c;
3743     OnMainThreadAsync(^{
3744         [view removeGLContext:context];
3745     });
3747     [pool release];
3750 #ifdef HAVE_METAL_METAL_H
3751 macdrv_metal_device macdrv_create_metal_device(void)
3753     macdrv_metal_device ret;
3755 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3756     if (MTLCreateSystemDefaultDevice == NULL)
3757         return NULL;
3758 #endif
3760     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3761     ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3762     [pool release];
3763     return ret;
3766 void macdrv_release_metal_device(macdrv_metal_device d)
3768     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3769     [(id<MTLDevice>)d release];
3770     [pool release];
3773 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3775     id<MTLDevice> device = (id<MTLDevice>)d;
3776     WineContentView* view = (WineContentView*)v;
3777     __block WineMetalView *metalView;
3779     OnMainThread(^{
3780         metalView = [view newMetalViewWithDevice:device];
3781     });
3783     return (macdrv_metal_view)metalView;
3786 void macdrv_view_release_metal_view(macdrv_metal_view v)
3788     WineMetalView* view = (WineMetalView*)v;
3789     OnMainThread(^{
3790         [view removeFromSuperview];
3791         [view release];
3792     });
3794 #endif
3796 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3798     WineContentView* view = (WineContentView*)v;
3800     if (![view isKindOfClass:[WineContentView class]])
3801         return FALSE;
3803     [view wine_getBackingSize:backing_size];
3804     return TRUE;
3807 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3809     WineContentView* view = (WineContentView*)v;
3811     if ([view isKindOfClass:[WineContentView class]])
3812         [view wine_setBackingSize:backing_size];
3815 /***********************************************************************
3816  *              macdrv_window_background_color
3818  * Returns the standard Mac window background color as a 32-bit value of
3819  * the form 0x00rrggbb.
3820  */
3821 uint32_t macdrv_window_background_color(void)
3823     static uint32_t result;
3824     static dispatch_once_t once;
3826     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3827     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
3828     // of it is to draw with it.
3829     dispatch_once(&once, ^{
3830         OnMainThread(^{
3831             unsigned char rgbx[4];
3832             unsigned char *planes = rgbx;
3833             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3834                                                                                pixelsWide:1
3835                                                                                pixelsHigh:1
3836                                                                             bitsPerSample:8
3837                                                                           samplesPerPixel:3
3838                                                                                  hasAlpha:NO
3839                                                                                  isPlanar:NO
3840                                                                            colorSpaceName:NSCalibratedRGBColorSpace
3841                                                                              bitmapFormat:0
3842                                                                               bytesPerRow:4
3843                                                                              bitsPerPixel:32];
3844             [NSGraphicsContext saveGraphicsState];
3845             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3846             [[NSColor windowBackgroundColor] set];
3847             NSRectFill(NSMakeRect(0, 0, 1, 1));
3848             [NSGraphicsContext restoreGraphicsState];
3849             [bitmap release];
3850             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3851         });
3852     });
3854     return result;
3857 /***********************************************************************
3858  *              macdrv_send_text_input_event
3859  */
3860 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3862     OnMainThreadAsync(^{
3863         BOOL ret;
3864         macdrv_event* event;
3865         WineWindow* window = (WineWindow*)[NSApp keyWindow];
3866         if (![window isKindOfClass:[WineWindow class]])
3867         {
3868             window = (WineWindow*)[NSApp mainWindow];
3869             if (![window isKindOfClass:[WineWindow class]])
3870                 window = [[WineApplicationController sharedController] frontWineWindow];
3871         }
3873         if (window)
3874         {
3875             NSUInteger localFlags = flags;
3876             CGEventRef c;
3877             NSEvent* event;
3879             window.imeData = data;
3880             fix_device_modifiers_by_generic(&localFlags);
3882             // An NSEvent created with +keyEventWithType:... is internally marked
3883             // as synthetic and doesn't get sent through input methods.  But one
3884             // created from a CGEvent doesn't have that problem.
3885             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3886             CGEventSetFlags(c, localFlags);
3887             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3888             event = [NSEvent eventWithCGEvent:c];
3889             CFRelease(c);
3891             window.commandDone = FALSE;
3892             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3893         }
3894         else
3895             ret = FALSE;
3897         event = macdrv_create_event(SENT_TEXT_INPUT, window);
3898         event->sent_text_input.handled = ret;
3899         event->sent_text_input.done = done;
3900         [[window queue] postEvent:event];
3901         macdrv_release_event(event);
3902     });