d3d11: Rename d3d10_vertex_shader to d3d_vertex_shader.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blob4ac03a77a2ddb7af7e90ac88f25b02a0e3cf9d5c
1 /*
2  * MACDRV Cocoa window code
3  *
4  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
21 #import <Carbon/Carbon.h>
23 #import "cocoa_window.h"
25 #include "macdrv_cocoa.h"
26 #import "cocoa_app.h"
27 #import "cocoa_event.h"
28 #import "cocoa_opengl.h"
31 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
32 enum {
33     NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
34     NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
35     NSWindowFullScreenButton = 7,
36     NSFullScreenWindowMask = 1 << 14,
39 @interface NSWindow (WineFullScreenExtensions)
40     - (void) toggleFullScreen:(id)sender;
41 @end
42 #endif
45 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
46 enum {
47     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
51 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
53     NSUInteger style_mask;
55     if (wf->title_bar)
56     {
57         style_mask = NSTitledWindowMask;
58         if (wf->close_button) style_mask |= NSClosableWindowMask;
59         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
60         if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
61         if (wf->utility) style_mask |= NSUtilityWindowMask;
62     }
63     else style_mask = NSBorderlessWindowMask;
65     return style_mask;
69 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
71     NSScreen* screen;
72     for (screen in screens)
73     {
74         if (NSIntersectsRect(frame, [screen frame]))
75             return TRUE;
76     }
77     return FALSE;
81 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
83     for (NSScreen* screen in screens)
84     {
85         if (NSContainsRect(rect, [screen frame]))
86             return screen;
87     }
88     return nil;
92 /* We rely on the supposedly device-dependent modifier flags to distinguish the
93    keys on the left side of the keyboard from those on the right.  Some event
94    sources don't set those device-depdendent flags.  If we see a device-independent
95    flag for a modifier without either corresponding device-dependent flag, assume
96    the left one. */
97 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
99     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
100         *modifiers |= NX_DEVICELCMDKEYMASK;
101     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
102         *modifiers |= NX_DEVICELSHIFTKEYMASK;
103     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
104         *modifiers |= NX_DEVICELCTLKEYMASK;
105     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
106         *modifiers |= NX_DEVICELALTKEYMASK;
109 /* As we manipulate individual bits of a modifier mask, we can end up with
110    inconsistent sets of flags.  In particular, we might set or clear one of the
111    left/right-specific bits, but not the corresponding non-side-specific bit.
112    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
113    otherwise clear it. */
114 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
116     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
117         *modifiers |= NX_COMMANDMASK;
118     else
119         *modifiers &= ~NX_COMMANDMASK;
120     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
121         *modifiers |= NX_SHIFTMASK;
122     else
123         *modifiers &= ~NX_SHIFTMASK;
124     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
125         *modifiers |= NX_CONTROLMASK;
126     else
127         *modifiers &= ~NX_CONTROLMASK;
128     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
129         *modifiers |= NX_ALTERNATEMASK;
130     else
131         *modifiers &= ~NX_ALTERNATEMASK;
134 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
136     fix_device_modifiers_by_generic(&modifiers);
137     if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
138     {
139         modifiers |= NX_DEVICELCMDKEYMASK;
140         modifiers &= ~NX_DEVICELALTKEYMASK;
141     }
142     if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
143     {
144         modifiers |= NX_DEVICERCMDKEYMASK;
145         modifiers &= ~NX_DEVICERALTKEYMASK;
146     }
147     fix_generic_modifiers_by_device(&modifiers);
149     return modifiers;
153 @interface WineContentView : NSView <NSTextInputClient>
155     NSMutableArray* glContexts;
156     NSMutableArray* pendingGlContexts;
157     BOOL clearedGlSurface;
159     NSMutableAttributedString* markedText;
160     NSRange markedTextSelection;
163     - (void) addGLContext:(WineOpenGLContext*)context;
164     - (void) removeGLContext:(WineOpenGLContext*)context;
165     - (void) updateGLContexts;
167 @end
170 @interface WineWindow ()
172 @property (readwrite, nonatomic) BOOL disabled;
173 @property (readwrite, nonatomic) BOOL noActivate;
174 @property (readwrite, nonatomic) BOOL floating;
175 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
176 @property (retain, nonatomic) NSWindow* latentParentWindow;
178 @property (nonatomic) void* hwnd;
179 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
181 @property (nonatomic) void* surface;
182 @property (nonatomic) pthread_mutex_t* surface_mutex;
184 @property (copy, nonatomic) NSBezierPath* shape;
185 @property (copy, nonatomic) NSData* shapeData;
186 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
187 @property (readonly, nonatomic) BOOL needsTransparency;
189 @property (nonatomic) BOOL colorKeyed;
190 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
191 @property (nonatomic) BOOL usePerPixelAlpha;
193 @property (assign, nonatomic) void* imeData;
194 @property (nonatomic) BOOL commandDone;
196 @property (retain, nonatomic) NSTimer* liveResizeDisplayTimer;
198 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
200     - (void) updateColorSpace;
201     - (void) updateForGLSubviews;
203     - (BOOL) becameEligibleParentOrChild;
204     - (void) becameIneligibleChild;
206 @end
209 @implementation WineContentView
211     - (void) dealloc
212     {
213         [markedText release];
214         [glContexts release];
215         [pendingGlContexts release];
216         [super dealloc];
217     }
219     - (BOOL) isFlipped
220     {
221         return YES;
222     }
224     - (void) drawRect:(NSRect)rect
225     {
226         WineWindow* window = (WineWindow*)[self window];
228         for (WineOpenGLContext* context in pendingGlContexts)
229         {
230             if (!clearedGlSurface)
231             {
232                 context.shouldClearToBlack = TRUE;
233                 clearedGlSurface = TRUE;
234             }
235             context.needsUpdate = TRUE;
236         }
237         [glContexts addObjectsFromArray:pendingGlContexts];
238         [pendingGlContexts removeAllObjects];
240         if ([window contentView] != self)
241             return;
243         if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
244         {
245             [[NSColor clearColor] setFill];
246             NSRectFill(rect);
248             [window.shape addClip];
250             [[NSColor windowBackgroundColor] setFill];
251             NSRectFill(rect);
252         }
254         if (window.surface && window.surface_mutex &&
255             !pthread_mutex_lock(window.surface_mutex))
256         {
257             const CGRect* rects;
258             int count;
260             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
261             {
262                 CGContextRef context;
263                 int i;
265                 [window.shape addClip];
267                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
268                 CGContextSetBlendMode(context, kCGBlendModeCopy);
269                 CGContextSetInterpolationQuality(context, kCGInterpolationNone);
271                 for (i = 0; i < count; i++)
272                 {
273                     CGRect imageRect;
274                     CGImageRef image;
276                     imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
277                     image = create_surface_image(window.surface, &imageRect, FALSE);
279                     if (image)
280                     {
281                         if (window.colorKeyed)
282                         {
283                             CGImageRef maskedImage;
284                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
285                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
286                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
287                             maskedImage = CGImageCreateWithMaskingColors(image, components);
288                             if (maskedImage)
289                             {
290                                 CGImageRelease(image);
291                                 image = maskedImage;
292                             }
293                         }
295                         CGContextDrawImage(context, imageRect, image);
297                         CGImageRelease(image);
298                     }
299                 }
300             }
302             pthread_mutex_unlock(window.surface_mutex);
303         }
305         // If the window may be transparent, then we have to invalidate the
306         // shadow every time we draw.  Also, if this is the first time we've
307         // drawn since changing from transparent to opaque.
308         if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
309         {
310             window.shapeChangedSinceLastDraw = FALSE;
311             [window invalidateShadow];
312         }
313     }
315     - (void) addGLContext:(WineOpenGLContext*)context
316     {
317         if (!glContexts)
318             glContexts = [[NSMutableArray alloc] init];
319         if (!pendingGlContexts)
320             pendingGlContexts = [[NSMutableArray alloc] init];
322         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
323         {
324             [glContexts addObject:context];
325             if (!clearedGlSurface)
326             {
327                 context.shouldClearToBlack = TRUE;
328                 clearedGlSurface = TRUE;
329             }
330             context.needsUpdate = TRUE;
331         }
332         else
333         {
334             [pendingGlContexts addObject:context];
335             [self setNeedsDisplay:YES];
336         }
338         [(WineWindow*)[self window] updateForGLSubviews];
339     }
341     - (void) removeGLContext:(WineOpenGLContext*)context
342     {
343         [glContexts removeObjectIdenticalTo:context];
344         [pendingGlContexts removeObjectIdenticalTo:context];
345         [(WineWindow*)[self window] updateForGLSubviews];
346     }
348     - (void) updateGLContexts
349     {
350         for (WineOpenGLContext* context in glContexts)
351             context.needsUpdate = TRUE;
352     }
354     - (BOOL) hasGLContext
355     {
356         return [glContexts count] || [pendingGlContexts count];
357     }
359     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
360     {
361         return YES;
362     }
364     - (BOOL) preservesContentDuringLiveResize
365     {
366         // Returning YES from this tells Cocoa to keep our view's content during
367         // a Cocoa-driven resize.  In theory, we're also supposed to override
368         // -setFrameSize: to mark exposed sections as needing redisplay, but
369         // user32 will take care of that in a roundabout way.  This way, we don't
370         // redraw until the window surface is flushed.
371         //
372         // This doesn't do anything when we resize the window ourselves.
373         return YES;
374     }
376     - (BOOL)acceptsFirstResponder
377     {
378         return [[self window] contentView] == self;
379     }
381     - (BOOL) mouseDownCanMoveWindow
382     {
383         return NO;
384     }
386     - (void) completeText:(NSString*)text
387     {
388         macdrv_event* event;
389         WineWindow* window = (WineWindow*)[self window];
391         event = macdrv_create_event(IM_SET_TEXT, window);
392         event->im_set_text.data = [window imeData];
393         event->im_set_text.text = (CFStringRef)[text copy];
394         event->im_set_text.complete = TRUE;
396         [[window queue] postEvent:event];
398         macdrv_release_event(event);
400         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
401         markedTextSelection = NSMakeRange(0, 0);
402         [[self inputContext] discardMarkedText];
403     }
405     - (NSFocusRingType) focusRingType
406     {
407         return NSFocusRingTypeNone;
408     }
410     /*
411      * ---------- NSTextInputClient methods ----------
412      */
413     - (NSTextInputContext*) inputContext
414     {
415         if (!markedText)
416             markedText = [[NSMutableAttributedString alloc] init];
417         return [super inputContext];
418     }
420     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
421     {
422         if ([string isKindOfClass:[NSAttributedString class]])
423             string = [string string];
425         if ([string isKindOfClass:[NSString class]])
426             [self completeText:string];
427     }
429     - (void) doCommandBySelector:(SEL)aSelector
430     {
431         [(WineWindow*)[self window] setCommandDone:TRUE];
432     }
434     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
435     {
436         if ([string isKindOfClass:[NSAttributedString class]])
437             string = [string string];
439         if ([string isKindOfClass:[NSString class]])
440         {
441             macdrv_event* event;
442             WineWindow* window = (WineWindow*)[self window];
444             if (replacementRange.location == NSNotFound)
445                 replacementRange = NSMakeRange(0, [markedText length]);
447             [markedText replaceCharactersInRange:replacementRange withString:string];
448             markedTextSelection = selectedRange;
449             markedTextSelection.location += replacementRange.location;
451             event = macdrv_create_event(IM_SET_TEXT, window);
452             event->im_set_text.data = [window imeData];
453             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
454             event->im_set_text.complete = FALSE;
455             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
457             [[window queue] postEvent:event];
459             macdrv_release_event(event);
461             [[self inputContext] invalidateCharacterCoordinates];
462         }
463     }
465     - (void) unmarkText
466     {
467         [self completeText:nil];
468     }
470     - (NSRange) selectedRange
471     {
472         return markedTextSelection;
473     }
475     - (NSRange) markedRange
476     {
477         NSRange range = NSMakeRange(0, [markedText length]);
478         if (!range.length)
479             range.location = NSNotFound;
480         return range;
481     }
483     - (BOOL) hasMarkedText
484     {
485         return [markedText length] > 0;
486     }
488     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
489     {
490         if (aRange.location >= [markedText length])
491             return nil;
493         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
494         if (actualRange)
495             *actualRange = aRange;
496         return [markedText attributedSubstringFromRange:aRange];
497     }
499     - (NSArray*) validAttributesForMarkedText
500     {
501         return [NSArray array];
502     }
504     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
505     {
506         macdrv_query* query;
507         WineWindow* window = (WineWindow*)[self window];
508         NSRect ret;
510         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
512         query = macdrv_create_query();
513         query->type = QUERY_IME_CHAR_RECT;
514         query->window = (macdrv_window)[window retain];
515         query->ime_char_rect.data = [window imeData];
516         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
518         if ([window.queue query:query timeout:1])
519         {
520             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
521             ret = NSRectFromCGRect(query->ime_char_rect.rect);
522             [[WineApplicationController sharedController] flipRect:&ret];
523         }
524         else
525             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
527         macdrv_release_query(query);
529         if (actualRange)
530             *actualRange = aRange;
531         return ret;
532     }
534     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
535     {
536         return NSNotFound;
537     }
539     - (NSInteger) windowLevel
540     {
541         return [[self window] level];
542     }
544 @end
547 @implementation WineWindow
549     static WineWindow* causing_becomeKeyWindow;
551     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
552     @synthesize surface, surface_mutex;
553     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
554     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
555     @synthesize usePerPixelAlpha;
556     @synthesize imeData, commandDone;
557     @synthesize liveResizeDisplayTimer;
559     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
560                                  windowFrame:(NSRect)window_frame
561                                         hwnd:(void*)hwnd
562                                        queue:(WineEventQueue*)queue
563     {
564         WineWindow* window;
565         WineContentView* contentView;
566         NSTrackingArea* trackingArea;
567         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
569         [[WineApplicationController sharedController] flipRect:&window_frame];
571         window = [[[self alloc] initWithContentRect:window_frame
572                                           styleMask:style_mask_for_features(wf)
573                                             backing:NSBackingStoreBuffered
574                                               defer:YES] autorelease];
576         if (!window) return nil;
578         /* Standardize windows to eliminate differences between titled and
579            borderless windows and between NSWindow and NSPanel. */
580         [window setHidesOnDeactivate:NO];
581         [window setReleasedWhenClosed:NO];
583         [window setOneShot:YES];
584         [window disableCursorRects];
585         [window setShowsResizeIndicator:NO];
586         [window setHasShadow:wf->shadow];
587         [window setAcceptsMouseMovedEvents:YES];
588         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
589         [window setDelegate:window];
590         window.hwnd = hwnd;
591         window.queue = queue;
592         window->savedContentMinSize = NSZeroSize;
593         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
594         window->resizable = wf->resizable;
596         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
597                                                                   (NSString*)kUTTypeContent,
598                                                                   nil]];
600         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
601         if (!contentView)
602             return nil;
603         [contentView setAutoresizesSubviews:NO];
605         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
606            because they give us mouse moves in the background. */
607         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
608                                                      options:(NSTrackingMouseMoved |
609                                                               NSTrackingActiveAlways |
610                                                               NSTrackingInVisibleRect)
611                                                        owner:window
612                                                     userInfo:nil] autorelease];
613         if (!trackingArea)
614             return nil;
615         [contentView addTrackingArea:trackingArea];
617         [window setContentView:contentView];
618         [window setInitialFirstResponder:contentView];
620         [nc addObserver:window
621                selector:@selector(updateFullscreen)
622                    name:NSApplicationDidChangeScreenParametersNotification
623                  object:NSApp];
624         [window updateFullscreen];
626         [nc addObserver:window
627                selector:@selector(applicationWillHide)
628                    name:NSApplicationWillHideNotification
629                  object:NSApp];
630         [nc addObserver:window
631                selector:@selector(applicationDidUnhide)
632                    name:NSApplicationDidUnhideNotification
633                  object:NSApp];
635         return window;
636     }
638     - (void) dealloc
639     {
640         [[NSNotificationCenter defaultCenter] removeObserver:self];
641         [liveResizeDisplayTimer invalidate];
642         [liveResizeDisplayTimer release];
643         [queue release];
644         [latentChildWindows release];
645         [latentParentWindow release];
646         [shape release];
647         [shapeData release];
648         [super dealloc];
649     }
651     - (BOOL) preventResizing
652     {
653         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
654         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
655     }
657     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
658     {
659         if (allow_immovable_windows && (disabled || inMaximized))
660             return NO;
661         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
662             return NO;
663         else
664             return YES;
665     }
667     - (void) adjustFeaturesForState
668     {
669         NSUInteger style = [self styleMask];
671         if (style & NSClosableWindowMask)
672             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
673         if (style & NSMiniaturizableWindowMask)
674             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
675         if (style & NSResizableWindowMask)
676             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
677         if ([self respondsToSelector:@selector(toggleFullScreen:)])
678         {
679             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
680                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
681         }
683         if ([self preventResizing])
684         {
685             NSSize size = [self contentRectForFrameRect:[self frame]].size;
686             [self setContentMinSize:size];
687             [self setContentMaxSize:size];
688         }
689         else
690         {
691             [self setContentMaxSize:savedContentMaxSize];
692             [self setContentMinSize:savedContentMinSize];
693         }
695         if (allow_immovable_windows || cursor_clipping_locks_windows)
696             [self setMovable:[self allowsMovingWithMaximized:maximized]];
697     }
699     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
700     {
701         if ([self respondsToSelector:@selector(toggleFullScreen:)])
702         {
703             NSUInteger style = [self styleMask];
705             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
706                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized)
707             {
708                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
709                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
710             }
711             else
712             {
713                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
714                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
715                 if (style & NSFullScreenWindowMask)
716                     [super toggleFullScreen:nil];
717             }
718         }
720         if (behavior != [self collectionBehavior])
721         {
722             [self setCollectionBehavior:behavior];
723             [self adjustFeaturesForState];
724         }
725     }
727     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
728     {
729         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
730                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
731         NSUInteger currentStyle = [self styleMask];
732         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
734         if (newStyle != currentStyle)
735         {
736             NSString* title = [[[self title] copy] autorelease];
737             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
738             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
739             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
740             {
741                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
742                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
743                 // just NSTitledWindowMask, the window buttons should disappear rather
744                 // than just being disabled.  But they don't.  Similarly in reverse.
745                 // The workaround is to also toggle NSClosableWindowMask at the same time.
746                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
747             }
748             [self setStyleMask:newStyle];
750             // -setStyleMask: resets the firstResponder to the window.  Set it
751             // back to the content view.
752             if ([[self contentView] acceptsFirstResponder])
753                 [self makeFirstResponder:[self contentView]];
755             [self adjustFullScreenBehavior:[self collectionBehavior]];
757             if ([[self title] length] == 0 && [title length] > 0)
758                 [self setTitle:title];
759         }
761         resizable = wf->resizable;
762         [self adjustFeaturesForState];
763         [self setHasShadow:wf->shadow];
764     }
766     // Indicates if the window would be visible if the app were not hidden.
767     - (BOOL) wouldBeVisible
768     {
769         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
770     }
772     - (BOOL) isOrderedIn
773     {
774         return [self wouldBeVisible] || [self isMiniaturized];
775     }
777     - (NSInteger) minimumLevelForActive:(BOOL)active
778     {
779         NSInteger level;
781         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
782                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
783             level = NSFloatingWindowLevel;
784         else
785             level = NSNormalWindowLevel;
787         if (active)
788         {
789             BOOL captured;
791             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
793             if (captured || fullscreen)
794             {
795                 if (captured)
796                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
797                 else
798                     level = NSStatusWindowLevel + 1;
800                 if (self.floating)
801                     level++;
802             }
803         }
805         return level;
806     }
808     - (void) postDidUnminimizeEvent
809     {
810         macdrv_event* event;
812         /* Coalesce events by discarding any previous ones still in the queue. */
813         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
814                                forWindow:self];
816         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
817         [queue postEvent:event];
818         macdrv_release_event(event);
819     }
821     - (void) sendResizeStartQuery
822     {
823         macdrv_query* query = macdrv_create_query();
824         query->type = QUERY_RESIZE_START;
825         query->window = (macdrv_window)[self retain];
827         [self.queue query:query timeout:0.3];
828         macdrv_release_query(query);
829     }
831     - (void) setMacDrvState:(const struct macdrv_window_state*)state
832     {
833         NSWindowCollectionBehavior behavior;
835         self.disabled = state->disabled;
836         self.noActivate = state->no_activate;
838         if (self.floating != state->floating)
839         {
840             self.floating = state->floating;
841             if (state->floating)
842             {
843                 // Became floating.  If child of non-floating window, make that
844                 // relationship latent.
845                 WineWindow* parent = (WineWindow*)[self parentWindow];
846                 if (parent && !parent.floating)
847                     [self becameIneligibleChild];
848             }
849             else
850             {
851                 // Became non-floating.  If parent of floating children, make that
852                 // relationship latent.
853                 WineWindow* child;
854                 for (child in [self childWineWindows])
855                 {
856                     if (child.floating)
857                         [child becameIneligibleChild];
858                 }
859             }
861             // Check our latent relationships.  If floating status was the only
862             // reason they were latent, then make them active.
863             if ([self isVisible])
864                 [self becameEligibleParentOrChild];
866             [[WineApplicationController sharedController] adjustWindowLevels];
867         }
869         if (state->minimized_valid)
870         {
871             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
873             pendingMinimize = FALSE;
874             if (state->minimized && ![self isMiniaturized])
875             {
876                 if ([self wouldBeVisible])
877                 {
878                     if ([self styleMask] & NSFullScreenWindowMask)
879                     {
880                         [self postDidUnminimizeEvent];
881                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
882                     }
883                     else
884                     {
885                         [super miniaturize:nil];
886                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
887                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
888                                    event_mask_for_type(WINDOW_LOST_FOCUS);
889                     }
890                 }
891                 else
892                     pendingMinimize = TRUE;
893             }
894             else if (!state->minimized && [self isMiniaturized])
895             {
896                 ignore_windowDeminiaturize = TRUE;
897                 [self deminiaturize:nil];
898                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
899             }
901             if (discard)
902                 [queue discardEventsMatchingMask:discard forWindow:self];
903         }
905         if (state->maximized != maximized)
906         {
907             maximized = state->maximized;
908             [self adjustFeaturesForState];
910             if (!maximized && [self inLiveResize])
911                 [self sendResizeStartQuery];
912         }
914         behavior = NSWindowCollectionBehaviorDefault;
915         if (state->excluded_by_expose)
916             behavior |= NSWindowCollectionBehaviorTransient;
917         else
918             behavior |= NSWindowCollectionBehaviorManaged;
919         if (state->excluded_by_cycle)
920         {
921             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
922             if ([self isOrderedIn])
923                 [NSApp removeWindowsItem:self];
924         }
925         else
926         {
927             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
928             if ([self isOrderedIn])
929                 [NSApp addWindowsItem:self title:[self title] filename:NO];
930         }
931         [self adjustFullScreenBehavior:behavior];
932     }
934     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
935     {
936         BOOL reordered = FALSE;
938         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
939         {
940             if ([self level] > [child level])
941                 [child setLevel:[self level]];
942             [self addChildWindow:child ordered:NSWindowAbove];
943             [latentChildWindows removeObjectIdenticalTo:child];
944             child.latentParentWindow = nil;
945             reordered = TRUE;
946         }
947         else
948         {
949             if (!latentChildWindows)
950                 latentChildWindows = [[NSMutableArray alloc] init];
951             if (![latentChildWindows containsObject:child])
952                 [latentChildWindows addObject:child];
953             child.latentParentWindow = self;
954         }
956         return reordered;
957     }
959     - (BOOL) addChildWineWindow:(WineWindow*)child
960     {
961         return [self addChildWineWindow:child assumeVisible:FALSE];
962     }
964     - (void) removeChildWineWindow:(WineWindow*)child
965     {
966         [self removeChildWindow:child];
967         if (child.latentParentWindow == self)
968             child.latentParentWindow = nil;
969         [latentChildWindows removeObjectIdenticalTo:child];
970     }
972     - (BOOL) becameEligibleParentOrChild
973     {
974         BOOL reordered = FALSE;
975         NSUInteger count;
977         if (latentParentWindow.floating || !self.floating)
978         {
979             // If we aren't visible currently, we assume that we should be and soon
980             // will be.  So, if the latent parent is visible that's enough to assume
981             // we can establish the parent-child relationship in Cocoa.  That will
982             // actually make us visible, which is fine.
983             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
984                 reordered = TRUE;
985         }
987         // Here, though, we may not actually be visible yet and adding a child
988         // won't make us visible.  The caller will have to call this method
989         // again after actually making us visible.
990         if ([self isVisible] && (count = [latentChildWindows count]))
991         {
992             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
993             NSUInteger i;
995             for (i = 0; i < count; i++)
996             {
997                 WineWindow* child = [latentChildWindows objectAtIndex:i];
998                 if ([child isVisible] && (self.floating || !child.floating))
999                 {
1000                     if (child.latentParentWindow == self)
1001                     {
1002                         if ([self level] > [child level])
1003                             [child setLevel:[self level]];
1004                         [self addChildWindow:child ordered:NSWindowAbove];
1005                         child.latentParentWindow = nil;
1006                         reordered = TRUE;
1007                     }
1008                     else
1009                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1010                     [indexesToRemove addIndex:i];
1011                 }
1012             }
1014             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1015         }
1017         return reordered;
1018     }
1020     - (void) becameIneligibleChild
1021     {
1022         WineWindow* parent = (WineWindow*)[self parentWindow];
1023         if (parent)
1024         {
1025             if (!parent->latentChildWindows)
1026                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1027             [parent->latentChildWindows insertObject:self atIndex:0];
1028             self.latentParentWindow = parent;
1029             [parent removeChildWindow:self];
1030         }
1031     }
1033     - (void) becameIneligibleParentOrChild
1034     {
1035         NSArray* childWindows = [self childWineWindows];
1037         [self becameIneligibleChild];
1039         if ([childWindows count])
1040         {
1041             WineWindow* child;
1043             for (child in childWindows)
1044             {
1045                 child.latentParentWindow = self;
1046                 [self removeChildWindow:child];
1047             }
1049             if (latentChildWindows)
1050                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1051             else
1052                 latentChildWindows = [childWindows mutableCopy];
1053         }
1054     }
1056     // Determine if, among Wine windows, this window is directly above or below
1057     // a given other Wine window with no other Wine window intervening.
1058     // Intervening non-Wine windows are ignored.
1059     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1060     {
1061         NSNumber* windowNumber;
1062         NSNumber* otherWindowNumber;
1063         NSArray* windowNumbers;
1064         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1066         if (![self isVisible] || ![otherWindow isVisible])
1067             return FALSE;
1069         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1070         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1071         windowNumbers = [[self class] windowNumbersWithOptions:0];
1072         windowIndex = [windowNumbers indexOfObject:windowNumber];
1073         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1075         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1076             return FALSE;
1078         if (orderingMode == NSWindowAbove)
1079         {
1080             lowIndex = windowIndex;
1081             highIndex = otherWindowIndex;
1082         }
1083         else if (orderingMode == NSWindowBelow)
1084         {
1085             lowIndex = otherWindowIndex;
1086             highIndex = windowIndex;
1087         }
1088         else
1089             return FALSE;
1091         if (highIndex <= lowIndex)
1092             return FALSE;
1094         for (i = lowIndex + 1; i < highIndex; i++)
1095         {
1096             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1097             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1098             if ([interveningWindow isKindOfClass:[WineWindow class]])
1099                 return FALSE;
1100         }
1102         return TRUE;
1103     }
1105     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1106     {
1107         NSMutableArray* windowNumbers;
1108         NSNumber* childWindowNumber;
1109         NSUInteger otherIndex, limit;
1110         NSArray* origChildren;
1111         NSMutableArray* children;
1113         // Get the z-order from the window server and modify it to reflect the
1114         // requested window ordering.
1115         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1116         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1117         [windowNumbers removeObject:childWindowNumber];
1118         otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1119         [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1121         // Get our child windows and sort them in the reverse of the desired
1122         // z-order (back-to-front).
1123         origChildren = [self childWineWindows];
1124         children = [[origChildren mutableCopy] autorelease];
1125         [children sortWithOptions:NSSortStable
1126                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1127             NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1128             NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1129             NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1130             NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1131             if (index1 == NSNotFound)
1132             {
1133                 if (index2 == NSNotFound)
1134                     return NSOrderedSame;
1135                 else
1136                     return NSOrderedAscending;
1137             }
1138             else if (index2 == NSNotFound)
1139                 return NSOrderedDescending;
1140             else if (index1 < index2)
1141                 return NSOrderedDescending;
1142             else if (index2 < index1)
1143                 return NSOrderedAscending;
1145             return NSOrderedSame;
1146         }];
1148         // If the current and desired children arrays match up to a point, leave
1149         // those matching children alone.
1150         limit = MIN([origChildren count], [children count]);
1151         for (otherIndex = 0; otherIndex < limit; otherIndex++)
1152         {
1153             if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1154                 break;
1155         }
1156         [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1158         // Remove all of the child windows and re-add them back-to-front so they
1159         // are in the desired order.
1160         for (other in children)
1161             [self removeChildWindow:other];
1162         for (other in children)
1163             [self addChildWindow:other ordered:NSWindowAbove];
1164     }
1166     /* Returns whether or not the window was ordered in, which depends on if
1167        its frame intersects any screen. */
1168     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1169     {
1170         WineApplicationController* controller = [WineApplicationController sharedController];
1171         if (![self isMiniaturized])
1172         {
1173             BOOL needAdjustWindowLevels = FALSE;
1174             BOOL wasVisible;
1176             [controller transformProcessToForeground];
1177             [NSApp unhide:nil];
1178             wasVisible = [self isVisible];
1180             if (activate)
1181                 [NSApp activateIgnoringOtherApps:YES];
1183             NSDisableScreenUpdates();
1185             if ([self becameEligibleParentOrChild])
1186                 needAdjustWindowLevels = TRUE;
1188             if (prev || next)
1189             {
1190                 WineWindow* other = [prev isVisible] ? prev : next;
1191                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1193                 if (![self isOrdered:orderingMode relativeTo:other])
1194                 {
1195                     WineWindow* parent = (WineWindow*)[self parentWindow];
1196                     WineWindow* otherParent = (WineWindow*)[other parentWindow];
1198                     // This window level may not be right for this window based
1199                     // on floating-ness, fullscreen-ness, etc.  But we set it
1200                     // temporarily to allow us to order the windows properly.
1201                     // Then the levels get fixed by -adjustWindowLevels.
1202                     if ([self level] != [other level])
1203                         [self setLevel:[other level]];
1204                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1206                     // The above call to -[NSWindow orderWindow:relativeTo:] won't
1207                     // reorder windows which are both children of the same parent
1208                     // relative to each other, so do that separately.
1209                     if (parent && parent == otherParent)
1210                         [parent order:orderingMode childWindow:self relativeTo:other];
1212                     needAdjustWindowLevels = TRUE;
1213                 }
1214             }
1215             else
1216             {
1217                 // Again, temporarily set level to make sure we can order to
1218                 // the right place.
1219                 next = [controller frontWineWindow];
1220                 if (next && [self level] < [next level])
1221                     [self setLevel:[next level]];
1222                 [self orderFront:nil];
1223                 needAdjustWindowLevels = TRUE;
1224             }
1226             if ([self becameEligibleParentOrChild])
1227                 needAdjustWindowLevels = TRUE;
1229             if (needAdjustWindowLevels)
1230             {
1231                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1232                     [controller updateFullscreenWindows];
1233                 [controller adjustWindowLevels];
1234             }
1236             if (pendingMinimize)
1237             {
1238                 [super miniaturize:nil];
1239                 pendingMinimize = FALSE;
1240             }
1242             NSEnableScreenUpdates();
1244             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1245                Generate a frame-changed event just in case.  The back end will ignore
1246                it if nothing actually changed. */
1247             [self windowDidResize:nil];
1249             if (![self isExcludedFromWindowsMenu])
1250                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1251         }
1252     }
1254     - (void) doOrderOut
1255     {
1256         WineApplicationController* controller = [WineApplicationController sharedController];
1257         BOOL wasVisible = [self isVisible];
1258         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1260         if ([self isMiniaturized])
1261             pendingMinimize = TRUE;
1263         [self becameIneligibleParentOrChild];
1264         if ([self isMiniaturized])
1265         {
1266             fakingClose = TRUE;
1267             [self close];
1268             fakingClose = FALSE;
1269         }
1270         else
1271             [self orderOut:nil];
1272         savedVisibleState = FALSE;
1273         if (wasVisible && wasOnActiveSpace && fullscreen)
1274             [controller updateFullscreenWindows];
1275         [controller adjustWindowLevels];
1276         [NSApp removeWindowsItem:self];
1278         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1279                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1280                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1281                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1282                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1283                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1284                                forWindow:self];
1285     }
1287     - (void) updateFullscreen
1288     {
1289         NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1290         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1292         if (nowFullscreen != fullscreen)
1293         {
1294             WineApplicationController* controller = [WineApplicationController sharedController];
1296             fullscreen = nowFullscreen;
1297             if ([self isVisible] && [self isOnActiveSpace])
1298                 [controller updateFullscreenWindows];
1300             [controller adjustWindowLevels];
1301         }
1302     }
1304     - (void) setFrameFromWine:(NSRect)contentRect
1305     {
1306         /* Origin is (left, top) in a top-down space.  Need to convert it to
1307            (left, bottom) in a bottom-up space. */
1308         [[WineApplicationController sharedController] flipRect:&contentRect];
1310         /* The back end is establishing a new window size and position.  It's
1311            not interested in any stale events regarding those that may be sitting
1312            in the queue. */
1313         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1314                                forWindow:self];
1316         if (!NSIsEmptyRect(contentRect))
1317         {
1318             NSRect frame, oldFrame;
1320             oldFrame = [self frame];
1321             frame = [self frameRectForContentRect:contentRect];
1322             if (!NSEqualRects(frame, oldFrame))
1323             {
1324                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1325                 BOOL needEnableScreenUpdates = FALSE;
1327                 if ([self preventResizing])
1328                 {
1329                     // Allow the following calls to -setFrame:display: to work even
1330                     // if they would violate the content size constraints. This
1331                     // shouldn't be necessary since the content size constraints are
1332                     // documented to not constrain that method, but it seems to be.
1333                     [self setContentMinSize:NSZeroSize];
1334                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1335                 }
1337                 if (equalSizes && [[self childWineWindows] count])
1338                 {
1339                     // If we change the window frame such that the origin moves
1340                     // but the size doesn't change, then Cocoa moves child
1341                     // windows with the parent.  We don't want that so we fake
1342                     // a change of the size and then change it back.
1343                     NSRect bogusFrame = frame;
1344                     bogusFrame.size.width++;
1346                     NSDisableScreenUpdates();
1347                     needEnableScreenUpdates = TRUE;
1349                     ignore_windowResize = TRUE;
1350                     [self setFrame:bogusFrame display:NO];
1351                     ignore_windowResize = FALSE;
1352                 }
1354                 [self setFrame:frame display:YES];
1355                 if ([self preventResizing])
1356                 {
1357                     [self setContentMinSize:contentRect.size];
1358                     [self setContentMaxSize:contentRect.size];
1359                 }
1361                 if (needEnableScreenUpdates)
1362                     NSEnableScreenUpdates();
1364                 if (!equalSizes)
1365                     [self updateColorSpace];
1367                 if (!enteringFullScreen &&
1368                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1369                     nonFullscreenFrame = frame;
1371                 [self updateFullscreen];
1373                 if ([self isOrderedIn])
1374                 {
1375                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1376                        event.  The back end will ignore it if nothing actually changed. */
1377                     [self windowDidResize:nil];
1378                 }
1379             }
1380         }
1381     }
1383     - (void) setMacDrvParentWindow:(WineWindow*)parent
1384     {
1385         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1386         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1387         {
1388             [oldParent removeChildWineWindow:self];
1389             [latentParentWindow removeChildWineWindow:self];
1390             if ([parent addChildWineWindow:self])
1391                 [[WineApplicationController sharedController] adjustWindowLevels];
1392         }
1393     }
1395     - (void) setDisabled:(BOOL)newValue
1396     {
1397         if (disabled != newValue)
1398         {
1399             disabled = newValue;
1400             [self adjustFeaturesForState];
1401         }
1402     }
1404     - (BOOL) needsTransparency
1405     {
1406         return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1407                 (gl_surface_mode == GL_SURFACE_BEHIND && [[self.contentView valueForKeyPath:@"subviews.@max.hasGLContext"] boolValue]);
1408     }
1410     - (void) checkTransparency
1411     {
1412         if (![self isOpaque] && !self.needsTransparency)
1413         {
1414             self.shapeChangedSinceLastDraw = TRUE;
1415             [[self contentView] setNeedsDisplay:YES];
1416             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1417             [self setOpaque:YES];
1418         }
1419         else if ([self isOpaque] && self.needsTransparency)
1420         {
1421             self.shapeChangedSinceLastDraw = TRUE;
1422             [[self contentView] setNeedsDisplay:YES];
1423             [self setBackgroundColor:[NSColor clearColor]];
1424             [self setOpaque:NO];
1425         }
1426     }
1428     - (void) setShape:(NSBezierPath*)newShape
1429     {
1430         if (shape == newShape) return;
1432         if (shape)
1433         {
1434             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1435             [shape release];
1436         }
1437         if (newShape)
1438             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1440         shape = [newShape copy];
1441         self.shapeChangedSinceLastDraw = TRUE;
1443         [self checkTransparency];
1444     }
1446     - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1447     {
1448         if (newTimer != liveResizeDisplayTimer)
1449         {
1450             [liveResizeDisplayTimer invalidate];
1451             [liveResizeDisplayTimer release];
1452             liveResizeDisplayTimer = [newTimer retain];
1453         }
1454     }
1456     - (void) makeFocused:(BOOL)activate
1457     {
1458         if (activate)
1459         {
1460             [[WineApplicationController sharedController] transformProcessToForeground];
1461             [NSApp activateIgnoringOtherApps:YES];
1462         }
1464         causing_becomeKeyWindow = self;
1465         [self makeKeyWindow];
1466         causing_becomeKeyWindow = nil;
1468         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1469                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1470                                forWindow:self];
1471     }
1473     - (void) postKey:(uint16_t)keyCode
1474              pressed:(BOOL)pressed
1475            modifiers:(NSUInteger)modifiers
1476                event:(NSEvent*)theEvent
1477     {
1478         macdrv_event* event;
1479         CGEventRef cgevent;
1480         WineApplicationController* controller = [WineApplicationController sharedController];
1482         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1483         event->key.keycode   = keyCode;
1484         event->key.modifiers = modifiers;
1485         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1487         if ((cgevent = [theEvent CGEvent]))
1488         {
1489             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1490                                                         kCGKeyboardEventKeyboardType);
1491             if (keyboardType != controller.keyboardType)
1492             {
1493                 controller.keyboardType = keyboardType;
1494                 [controller keyboardSelectionDidChange];
1495             }
1496         }
1498         [queue postEvent:event];
1500         macdrv_release_event(event);
1502         [controller noteKey:keyCode pressed:pressed];
1503     }
1505     - (void) postKeyEvent:(NSEvent *)theEvent
1506     {
1507         [self flagsChanged:theEvent];
1508         [self postKey:[theEvent keyCode]
1509               pressed:[theEvent type] == NSKeyDown
1510             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1511                 event:theEvent];
1512     }
1514     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1515     {
1516         savedContentMinSize = minSize;
1517         savedContentMaxSize = maxSize;
1518         if (![self preventResizing])
1519         {
1520             [self setContentMinSize:minSize];
1521             [self setContentMaxSize:maxSize];
1522         }
1523     }
1525     - (WineWindow*) ancestorWineWindow
1526     {
1527         WineWindow* ancestor = self;
1528         for (;;)
1529         {
1530             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1531             if ([parent isKindOfClass:[WineWindow class]])
1532                 ancestor = parent;
1533             else
1534                 break;
1535         }
1536         return ancestor;
1537     }
1539     - (void) postBroughtForwardEvent
1540     {
1541         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1542         [queue postEvent:event];
1543         macdrv_release_event(event);
1544     }
1546     - (void) updateForCursorClipping
1547     {
1548         [self adjustFeaturesForState];
1549     }
1551     - (void) endWindowDragging
1552     {
1553         if (draggingPhase)
1554         {
1555             if (draggingPhase == 3)
1556             {
1557                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
1558                 [queue postEvent:event];
1559                 macdrv_release_event(event);
1560             }
1562             draggingPhase = 0;
1563             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
1564         }
1565     }
1568     /*
1569      * ---------- NSWindow method overrides ----------
1570      */
1571     - (BOOL) canBecomeKeyWindow
1572     {
1573         if (causing_becomeKeyWindow == self) return YES;
1574         if (self.disabled || self.noActivate) return NO;
1575         return [self isKeyWindow];
1576     }
1578     - (BOOL) canBecomeMainWindow
1579     {
1580         return [self canBecomeKeyWindow];
1581     }
1583     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1584     {
1585         // If a window is sized to completely cover a screen, then it's in
1586         // full-screen mode.  In that case, we don't allow NSWindow to constrain
1587         // it.
1588         NSArray* screens = [NSScreen screens];
1589         NSRect contentRect = [self contentRectForFrameRect:frameRect];
1590         if (!screen_covered_by_rect(contentRect, screens) &&
1591             frame_intersects_screens(frameRect, screens))
1592             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1593         return frameRect;
1594     }
1596     - (BOOL) isExcludedFromWindowsMenu
1597     {
1598         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1599     }
1601     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1602     {
1603         BOOL ret = [super validateMenuItem:menuItem];
1605         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1606             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1607         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
1608             ret = NO;
1610         return ret;
1611     }
1613     /* We don't call this.  It's the action method of the items in the Window menu. */
1614     - (void) makeKeyAndOrderFront:(id)sender
1615     {
1616         if ([self isMiniaturized])
1617             [self deminiaturize:nil];
1618         [self orderBelow:nil orAbove:nil activate:NO];
1619         [[self ancestorWineWindow] postBroughtForwardEvent];
1621         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1622             [[WineApplicationController sharedController] windowGotFocus:self];
1623     }
1625     - (void) sendEvent:(NSEvent*)event
1626     {
1627         NSEventType type = event.type;
1629         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1630            interface control.  For example, Control-Tab switches focus among
1631            views.  We want to bypass that feature, so directly route key-down
1632            events to -keyDown:. */
1633         if (type == NSKeyDown)
1634             [[self firstResponder] keyDown:event];
1635         else
1636         {
1637             if (!draggingPhase && maximized && ![self isMovable] &&
1638                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
1639                 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
1640             {
1641                 NSRect titleBar = self.frame;
1642                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
1643                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
1644                 titleBar.origin.y = NSMaxY(contentRect);
1646                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
1648                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
1649                 {
1650                     static const NSWindowButton buttons[] = {
1651                         NSWindowCloseButton,
1652                         NSWindowMiniaturizeButton,
1653                         NSWindowZoomButton,
1654                         NSWindowFullScreenButton,
1655                     };
1656                     BOOL hitButton = NO;
1657                     int i;
1659                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
1660                     {
1661                         NSButton* button;
1663                         if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
1664                             continue;
1666                         button = [self standardWindowButton:buttons[i]];
1667                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
1668                         {
1669                             hitButton = YES;
1670                             break;
1671                         }
1672                     }
1674                     if (!hitButton)
1675                     {
1676                         draggingPhase = 1;
1677                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
1678                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
1679                     }
1680                 }
1681             }
1682             else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
1683             {
1684                 if ([self isMovable])
1685                 {
1686                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
1687                     NSPoint newTopLeft = dragWindowStartPosition;
1689                     newTopLeft.x += point.x - dragStartPosition.x;
1690                     newTopLeft.y += point.y - dragStartPosition.y;
1692                     if (draggingPhase == 2)
1693                     {
1694                         macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
1695                         [queue postEvent:event];
1696                         macdrv_release_event(event);
1698                         draggingPhase = 3;
1699                     }
1701                     [self setFrameTopLeftPoint:newTopLeft];
1702                 }
1703                 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
1704                 {
1705                     macdrv_event* event;
1706                     NSRect frame = [self contentRectForFrameRect:self.frame];
1708                     [[WineApplicationController sharedController] flipRect:&frame];
1710                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1711                     event->window_restore_requested.keep_frame = TRUE;
1712                     event->window_restore_requested.frame = NSRectToCGRect(frame);
1713                     [queue postEvent:event];
1714                     macdrv_release_event(event);
1716                     draggingPhase = 2;
1717                 }
1719                 if (type == NSLeftMouseUp)
1720                     [self endWindowDragging];
1721             }
1723             [super sendEvent:event];
1724         }
1725     }
1727     - (void) miniaturize:(id)sender
1728     {
1729         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1730         [queue postEvent:event];
1731         macdrv_release_event(event);
1732     }
1734     - (void) toggleFullScreen:(id)sender
1735     {
1736         if (!self.disabled && !maximized)
1737             [super toggleFullScreen:sender];
1738     }
1740     - (NSArray*) childWineWindows
1741     {
1742         NSArray* childWindows = self.childWindows;
1743         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
1744             return [child isKindOfClass:[WineWindow class]];
1745         }];
1746         return [childWindows objectsAtIndexes:indexes];
1747     }
1749     // We normally use the generic/calibrated RGB color space for the window,
1750     // rather than the device color space, to avoid expensive color conversion
1751     // which slows down drawing.  However, for windows displaying OpenGL, having
1752     // a different color space than the screen greatly reduces frame rates, often
1753     // limiting it to the display refresh rate.
1754     //
1755     // To avoid this, we switch back to the screen color space whenever the
1756     // window is covered by a view with an attached OpenGL context.
1757     - (void) updateColorSpace
1758     {
1759         NSRect contentRect = [[self contentView] frame];
1760         BOOL coveredByGLView = FALSE;
1761         for (WineContentView* view in [[self contentView] subviews])
1762         {
1763             if ([view hasGLContext])
1764             {
1765                 NSRect frame = [view convertRect:[view bounds] toView:nil];
1766                 if (NSContainsRect(frame, contentRect))
1767                 {
1768                     coveredByGLView = TRUE;
1769                     break;
1770                 }
1771             }
1772         }
1774         if (coveredByGLView)
1775             [self setColorSpace:nil];
1776         else
1777             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1778     }
1780     - (void) updateForGLSubviews
1781     {
1782         [self updateColorSpace];
1783         if (gl_surface_mode == GL_SURFACE_BEHIND)
1784             [self checkTransparency];
1785     }
1788     /*
1789      * ---------- NSResponder method overrides ----------
1790      */
1791     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1793     - (void) flagsChanged:(NSEvent *)theEvent
1794     {
1795         static const struct {
1796             NSUInteger  mask;
1797             uint16_t    keycode;
1798         } modifiers[] = {
1799             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
1800             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
1801             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
1802             { NX_DEVICELCTLKEYMASK,     kVK_Control },
1803             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
1804             { NX_DEVICELALTKEYMASK,     kVK_Option },
1805             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
1806             { NX_DEVICELCMDKEYMASK,     kVK_Command },
1807             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
1808         };
1810         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1811         NSUInteger changed;
1812         int i, last_changed;
1814         fix_device_modifiers_by_generic(&modifierFlags);
1815         changed = modifierFlags ^ lastModifierFlags;
1817         last_changed = -1;
1818         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1819             if (changed & modifiers[i].mask)
1820                 last_changed = i;
1822         for (i = 0; i <= last_changed; i++)
1823         {
1824             if (changed & modifiers[i].mask)
1825             {
1826                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1828                 if (i == last_changed)
1829                     lastModifierFlags = modifierFlags;
1830                 else
1831                 {
1832                     lastModifierFlags ^= modifiers[i].mask;
1833                     fix_generic_modifiers_by_device(&lastModifierFlags);
1834                 }
1836                 // Caps lock generates one event for each press-release action.
1837                 // We need to simulate a pair of events for each actual event.
1838                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1839                 {
1840                     [self postKey:modifiers[i].keycode
1841                           pressed:TRUE
1842                         modifiers:lastModifierFlags
1843                             event:(NSEvent*)theEvent];
1844                     pressed = FALSE;
1845                 }
1847                 [self postKey:modifiers[i].keycode
1848                       pressed:pressed
1849                     modifiers:lastModifierFlags
1850                         event:(NSEvent*)theEvent];
1851             }
1852         }
1853     }
1855     - (void) applicationWillHide
1856     {
1857         savedVisibleState = [self isVisible];
1858     }
1860     - (void) applicationDidUnhide
1861     {
1862         if ([self isVisible])
1863             [self becameEligibleParentOrChild];
1864     }
1867     /*
1868      * ---------- NSWindowDelegate methods ----------
1869      */
1870     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1871     {
1872         macdrv_query* query;
1873         NSSize size;
1875         query = macdrv_create_query();
1876         query->type = QUERY_MIN_MAX_INFO;
1877         query->window = (macdrv_window)[self retain];
1878         [self.queue query:query timeout:0.5];
1879         macdrv_release_query(query);
1881         size = [self contentMaxSize];
1882         if (proposedSize.width < size.width)
1883             size.width = proposedSize.width;
1884         if (proposedSize.height < size.height)
1885             size.height = proposedSize.height;
1886         return size;
1887     }
1889     - (void)windowDidBecomeKey:(NSNotification *)notification
1890     {
1891         WineApplicationController* controller = [WineApplicationController sharedController];
1892         NSEvent* event = [controller lastFlagsChanged];
1893         if (event)
1894             [self flagsChanged:event];
1896         if (causing_becomeKeyWindow == self) return;
1898         [controller windowGotFocus:self];
1899     }
1901     - (void)windowDidDeminiaturize:(NSNotification *)notification
1902     {
1903         WineApplicationController* controller = [WineApplicationController sharedController];
1905         if (!ignore_windowDeminiaturize)
1906             [self postDidUnminimizeEvent];
1907         ignore_windowDeminiaturize = FALSE;
1909         [self becameEligibleParentOrChild];
1911         if (fullscreen && [self isOnActiveSpace])
1912             [controller updateFullscreenWindows];
1913         [controller adjustWindowLevels];
1915         if (![self parentWindow])
1916             [self postBroughtForwardEvent];
1918         if (!self.disabled && !self.noActivate)
1919         {
1920             causing_becomeKeyWindow = self;
1921             [self makeKeyWindow];
1922             causing_becomeKeyWindow = nil;
1923             [controller windowGotFocus:self];
1924         }
1926         [self windowDidResize:notification];
1927     }
1929     - (void) windowDidEndLiveResize:(NSNotification *)notification
1930     {
1931         if (!maximized)
1932         {
1933             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1934             [queue postEvent:event];
1935             macdrv_release_event(event);
1936         }
1938         self.liveResizeDisplayTimer = nil;
1939     }
1941     - (void) windowDidEnterFullScreen:(NSNotification*)notification
1942     {
1943         enteringFullScreen = FALSE;
1944         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1945     }
1947     - (void) windowDidExitFullScreen:(NSNotification*)notification
1948     {
1949         exitingFullScreen = FALSE;
1950         [self setFrame:nonFullscreenFrame display:YES animate:NO];
1951         [self windowDidResize:nil];
1952     }
1954     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1955     {
1956         enteringFullScreen = FALSE;
1957         enteredFullScreenTime = 0;
1958     }
1960     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1961     {
1962         exitingFullScreen = FALSE;
1963         [self windowDidResize:nil];
1964     }
1966     - (void)windowDidMiniaturize:(NSNotification *)notification
1967     {
1968         if (fullscreen && [self isOnActiveSpace])
1969             [[WineApplicationController sharedController] updateFullscreenWindows];
1970     }
1972     - (void)windowDidMove:(NSNotification *)notification
1973     {
1974         [self windowDidResize:notification];
1975     }
1977     - (void)windowDidResignKey:(NSNotification *)notification
1978     {
1979         macdrv_event* event;
1981         if (causing_becomeKeyWindow) return;
1983         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1984         [queue postEvent:event];
1985         macdrv_release_event(event);
1986     }
1988     - (void)windowDidResize:(NSNotification *)notification
1989     {
1990         macdrv_event* event;
1991         NSRect frame = [self frame];
1993         if ([self inLiveResize])
1994         {
1995             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1996                 resizingFromLeft = TRUE;
1997             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1998                 resizingFromTop = TRUE;
1999         }
2001         frame = [self contentRectForFrameRect:frame];
2003         if (ignore_windowResize || exitingFullScreen) return;
2005         if ([self preventResizing])
2006         {
2007             [self setContentMinSize:frame.size];
2008             [self setContentMaxSize:frame.size];
2009         }
2011         [[WineApplicationController sharedController] flipRect:&frame];
2013         /* Coalesce events by discarding any previous ones still in the queue. */
2014         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2015                                forWindow:self];
2017         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2018         event->window_frame_changed.frame = NSRectToCGRect(frame);
2019         event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
2020         event->window_frame_changed.in_resize = [self inLiveResize];
2021         [queue postEvent:event];
2022         macdrv_release_event(event);
2024         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2025         [self updateFullscreen];
2026     }
2028     - (BOOL)windowShouldClose:(id)sender
2029     {
2030         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2031         [queue postEvent:event];
2032         macdrv_release_event(event);
2033         return NO;
2034     }
2036     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2037     {
2038         if (maximized)
2039         {
2040             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2041             [queue postEvent:event];
2042             macdrv_release_event(event);
2043             return NO;
2044         }
2045         else if (!resizable)
2046         {
2047             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2048             [queue postEvent:event];
2049             macdrv_release_event(event);
2050             return NO;
2051         }
2053         return YES;
2054     }
2056     - (void) windowWillClose:(NSNotification*)notification
2057     {
2058         WineWindow* child;
2060         if (fakingClose) return;
2061         if (latentParentWindow)
2062         {
2063             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2064             self.latentParentWindow = nil;
2065         }
2067         for (child in latentChildWindows)
2068         {
2069             if (child.latentParentWindow == self)
2070                 child.latentParentWindow = nil;
2071         }
2072         [latentChildWindows removeAllObjects];
2073     }
2075     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2076     {
2077         enteringFullScreen = TRUE;
2078         nonFullscreenFrame = [self frame];
2079     }
2081     - (void) windowWillExitFullScreen:(NSNotification*)notification
2082     {
2083         exitingFullScreen = TRUE;
2084     }
2086     - (void)windowWillMiniaturize:(NSNotification *)notification
2087     {
2088         [self becameIneligibleParentOrChild];
2089     }
2091     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2092     {
2093         if ([self inLiveResize])
2094         {
2095             if (maximized)
2096                 return self.frame.size;
2098             NSRect rect;
2099             macdrv_query* query;
2101             rect = [self frame];
2102             if (resizingFromLeft)
2103                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2104             if (!resizingFromTop)
2105                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2106             rect.size = frameSize;
2107             rect = [self contentRectForFrameRect:rect];
2108             [[WineApplicationController sharedController] flipRect:&rect];
2110             query = macdrv_create_query();
2111             query->type = QUERY_RESIZE_SIZE;
2112             query->window = (macdrv_window)[self retain];
2113             query->resize_size.rect = NSRectToCGRect(rect);
2114             query->resize_size.from_left = resizingFromLeft;
2115             query->resize_size.from_top = resizingFromTop;
2117             if ([self.queue query:query timeout:0.1])
2118             {
2119                 rect = NSRectFromCGRect(query->resize_size.rect);
2120                 rect = [self frameRectForContentRect:rect];
2121                 frameSize = rect.size;
2122             }
2124             macdrv_release_query(query);
2125         }
2127         return frameSize;
2128     }
2130     - (void) windowWillStartLiveResize:(NSNotification *)notification
2131     {
2132         [self endWindowDragging];
2134         if (maximized)
2135         {
2136             macdrv_event* event;
2137             NSRect frame = [self contentRectForFrameRect:self.frame];
2139             [[WineApplicationController sharedController] flipRect:&frame];
2141             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2142             event->window_restore_requested.keep_frame = TRUE;
2143             event->window_restore_requested.frame = NSRectToCGRect(frame);
2144             [queue postEvent:event];
2145             macdrv_release_event(event);
2146         }
2147         else
2148             [self sendResizeStartQuery];
2150         frameAtResizeStart = [self frame];
2151         resizingFromLeft = resizingFromTop = FALSE;
2153         // There's a strange restriction in window redrawing during Cocoa-
2154         // managed window resizing.  Only calls to -[NSView setNeedsDisplay...]
2155         // that happen synchronously when Cocoa tells us that our window size
2156         // has changed or asynchronously in a short interval thereafter provoke
2157         // the window to redraw.  Calls to those methods that happen asynchronously
2158         // a half second or more after the last change of the window size aren't
2159         // heeded until the next resize-related user event (e.g. mouse movement).
2160         //
2161         // Wine often has a significant delay between when it's been told that
2162         // the window has changed size and when it can flush completed drawing.
2163         // So, our windows would get stuck with incomplete drawing for as long
2164         // as the user holds the mouse button down and doesn't move it.
2165         //
2166         // We address this by "manually" asking our windows to check if they need
2167         // redrawing every so often (during live resize only).
2168         self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
2169                                                                        target:self
2170                                                                      selector:@selector(displayIfNeeded)
2171                                                                      userInfo:nil
2172                                                                       repeats:YES];
2173         [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
2174                                      forMode:NSRunLoopCommonModes];
2175     }
2177     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2178     {
2179         macdrv_query* query;
2180         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2181         NSSize maxSize;
2183         query = macdrv_create_query();
2184         query->type = QUERY_MIN_MAX_INFO;
2185         query->window = (macdrv_window)[self retain];
2186         [self.queue query:query timeout:0.5];
2187         macdrv_release_query(query);
2189         currentContentRect = [self contentRectForFrameRect:[self frame]];
2190         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2192         maxSize = [self contentMaxSize];
2193         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2194         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2196         // Try to keep the top-left corner where it is.
2197         newContentRect.origin.x = NSMinX(currentContentRect);
2198         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2200         // If that pushes the bottom or right off the screen, pull it up and to the left.
2201         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2202         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2203             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2204         if (NSMinY(newContentRect) < NSMinY(screenRect))
2205             newContentRect.origin.y = NSMinY(screenRect);
2207         // If that pushes the top or left off the screen, push it down and the right
2208         // again.  Do this last because the top-left corner is more important than the
2209         // bottom-right.
2210         if (NSMinX(newContentRect) < NSMinX(screenRect))
2211             newContentRect.origin.x = NSMinX(screenRect);
2212         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2213             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2215         return [self frameRectForContentRect:newContentRect];
2216     }
2219     /*
2220      * ---------- NSPasteboardOwner methods ----------
2221      */
2222     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2223     {
2224         macdrv_query* query = macdrv_create_query();
2225         query->type = QUERY_PASTEBOARD_DATA;
2226         query->window = (macdrv_window)[self retain];
2227         query->pasteboard_data.type = (CFStringRef)[type copy];
2229         [self.queue query:query timeout:3];
2230         macdrv_release_query(query);
2231     }
2234     /*
2235      * ---------- NSDraggingDestination methods ----------
2236      */
2237     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2238     {
2239         return [self draggingUpdated:sender];
2240     }
2242     - (void) draggingExited:(id <NSDraggingInfo>)sender
2243     {
2244         // This isn't really a query.  We don't need any response.  However, it
2245         // has to be processed in a similar manner as the other drag-and-drop
2246         // queries in order to maintain the proper order of operations.
2247         macdrv_query* query = macdrv_create_query();
2248         query->type = QUERY_DRAG_EXITED;
2249         query->window = (macdrv_window)[self retain];
2251         [self.queue query:query timeout:0.1];
2252         macdrv_release_query(query);
2253     }
2255     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2256     {
2257         NSDragOperation ret;
2258         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2259         NSPasteboard* pb = [sender draggingPasteboard];
2261         macdrv_query* query = macdrv_create_query();
2262         query->type = QUERY_DRAG_OPERATION;
2263         query->window = (macdrv_window)[self retain];
2264         query->drag_operation.x = pt.x;
2265         query->drag_operation.y = pt.y;
2266         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2267         query->drag_operation.accepted_op = NSDragOperationNone;
2268         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2270         [self.queue query:query timeout:3];
2271         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2272         macdrv_release_query(query);
2274         return ret;
2275     }
2277     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2278     {
2279         BOOL ret;
2280         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2281         NSPasteboard* pb = [sender draggingPasteboard];
2283         macdrv_query* query = macdrv_create_query();
2284         query->type = QUERY_DRAG_DROP;
2285         query->window = (macdrv_window)[self retain];
2286         query->drag_drop.x = pt.x;
2287         query->drag_drop.y = pt.y;
2288         query->drag_drop.op = [sender draggingSourceOperationMask];
2289         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2291         [self.queue query:query timeout:3 * 60 processEvents:YES];
2292         ret = query->status;
2293         macdrv_release_query(query);
2295         return ret;
2296     }
2298     - (BOOL) wantsPeriodicDraggingUpdates
2299     {
2300         return NO;
2301     }
2303 @end
2306 /***********************************************************************
2307  *              macdrv_create_cocoa_window
2309  * Create a Cocoa window with the given content frame and features (e.g.
2310  * title bar, close box, etc.).
2311  */
2312 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2313         CGRect frame, void* hwnd, macdrv_event_queue queue)
2315     __block WineWindow* window;
2317     OnMainThread(^{
2318         window = [[WineWindow createWindowWithFeatures:wf
2319                                            windowFrame:NSRectFromCGRect(frame)
2320                                                   hwnd:hwnd
2321                                                  queue:(WineEventQueue*)queue] retain];
2322     });
2324     return (macdrv_window)window;
2327 /***********************************************************************
2328  *              macdrv_destroy_cocoa_window
2330  * Destroy a Cocoa window.
2331  */
2332 void macdrv_destroy_cocoa_window(macdrv_window w)
2334     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2335     WineWindow* window = (WineWindow*)w;
2337     OnMainThread(^{
2338         [window doOrderOut];
2339         [window close];
2340     });
2341     [window.queue discardEventsMatchingMask:-1 forWindow:window];
2342     [window release];
2344     [pool release];
2347 /***********************************************************************
2348  *              macdrv_get_window_hwnd
2350  * Get the hwnd that was set for the window at creation.
2351  */
2352 void* macdrv_get_window_hwnd(macdrv_window w)
2354     WineWindow* window = (WineWindow*)w;
2355     return window.hwnd;
2358 /***********************************************************************
2359  *              macdrv_set_cocoa_window_features
2361  * Update a Cocoa window's features.
2362  */
2363 void macdrv_set_cocoa_window_features(macdrv_window w,
2364         const struct macdrv_window_features* wf)
2366     WineWindow* window = (WineWindow*)w;
2368     OnMainThread(^{
2369         [window setWindowFeatures:wf];
2370     });
2373 /***********************************************************************
2374  *              macdrv_set_cocoa_window_state
2376  * Update a Cocoa window's state.
2377  */
2378 void macdrv_set_cocoa_window_state(macdrv_window w,
2379         const struct macdrv_window_state* state)
2381     WineWindow* window = (WineWindow*)w;
2383     OnMainThread(^{
2384         [window setMacDrvState:state];
2385     });
2388 /***********************************************************************
2389  *              macdrv_set_cocoa_window_title
2391  * Set a Cocoa window's title.
2392  */
2393 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2394         size_t length)
2396     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2397     WineWindow* window = (WineWindow*)w;
2398     NSString* titleString;
2400     if (title)
2401         titleString = [NSString stringWithCharacters:title length:length];
2402     else
2403         titleString = @"";
2404     OnMainThreadAsync(^{
2405         [window setTitle:titleString];
2406         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2407             [NSApp changeWindowsItem:window title:titleString filename:NO];
2408     });
2410     [pool release];
2413 /***********************************************************************
2414  *              macdrv_order_cocoa_window
2416  * Reorder a Cocoa window relative to other windows.  If prev is
2417  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
2418  * it is ordered above that window.  Otherwise, it is ordered to the
2419  * front.
2420  */
2421 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2422         macdrv_window n, int activate)
2424     WineWindow* window = (WineWindow*)w;
2425     WineWindow* prev = (WineWindow*)p;
2426     WineWindow* next = (WineWindow*)n;
2428     OnMainThreadAsync(^{
2429         [window orderBelow:prev
2430                    orAbove:next
2431                   activate:activate];
2432     });
2433     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2434                                   forWindow:window];
2435     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2436                                 forWindow:next];
2439 /***********************************************************************
2440  *              macdrv_hide_cocoa_window
2442  * Hides a Cocoa window.
2443  */
2444 void macdrv_hide_cocoa_window(macdrv_window w)
2446     WineWindow* window = (WineWindow*)w;
2448     OnMainThread(^{
2449         [window doOrderOut];
2450     });
2453 /***********************************************************************
2454  *              macdrv_set_cocoa_window_frame
2456  * Move a Cocoa window.
2457  */
2458 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2460     WineWindow* window = (WineWindow*)w;
2462     OnMainThread(^{
2463         [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2464     });
2467 /***********************************************************************
2468  *              macdrv_get_cocoa_window_frame
2470  * Gets the frame of a Cocoa window.
2471  */
2472 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2474     WineWindow* window = (WineWindow*)w;
2476     OnMainThread(^{
2477         NSRect frame;
2479         frame = [window contentRectForFrameRect:[window frame]];
2480         [[WineApplicationController sharedController] flipRect:&frame];
2481         *out_frame = NSRectToCGRect(frame);
2482     });
2485 /***********************************************************************
2486  *              macdrv_set_cocoa_parent_window
2488  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
2489  * the parent window.
2490  */
2491 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2493     WineWindow* window = (WineWindow*)w;
2495     OnMainThread(^{
2496         [window setMacDrvParentWindow:(WineWindow*)parent];
2497     });
2500 /***********************************************************************
2501  *              macdrv_set_window_surface
2502  */
2503 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2505     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2506     WineWindow* window = (WineWindow*)w;
2508     OnMainThread(^{
2509         window.surface = surface;
2510         window.surface_mutex = mutex;
2511     });
2513     [pool release];
2516 /***********************************************************************
2517  *              macdrv_window_needs_display
2519  * Mark a window as needing display in a specified rect (in non-client
2520  * area coordinates).
2521  */
2522 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2524     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2525     WineWindow* window = (WineWindow*)w;
2527     OnMainThreadAsync(^{
2528         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2529     });
2531     [pool release];
2534 /***********************************************************************
2535  *              macdrv_set_window_shape
2537  * Sets the shape of a Cocoa window from an array of rectangles.  If
2538  * rects is NULL, resets the window's shape to its frame.
2539  */
2540 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2542     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2543     WineWindow* window = (WineWindow*)w;
2545     OnMainThread(^{
2546         if (!rects || !count)
2547         {
2548             window.shape = nil;
2549             window.shapeData = nil;
2550         }
2551         else
2552         {
2553             size_t length = sizeof(*rects) * count;
2554             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
2555             {
2556                 NSBezierPath* path;
2557                 unsigned int i;
2559                 path = [NSBezierPath bezierPath];
2560                 for (i = 0; i < count; i++)
2561                     [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2562                 window.shape = path;
2563                 window.shapeData = [NSData dataWithBytes:rects length:length];
2564             }
2565         }
2566     });
2568     [pool release];
2571 /***********************************************************************
2572  *              macdrv_set_window_alpha
2573  */
2574 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2576     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2577     WineWindow* window = (WineWindow*)w;
2579     [window setAlphaValue:alpha];
2581     [pool release];
2584 /***********************************************************************
2585  *              macdrv_set_window_color_key
2586  */
2587 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2588                                  CGFloat keyBlue)
2590     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2591     WineWindow* window = (WineWindow*)w;
2593     OnMainThread(^{
2594         window.colorKeyed       = TRUE;
2595         window.colorKeyRed      = keyRed;
2596         window.colorKeyGreen    = keyGreen;
2597         window.colorKeyBlue     = keyBlue;
2598         [window checkTransparency];
2599     });
2601     [pool release];
2604 /***********************************************************************
2605  *              macdrv_clear_window_color_key
2606  */
2607 void macdrv_clear_window_color_key(macdrv_window w)
2609     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2610     WineWindow* window = (WineWindow*)w;
2612     OnMainThread(^{
2613         window.colorKeyed = FALSE;
2614         [window checkTransparency];
2615     });
2617     [pool release];
2620 /***********************************************************************
2621  *              macdrv_window_use_per_pixel_alpha
2622  */
2623 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2625     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2626     WineWindow* window = (WineWindow*)w;
2628     OnMainThread(^{
2629         window.usePerPixelAlpha = use_per_pixel_alpha;
2630         [window checkTransparency];
2631     });
2633     [pool release];
2636 /***********************************************************************
2637  *              macdrv_give_cocoa_window_focus
2639  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
2640  * orders it front and, if its frame was not within the desktop bounds,
2641  * Cocoa will typically move it on-screen.
2642  */
2643 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2645     WineWindow* window = (WineWindow*)w;
2647     OnMainThread(^{
2648         [window makeFocused:activate];
2649     });
2652 /***********************************************************************
2653  *              macdrv_set_window_min_max_sizes
2655  * Sets the window's minimum and maximum content sizes.
2656  */
2657 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2659     WineWindow* window = (WineWindow*)w;
2661     OnMainThread(^{
2662         [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2663     });
2666 /***********************************************************************
2667  *              macdrv_create_view
2669  * Creates and returns a view in the specified rect of the window.  The
2670  * caller is responsible for calling macdrv_dispose_view() on the view
2671  * when it is done with it.
2672  */
2673 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2675     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2676     WineWindow* window = (WineWindow*)w;
2677     __block WineContentView* view;
2679     if (CGRectIsNull(rect)) rect = CGRectZero;
2681     OnMainThread(^{
2682         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2684         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2685         [view setAutoresizesSubviews:NO];
2686         [nc addObserver:view
2687                selector:@selector(updateGLContexts)
2688                    name:NSViewGlobalFrameDidChangeNotification
2689                  object:view];
2690         [nc addObserver:view
2691                selector:@selector(updateGLContexts)
2692                    name:NSApplicationDidChangeScreenParametersNotification
2693                  object:NSApp];
2694         [[window contentView] addSubview:view];
2695         [window updateForGLSubviews];
2696     });
2698     [pool release];
2699     return (macdrv_view)view;
2702 /***********************************************************************
2703  *              macdrv_dispose_view
2705  * Destroys a view previously returned by macdrv_create_view.
2706  */
2707 void macdrv_dispose_view(macdrv_view v)
2709     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2710     WineContentView* view = (WineContentView*)v;
2712     OnMainThread(^{
2713         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2714         WineWindow* window = (WineWindow*)[view window];
2716         [nc removeObserver:view
2717                       name:NSViewGlobalFrameDidChangeNotification
2718                     object:view];
2719         [nc removeObserver:view
2720                       name:NSApplicationDidChangeScreenParametersNotification
2721                     object:NSApp];
2722         [view removeFromSuperview];
2723         [view release];
2724         [window updateForGLSubviews];
2725     });
2727     [pool release];
2730 /***********************************************************************
2731  *              macdrv_set_view_window_and_frame
2733  * Move a view to a new window and/or position within its window.  If w
2734  * is NULL, leave the view in its current window and just change its
2735  * frame.
2736  */
2737 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2739     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2740     WineContentView* view = (WineContentView*)v;
2741     WineWindow* window = (WineWindow*)w;
2743     if (CGRectIsNull(rect)) rect = CGRectZero;
2745     OnMainThread(^{
2746         BOOL changedWindow = (window && window != [view window]);
2747         NSRect newFrame = NSRectFromCGRect(rect);
2748         NSRect oldFrame = [view frame];
2749         BOOL needUpdateWindowForGLSubviews = FALSE;
2751         if (changedWindow)
2752         {
2753             WineWindow* oldWindow = (WineWindow*)[view window];
2754             [view removeFromSuperview];
2755             [oldWindow updateForGLSubviews];
2756             [[window contentView] addSubview:view];
2757             needUpdateWindowForGLSubviews = TRUE;
2758         }
2760         if (!NSEqualRects(oldFrame, newFrame))
2761         {
2762             if (!changedWindow)
2763                 [[view superview] setNeedsDisplayInRect:oldFrame];
2764             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2765                 [view setFrameSize:newFrame.size];
2766             else if (NSEqualSizes(oldFrame.size, newFrame.size))
2767                 [view setFrameOrigin:newFrame.origin];
2768             else
2769                 [view setFrame:newFrame];
2770             [view setNeedsDisplay:YES];
2771             needUpdateWindowForGLSubviews = TRUE;
2772         }
2774         if (needUpdateWindowForGLSubviews)
2775             [(WineWindow*)[view window] updateForGLSubviews];
2776     });
2778     [pool release];
2781 /***********************************************************************
2782  *              macdrv_add_view_opengl_context
2784  * Add an OpenGL context to the list being tracked for each view.
2785  */
2786 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2788     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2789     WineContentView* view = (WineContentView*)v;
2790     WineOpenGLContext *context = (WineOpenGLContext*)c;
2792     OnMainThread(^{
2793         [view addGLContext:context];
2794     });
2796     [pool release];
2799 /***********************************************************************
2800  *              macdrv_remove_view_opengl_context
2802  * Add an OpenGL context to the list being tracked for each view.
2803  */
2804 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2806     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2807     WineContentView* view = (WineContentView*)v;
2808     WineOpenGLContext *context = (WineOpenGLContext*)c;
2810     OnMainThreadAsync(^{
2811         [view removeGLContext:context];
2812     });
2814     [pool release];
2817 /***********************************************************************
2818  *              macdrv_window_background_color
2820  * Returns the standard Mac window background color as a 32-bit value of
2821  * the form 0x00rrggbb.
2822  */
2823 uint32_t macdrv_window_background_color(void)
2825     static uint32_t result;
2826     static dispatch_once_t once;
2828     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2829     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
2830     // of it is to draw with it.
2831     dispatch_once(&once, ^{
2832         OnMainThread(^{
2833             unsigned char rgbx[4];
2834             unsigned char *planes = rgbx;
2835             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2836                                                                                pixelsWide:1
2837                                                                                pixelsHigh:1
2838                                                                             bitsPerSample:8
2839                                                                           samplesPerPixel:3
2840                                                                                  hasAlpha:NO
2841                                                                                  isPlanar:NO
2842                                                                            colorSpaceName:NSCalibratedRGBColorSpace
2843                                                                              bitmapFormat:0
2844                                                                               bytesPerRow:4
2845                                                                              bitsPerPixel:32];
2846             [NSGraphicsContext saveGraphicsState];
2847             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2848             [[NSColor windowBackgroundColor] set];
2849             NSRectFill(NSMakeRect(0, 0, 1, 1));
2850             [NSGraphicsContext restoreGraphicsState];
2851             [bitmap release];
2852             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2853         });
2854     });
2856     return result;
2859 /***********************************************************************
2860  *              macdrv_send_text_input_event
2861  */
2862 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2864     __block BOOL ret;
2866     OnMainThread(^{
2867         WineWindow* window = (WineWindow*)[NSApp keyWindow];
2868         if (![window isKindOfClass:[WineWindow class]])
2869         {
2870             window = (WineWindow*)[NSApp mainWindow];
2871             if (![window isKindOfClass:[WineWindow class]])
2872                 window = [[WineApplicationController sharedController] frontWineWindow];
2873         }
2875         if (window)
2876         {
2877             NSUInteger localFlags = flags;
2878             CGEventRef c;
2879             NSEvent* event;
2881             window.imeData = data;
2882             fix_device_modifiers_by_generic(&localFlags);
2884             // An NSEvent created with +keyEventWithType:... is internally marked
2885             // as synthetic and doesn't get sent through input methods.  But one
2886             // created from a CGEvent doesn't have that problem.
2887             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2888             CGEventSetFlags(c, localFlags);
2889             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2890             event = [NSEvent eventWithCGEvent:c];
2891             CFRelease(c);
2893             window.commandDone = FALSE;
2894             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
2895         }
2896         else
2897             ret = FALSE;
2898     });
2900     return ret;