include: Add event trace flags and guid to evntrace.h.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blobf68a1c56f78fc002b40e84cd32978abb1da80bf4
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;
202     - (BOOL) becameEligibleParentOrChild;
203     - (void) becameIneligibleChild;
205 @end
208 @implementation WineContentView
210     - (void) dealloc
211     {
212         [markedText release];
213         [glContexts release];
214         [pendingGlContexts release];
215         [super dealloc];
216     }
218     - (BOOL) isFlipped
219     {
220         return YES;
221     }
223     - (void) drawRect:(NSRect)rect
224     {
225         WineWindow* window = (WineWindow*)[self window];
227         for (WineOpenGLContext* context in pendingGlContexts)
228         {
229             if (!clearedGlSurface)
230             {
231                 context.shouldClearToBlack = TRUE;
232                 clearedGlSurface = TRUE;
233             }
234             context.needsUpdate = TRUE;
235         }
236         [glContexts addObjectsFromArray:pendingGlContexts];
237         [pendingGlContexts removeAllObjects];
239         if ([window contentView] != self)
240             return;
242         if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
243         {
244             [[NSColor clearColor] setFill];
245             NSRectFill(rect);
247             [window.shape addClip];
249             [[NSColor windowBackgroundColor] setFill];
250             NSRectFill(rect);
251         }
253         if (window.surface && window.surface_mutex &&
254             !pthread_mutex_lock(window.surface_mutex))
255         {
256             const CGRect* rects;
257             int count;
259             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
260             {
261                 CGContextRef context;
262                 int i;
264                 [window.shape addClip];
266                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
267                 CGContextSetBlendMode(context, kCGBlendModeCopy);
268                 CGContextSetInterpolationQuality(context, kCGInterpolationNone);
270                 for (i = 0; i < count; i++)
271                 {
272                     CGRect imageRect;
273                     CGImageRef image;
275                     imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
276                     image = create_surface_image(window.surface, &imageRect, FALSE);
278                     if (image)
279                     {
280                         if (window.colorKeyed)
281                         {
282                             CGImageRef maskedImage;
283                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
284                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
285                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
286                             maskedImage = CGImageCreateWithMaskingColors(image, components);
287                             if (maskedImage)
288                             {
289                                 CGImageRelease(image);
290                                 image = maskedImage;
291                             }
292                         }
294                         CGContextDrawImage(context, imageRect, image);
296                         CGImageRelease(image);
297                     }
298                 }
299             }
301             pthread_mutex_unlock(window.surface_mutex);
302         }
304         // If the window may be transparent, then we have to invalidate the
305         // shadow every time we draw.  Also, if this is the first time we've
306         // drawn since changing from transparent to opaque.
307         if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
308         {
309             window.shapeChangedSinceLastDraw = FALSE;
310             [window invalidateShadow];
311         }
312     }
314     - (void) addGLContext:(WineOpenGLContext*)context
315     {
316         if (!glContexts)
317             glContexts = [[NSMutableArray alloc] init];
318         if (!pendingGlContexts)
319             pendingGlContexts = [[NSMutableArray alloc] init];
321         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
322         {
323             [glContexts addObject:context];
324             if (!clearedGlSurface)
325             {
326                 context.shouldClearToBlack = TRUE;
327                 clearedGlSurface = TRUE;
328             }
329             context.needsUpdate = TRUE;
330         }
331         else
332         {
333             [pendingGlContexts addObject:context];
334             [self setNeedsDisplay:YES];
335         }
337         [(WineWindow*)[self window] updateColorSpace];
338     }
340     - (void) removeGLContext:(WineOpenGLContext*)context
341     {
342         [glContexts removeObjectIdenticalTo:context];
343         [pendingGlContexts removeObjectIdenticalTo:context];
344         [(WineWindow*)[self window] updateColorSpace];
345     }
347     - (void) updateGLContexts
348     {
349         for (WineOpenGLContext* context in glContexts)
350             context.needsUpdate = TRUE;
351     }
353     - (BOOL) hasGLContext
354     {
355         return [glContexts count] || [pendingGlContexts count];
356     }
358     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
359     {
360         return YES;
361     }
363     - (BOOL) preservesContentDuringLiveResize
364     {
365         // Returning YES from this tells Cocoa to keep our view's content during
366         // a Cocoa-driven resize.  In theory, we're also supposed to override
367         // -setFrameSize: to mark exposed sections as needing redisplay, but
368         // user32 will take care of that in a roundabout way.  This way, we don't
369         // redraw until the window surface is flushed.
370         //
371         // This doesn't do anything when we resize the window ourselves.
372         return YES;
373     }
375     - (BOOL)acceptsFirstResponder
376     {
377         return [[self window] contentView] == self;
378     }
380     - (BOOL) mouseDownCanMoveWindow
381     {
382         return NO;
383     }
385     - (void) completeText:(NSString*)text
386     {
387         macdrv_event* event;
388         WineWindow* window = (WineWindow*)[self window];
390         event = macdrv_create_event(IM_SET_TEXT, window);
391         event->im_set_text.data = [window imeData];
392         event->im_set_text.text = (CFStringRef)[text copy];
393         event->im_set_text.complete = TRUE;
395         [[window queue] postEvent:event];
397         macdrv_release_event(event);
399         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
400         markedTextSelection = NSMakeRange(0, 0);
401         [[self inputContext] discardMarkedText];
402     }
404     - (NSFocusRingType) focusRingType
405     {
406         return NSFocusRingTypeNone;
407     }
409     /*
410      * ---------- NSTextInputClient methods ----------
411      */
412     - (NSTextInputContext*) inputContext
413     {
414         if (!markedText)
415             markedText = [[NSMutableAttributedString alloc] init];
416         return [super inputContext];
417     }
419     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
420     {
421         if ([string isKindOfClass:[NSAttributedString class]])
422             string = [string string];
424         if ([string isKindOfClass:[NSString class]])
425             [self completeText:string];
426     }
428     - (void) doCommandBySelector:(SEL)aSelector
429     {
430         [(WineWindow*)[self window] setCommandDone:TRUE];
431     }
433     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
434     {
435         if ([string isKindOfClass:[NSAttributedString class]])
436             string = [string string];
438         if ([string isKindOfClass:[NSString class]])
439         {
440             macdrv_event* event;
441             WineWindow* window = (WineWindow*)[self window];
443             if (replacementRange.location == NSNotFound)
444                 replacementRange = NSMakeRange(0, [markedText length]);
446             [markedText replaceCharactersInRange:replacementRange withString:string];
447             markedTextSelection = selectedRange;
448             markedTextSelection.location += replacementRange.location;
450             event = macdrv_create_event(IM_SET_TEXT, window);
451             event->im_set_text.data = [window imeData];
452             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
453             event->im_set_text.complete = FALSE;
454             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
456             [[window queue] postEvent:event];
458             macdrv_release_event(event);
460             [[self inputContext] invalidateCharacterCoordinates];
461         }
462     }
464     - (void) unmarkText
465     {
466         [self completeText:nil];
467     }
469     - (NSRange) selectedRange
470     {
471         return markedTextSelection;
472     }
474     - (NSRange) markedRange
475     {
476         NSRange range = NSMakeRange(0, [markedText length]);
477         if (!range.length)
478             range.location = NSNotFound;
479         return range;
480     }
482     - (BOOL) hasMarkedText
483     {
484         return [markedText length] > 0;
485     }
487     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
488     {
489         if (aRange.location >= [markedText length])
490             return nil;
492         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
493         if (actualRange)
494             *actualRange = aRange;
495         return [markedText attributedSubstringFromRange:aRange];
496     }
498     - (NSArray*) validAttributesForMarkedText
499     {
500         return [NSArray array];
501     }
503     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
504     {
505         macdrv_query* query;
506         WineWindow* window = (WineWindow*)[self window];
507         NSRect ret;
509         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
511         query = macdrv_create_query();
512         query->type = QUERY_IME_CHAR_RECT;
513         query->window = (macdrv_window)[window retain];
514         query->ime_char_rect.data = [window imeData];
515         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
517         if ([window.queue query:query timeout:1])
518         {
519             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
520             ret = NSRectFromCGRect(query->ime_char_rect.rect);
521             [[WineApplicationController sharedController] flipRect:&ret];
522         }
523         else
524             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
526         macdrv_release_query(query);
528         if (actualRange)
529             *actualRange = aRange;
530         return ret;
531     }
533     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
534     {
535         return NSNotFound;
536     }
538     - (NSInteger) windowLevel
539     {
540         return [[self window] level];
541     }
543 @end
546 @implementation WineWindow
548     static WineWindow* causing_becomeKeyWindow;
550     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
551     @synthesize surface, surface_mutex;
552     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
553     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
554     @synthesize usePerPixelAlpha;
555     @synthesize imeData, commandDone;
556     @synthesize liveResizeDisplayTimer;
558     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
559                                  windowFrame:(NSRect)window_frame
560                                         hwnd:(void*)hwnd
561                                        queue:(WineEventQueue*)queue
562     {
563         WineWindow* window;
564         WineContentView* contentView;
565         NSTrackingArea* trackingArea;
566         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
568         [[WineApplicationController sharedController] flipRect:&window_frame];
570         window = [[[self alloc] initWithContentRect:window_frame
571                                           styleMask:style_mask_for_features(wf)
572                                             backing:NSBackingStoreBuffered
573                                               defer:YES] autorelease];
575         if (!window) return nil;
577         /* Standardize windows to eliminate differences between titled and
578            borderless windows and between NSWindow and NSPanel. */
579         [window setHidesOnDeactivate:NO];
580         [window setReleasedWhenClosed:NO];
582         [window setOneShot:YES];
583         [window disableCursorRects];
584         [window setShowsResizeIndicator:NO];
585         [window setHasShadow:wf->shadow];
586         [window setAcceptsMouseMovedEvents:YES];
587         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
588         [window setDelegate:window];
589         window.hwnd = hwnd;
590         window.queue = queue;
591         window->savedContentMinSize = NSZeroSize;
592         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
593         window->resizable = wf->resizable;
595         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
596                                                                   (NSString*)kUTTypeContent,
597                                                                   nil]];
599         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
600         if (!contentView)
601             return nil;
602         [contentView setAutoresizesSubviews:NO];
604         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
605            because they give us mouse moves in the background. */
606         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
607                                                      options:(NSTrackingMouseMoved |
608                                                               NSTrackingActiveAlways |
609                                                               NSTrackingInVisibleRect)
610                                                        owner:window
611                                                     userInfo:nil] autorelease];
612         if (!trackingArea)
613             return nil;
614         [contentView addTrackingArea:trackingArea];
616         [window setContentView:contentView];
617         [window setInitialFirstResponder:contentView];
619         [nc addObserver:window
620                selector:@selector(updateFullscreen)
621                    name:NSApplicationDidChangeScreenParametersNotification
622                  object:NSApp];
623         [window updateFullscreen];
625         [nc addObserver:window
626                selector:@selector(applicationWillHide)
627                    name:NSApplicationWillHideNotification
628                  object:NSApp];
629         [nc addObserver:window
630                selector:@selector(applicationDidUnhide)
631                    name:NSApplicationDidUnhideNotification
632                  object:NSApp];
634         return window;
635     }
637     - (void) dealloc
638     {
639         [[NSNotificationCenter defaultCenter] removeObserver:self];
640         [liveResizeDisplayTimer invalidate];
641         [liveResizeDisplayTimer release];
642         [queue release];
643         [latentChildWindows release];
644         [latentParentWindow release];
645         [shape release];
646         [shapeData release];
647         [super dealloc];
648     }
650     - (BOOL) preventResizing
651     {
652         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
653         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
654     }
656     - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
657     {
658         if (allow_immovable_windows && (disabled || inMaximized))
659             return NO;
660         else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
661             return NO;
662         else
663             return YES;
664     }
666     - (void) adjustFeaturesForState
667     {
668         NSUInteger style = [self styleMask];
670         if (style & NSClosableWindowMask)
671             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
672         if (style & NSMiniaturizableWindowMask)
673             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
674         if (style & NSResizableWindowMask)
675             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
676         if ([self respondsToSelector:@selector(toggleFullScreen:)])
677         {
678             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
679                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
680         }
682         if ([self preventResizing])
683         {
684             NSSize size = [self contentRectForFrameRect:[self frame]].size;
685             [self setContentMinSize:size];
686             [self setContentMaxSize:size];
687         }
688         else
689         {
690             [self setContentMaxSize:savedContentMaxSize];
691             [self setContentMinSize:savedContentMinSize];
692         }
694         if (allow_immovable_windows || cursor_clipping_locks_windows)
695             [self setMovable:[self allowsMovingWithMaximized:maximized]];
696     }
698     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
699     {
700         if ([self respondsToSelector:@selector(toggleFullScreen:)])
701         {
702             NSUInteger style = [self styleMask];
704             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
705                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized)
706             {
707                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
708                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
709             }
710             else
711             {
712                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
713                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
714                 if (style & NSFullScreenWindowMask)
715                     [super toggleFullScreen:nil];
716             }
717         }
719         if (behavior != [self collectionBehavior])
720         {
721             [self setCollectionBehavior:behavior];
722             [self adjustFeaturesForState];
723         }
724     }
726     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
727     {
728         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
729                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
730         NSUInteger currentStyle = [self styleMask];
731         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
733         if (newStyle != currentStyle)
734         {
735             NSString* title = [[[self title] copy] autorelease];
736             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
737             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
738             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
739             {
740                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
741                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
742                 // just NSTitledWindowMask, the window buttons should disappear rather
743                 // than just being disabled.  But they don't.  Similarly in reverse.
744                 // The workaround is to also toggle NSClosableWindowMask at the same time.
745                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
746             }
747             [self setStyleMask:newStyle];
749             // -setStyleMask: resets the firstResponder to the window.  Set it
750             // back to the content view.
751             if ([[self contentView] acceptsFirstResponder])
752                 [self makeFirstResponder:[self contentView]];
754             [self adjustFullScreenBehavior:[self collectionBehavior]];
756             if ([[self title] length] == 0 && [title length] > 0)
757                 [self setTitle:title];
758         }
760         resizable = wf->resizable;
761         [self adjustFeaturesForState];
762         [self setHasShadow:wf->shadow];
763     }
765     // Indicates if the window would be visible if the app were not hidden.
766     - (BOOL) wouldBeVisible
767     {
768         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
769     }
771     - (BOOL) isOrderedIn
772     {
773         return [self wouldBeVisible] || [self isMiniaturized];
774     }
776     - (NSInteger) minimumLevelForActive:(BOOL)active
777     {
778         NSInteger level;
780         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
781                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
782             level = NSFloatingWindowLevel;
783         else
784             level = NSNormalWindowLevel;
786         if (active)
787         {
788             BOOL captured;
790             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
792             if (captured || fullscreen)
793             {
794                 if (captured)
795                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
796                 else
797                     level = NSStatusWindowLevel + 1;
799                 if (self.floating)
800                     level++;
801             }
802         }
804         return level;
805     }
807     - (void) postDidUnminimizeEvent
808     {
809         macdrv_event* event;
811         /* Coalesce events by discarding any previous ones still in the queue. */
812         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
813                                forWindow:self];
815         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
816         [queue postEvent:event];
817         macdrv_release_event(event);
818     }
820     - (void) sendResizeStartQuery
821     {
822         macdrv_query* query = macdrv_create_query();
823         query->type = QUERY_RESIZE_START;
824         query->window = (macdrv_window)[self retain];
826         [self.queue query:query timeout:0.3];
827         macdrv_release_query(query);
828     }
830     - (void) setMacDrvState:(const struct macdrv_window_state*)state
831     {
832         NSWindowCollectionBehavior behavior;
834         self.disabled = state->disabled;
835         self.noActivate = state->no_activate;
837         if (self.floating != state->floating)
838         {
839             self.floating = state->floating;
840             if (state->floating)
841             {
842                 // Became floating.  If child of non-floating window, make that
843                 // relationship latent.
844                 WineWindow* parent = (WineWindow*)[self parentWindow];
845                 if (parent && !parent.floating)
846                     [self becameIneligibleChild];
847             }
848             else
849             {
850                 // Became non-floating.  If parent of floating children, make that
851                 // relationship latent.
852                 WineWindow* child;
853                 for (child in [self childWineWindows])
854                 {
855                     if (child.floating)
856                         [child becameIneligibleChild];
857                 }
858             }
860             // Check our latent relationships.  If floating status was the only
861             // reason they were latent, then make them active.
862             if ([self isVisible])
863                 [self becameEligibleParentOrChild];
865             [[WineApplicationController sharedController] adjustWindowLevels];
866         }
868         if (state->minimized_valid)
869         {
870             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
872             pendingMinimize = FALSE;
873             if (state->minimized && ![self isMiniaturized])
874             {
875                 if ([self wouldBeVisible])
876                 {
877                     if ([self styleMask] & NSFullScreenWindowMask)
878                     {
879                         [self postDidUnminimizeEvent];
880                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
881                     }
882                     else
883                     {
884                         [super miniaturize:nil];
885                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
886                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
887                                    event_mask_for_type(WINDOW_LOST_FOCUS);
888                     }
889                 }
890                 else
891                     pendingMinimize = TRUE;
892             }
893             else if (!state->minimized && [self isMiniaturized])
894             {
895                 ignore_windowDeminiaturize = TRUE;
896                 [self deminiaturize:nil];
897                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
898             }
900             if (discard)
901                 [queue discardEventsMatchingMask:discard forWindow:self];
902         }
904         if (state->maximized != maximized)
905         {
906             maximized = state->maximized;
907             [self adjustFeaturesForState];
909             if (!maximized && [self inLiveResize])
910                 [self sendResizeStartQuery];
911         }
913         behavior = NSWindowCollectionBehaviorDefault;
914         if (state->excluded_by_expose)
915             behavior |= NSWindowCollectionBehaviorTransient;
916         else
917             behavior |= NSWindowCollectionBehaviorManaged;
918         if (state->excluded_by_cycle)
919         {
920             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
921             if ([self isOrderedIn])
922                 [NSApp removeWindowsItem:self];
923         }
924         else
925         {
926             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
927             if ([self isOrderedIn])
928                 [NSApp addWindowsItem:self title:[self title] filename:NO];
929         }
930         [self adjustFullScreenBehavior:behavior];
931     }
933     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
934     {
935         BOOL reordered = FALSE;
937         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
938         {
939             if ([self level] > [child level])
940                 [child setLevel:[self level]];
941             [self addChildWindow:child ordered:NSWindowAbove];
942             [latentChildWindows removeObjectIdenticalTo:child];
943             child.latentParentWindow = nil;
944             reordered = TRUE;
945         }
946         else
947         {
948             if (!latentChildWindows)
949                 latentChildWindows = [[NSMutableArray alloc] init];
950             if (![latentChildWindows containsObject:child])
951                 [latentChildWindows addObject:child];
952             child.latentParentWindow = self;
953         }
955         return reordered;
956     }
958     - (BOOL) addChildWineWindow:(WineWindow*)child
959     {
960         return [self addChildWineWindow:child assumeVisible:FALSE];
961     }
963     - (void) removeChildWineWindow:(WineWindow*)child
964     {
965         [self removeChildWindow:child];
966         if (child.latentParentWindow == self)
967             child.latentParentWindow = nil;
968         [latentChildWindows removeObjectIdenticalTo:child];
969     }
971     - (BOOL) becameEligibleParentOrChild
972     {
973         BOOL reordered = FALSE;
974         NSUInteger count;
976         if (latentParentWindow.floating || !self.floating)
977         {
978             // If we aren't visible currently, we assume that we should be and soon
979             // will be.  So, if the latent parent is visible that's enough to assume
980             // we can establish the parent-child relationship in Cocoa.  That will
981             // actually make us visible, which is fine.
982             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
983                 reordered = TRUE;
984         }
986         // Here, though, we may not actually be visible yet and adding a child
987         // won't make us visible.  The caller will have to call this method
988         // again after actually making us visible.
989         if ([self isVisible] && (count = [latentChildWindows count]))
990         {
991             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
992             NSUInteger i;
994             for (i = 0; i < count; i++)
995             {
996                 WineWindow* child = [latentChildWindows objectAtIndex:i];
997                 if ([child isVisible] && (self.floating || !child.floating))
998                 {
999                     if (child.latentParentWindow == self)
1000                     {
1001                         if ([self level] > [child level])
1002                             [child setLevel:[self level]];
1003                         [self addChildWindow:child ordered:NSWindowAbove];
1004                         child.latentParentWindow = nil;
1005                         reordered = TRUE;
1006                     }
1007                     else
1008                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1009                     [indexesToRemove addIndex:i];
1010                 }
1011             }
1013             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1014         }
1016         return reordered;
1017     }
1019     - (void) becameIneligibleChild
1020     {
1021         WineWindow* parent = (WineWindow*)[self parentWindow];
1022         if (parent)
1023         {
1024             if (!parent->latentChildWindows)
1025                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1026             [parent->latentChildWindows insertObject:self atIndex:0];
1027             self.latentParentWindow = parent;
1028             [parent removeChildWindow:self];
1029         }
1030     }
1032     - (void) becameIneligibleParentOrChild
1033     {
1034         NSArray* childWindows = [self childWineWindows];
1036         [self becameIneligibleChild];
1038         if ([childWindows count])
1039         {
1040             WineWindow* child;
1042             for (child in childWindows)
1043             {
1044                 child.latentParentWindow = self;
1045                 [self removeChildWindow:child];
1046             }
1048             if (latentChildWindows)
1049                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1050             else
1051                 latentChildWindows = [childWindows mutableCopy];
1052         }
1053     }
1055     // Determine if, among Wine windows, this window is directly above or below
1056     // a given other Wine window with no other Wine window intervening.
1057     // Intervening non-Wine windows are ignored.
1058     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1059     {
1060         NSNumber* windowNumber;
1061         NSNumber* otherWindowNumber;
1062         NSArray* windowNumbers;
1063         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1065         if (![self isVisible] || ![otherWindow isVisible])
1066             return FALSE;
1068         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1069         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1070         windowNumbers = [[self class] windowNumbersWithOptions:0];
1071         windowIndex = [windowNumbers indexOfObject:windowNumber];
1072         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1074         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1075             return FALSE;
1077         if (orderingMode == NSWindowAbove)
1078         {
1079             lowIndex = windowIndex;
1080             highIndex = otherWindowIndex;
1081         }
1082         else if (orderingMode == NSWindowBelow)
1083         {
1084             lowIndex = otherWindowIndex;
1085             highIndex = windowIndex;
1086         }
1087         else
1088             return FALSE;
1090         if (highIndex <= lowIndex)
1091             return FALSE;
1093         for (i = lowIndex + 1; i < highIndex; i++)
1094         {
1095             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1096             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1097             if ([interveningWindow isKindOfClass:[WineWindow class]])
1098                 return FALSE;
1099         }
1101         return TRUE;
1102     }
1104     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1105     {
1106         NSMutableArray* windowNumbers;
1107         NSNumber* childWindowNumber;
1108         NSUInteger otherIndex, limit;
1109         NSArray* origChildren;
1110         NSMutableArray* children;
1112         // Get the z-order from the window server and modify it to reflect the
1113         // requested window ordering.
1114         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1115         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1116         [windowNumbers removeObject:childWindowNumber];
1117         otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1118         [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1120         // Get our child windows and sort them in the reverse of the desired
1121         // z-order (back-to-front).
1122         origChildren = [self childWineWindows];
1123         children = [[origChildren mutableCopy] autorelease];
1124         [children sortWithOptions:NSSortStable
1125                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1126             NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1127             NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1128             NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1129             NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1130             if (index1 == NSNotFound)
1131             {
1132                 if (index2 == NSNotFound)
1133                     return NSOrderedSame;
1134                 else
1135                     return NSOrderedAscending;
1136             }
1137             else if (index2 == NSNotFound)
1138                 return NSOrderedDescending;
1139             else if (index1 < index2)
1140                 return NSOrderedDescending;
1141             else if (index2 < index1)
1142                 return NSOrderedAscending;
1144             return NSOrderedSame;
1145         }];
1147         // If the current and desired children arrays match up to a point, leave
1148         // those matching children alone.
1149         limit = MIN([origChildren count], [children count]);
1150         for (otherIndex = 0; otherIndex < limit; otherIndex++)
1151         {
1152             if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1153                 break;
1154         }
1155         [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1157         // Remove all of the child windows and re-add them back-to-front so they
1158         // are in the desired order.
1159         for (other in children)
1160             [self removeChildWindow:other];
1161         for (other in children)
1162             [self addChildWindow:other ordered:NSWindowAbove];
1163     }
1165     /* Returns whether or not the window was ordered in, which depends on if
1166        its frame intersects any screen. */
1167     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1168     {
1169         WineApplicationController* controller = [WineApplicationController sharedController];
1170         if (![self isMiniaturized])
1171         {
1172             BOOL needAdjustWindowLevels = FALSE;
1173             BOOL wasVisible;
1175             [controller transformProcessToForeground];
1176             [NSApp unhide:nil];
1177             wasVisible = [self isVisible];
1179             if (activate)
1180                 [NSApp activateIgnoringOtherApps:YES];
1182             NSDisableScreenUpdates();
1184             if ([self becameEligibleParentOrChild])
1185                 needAdjustWindowLevels = TRUE;
1187             if (prev || next)
1188             {
1189                 WineWindow* other = [prev isVisible] ? prev : next;
1190                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1192                 if (![self isOrdered:orderingMode relativeTo:other])
1193                 {
1194                     WineWindow* parent = (WineWindow*)[self parentWindow];
1195                     WineWindow* otherParent = (WineWindow*)[other parentWindow];
1197                     // This window level may not be right for this window based
1198                     // on floating-ness, fullscreen-ness, etc.  But we set it
1199                     // temporarily to allow us to order the windows properly.
1200                     // Then the levels get fixed by -adjustWindowLevels.
1201                     if ([self level] != [other level])
1202                         [self setLevel:[other level]];
1203                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1205                     // The above call to -[NSWindow orderWindow:relativeTo:] won't
1206                     // reorder windows which are both children of the same parent
1207                     // relative to each other, so do that separately.
1208                     if (parent && parent == otherParent)
1209                         [parent order:orderingMode childWindow:self relativeTo:other];
1211                     needAdjustWindowLevels = TRUE;
1212                 }
1213             }
1214             else
1215             {
1216                 // Again, temporarily set level to make sure we can order to
1217                 // the right place.
1218                 next = [controller frontWineWindow];
1219                 if (next && [self level] < [next level])
1220                     [self setLevel:[next level]];
1221                 [self orderFront:nil];
1222                 needAdjustWindowLevels = TRUE;
1223             }
1225             if ([self becameEligibleParentOrChild])
1226                 needAdjustWindowLevels = TRUE;
1228             if (needAdjustWindowLevels)
1229             {
1230                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1231                     [controller updateFullscreenWindows];
1232                 [controller adjustWindowLevels];
1233             }
1235             if (pendingMinimize)
1236             {
1237                 [super miniaturize:nil];
1238                 pendingMinimize = FALSE;
1239             }
1241             NSEnableScreenUpdates();
1243             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1244                Generate a frame-changed event just in case.  The back end will ignore
1245                it if nothing actually changed. */
1246             [self windowDidResize:nil];
1248             if (![self isExcludedFromWindowsMenu])
1249                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1250         }
1251     }
1253     - (void) doOrderOut
1254     {
1255         WineApplicationController* controller = [WineApplicationController sharedController];
1256         BOOL wasVisible = [self isVisible];
1257         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1259         if ([self isMiniaturized])
1260             pendingMinimize = TRUE;
1262         [self becameIneligibleParentOrChild];
1263         if ([self isMiniaturized])
1264         {
1265             fakingClose = TRUE;
1266             [self close];
1267             fakingClose = FALSE;
1268         }
1269         else
1270             [self orderOut:nil];
1271         savedVisibleState = FALSE;
1272         if (wasVisible && wasOnActiveSpace && fullscreen)
1273             [controller updateFullscreenWindows];
1274         [controller adjustWindowLevels];
1275         [NSApp removeWindowsItem:self];
1277         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1278                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1279                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1280                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1281                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1282                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1283                                forWindow:self];
1284     }
1286     - (void) updateFullscreen
1287     {
1288         NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1289         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1291         if (nowFullscreen != fullscreen)
1292         {
1293             WineApplicationController* controller = [WineApplicationController sharedController];
1295             fullscreen = nowFullscreen;
1296             if ([self isVisible] && [self isOnActiveSpace])
1297                 [controller updateFullscreenWindows];
1299             [controller adjustWindowLevels];
1300         }
1301     }
1303     - (void) setFrameFromWine:(NSRect)contentRect
1304     {
1305         /* Origin is (left, top) in a top-down space.  Need to convert it to
1306            (left, bottom) in a bottom-up space. */
1307         [[WineApplicationController sharedController] flipRect:&contentRect];
1309         /* The back end is establishing a new window size and position.  It's
1310            not interested in any stale events regarding those that may be sitting
1311            in the queue. */
1312         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1313                                forWindow:self];
1315         if (!NSIsEmptyRect(contentRect))
1316         {
1317             NSRect frame, oldFrame;
1319             oldFrame = [self frame];
1320             frame = [self frameRectForContentRect:contentRect];
1321             if (!NSEqualRects(frame, oldFrame))
1322             {
1323                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1324                 BOOL needEnableScreenUpdates = FALSE;
1326                 if ([self preventResizing])
1327                 {
1328                     // Allow the following calls to -setFrame:display: to work even
1329                     // if they would violate the content size constraints. This
1330                     // shouldn't be necessary since the content size constraints are
1331                     // documented to not constrain that method, but it seems to be.
1332                     [self setContentMinSize:NSZeroSize];
1333                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1334                 }
1336                 if (equalSizes && [[self childWineWindows] count])
1337                 {
1338                     // If we change the window frame such that the origin moves
1339                     // but the size doesn't change, then Cocoa moves child
1340                     // windows with the parent.  We don't want that so we fake
1341                     // a change of the size and then change it back.
1342                     NSRect bogusFrame = frame;
1343                     bogusFrame.size.width++;
1345                     NSDisableScreenUpdates();
1346                     needEnableScreenUpdates = TRUE;
1348                     ignore_windowResize = TRUE;
1349                     [self setFrame:bogusFrame display:NO];
1350                     ignore_windowResize = FALSE;
1351                 }
1353                 [self setFrame:frame display:YES];
1354                 if ([self preventResizing])
1355                 {
1356                     [self setContentMinSize:contentRect.size];
1357                     [self setContentMaxSize:contentRect.size];
1358                 }
1360                 if (needEnableScreenUpdates)
1361                     NSEnableScreenUpdates();
1363                 if (!equalSizes)
1364                     [self updateColorSpace];
1366                 if (!enteringFullScreen &&
1367                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1368                     nonFullscreenFrame = frame;
1370                 [self updateFullscreen];
1372                 if ([self isOrderedIn])
1373                 {
1374                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1375                        event.  The back end will ignore it if nothing actually changed. */
1376                     [self windowDidResize:nil];
1377                 }
1378             }
1379         }
1380     }
1382     - (void) setMacDrvParentWindow:(WineWindow*)parent
1383     {
1384         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1385         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1386         {
1387             [oldParent removeChildWineWindow:self];
1388             [latentParentWindow removeChildWineWindow:self];
1389             if ([parent addChildWineWindow:self])
1390                 [[WineApplicationController sharedController] adjustWindowLevels];
1391         }
1392     }
1394     - (void) setDisabled:(BOOL)newValue
1395     {
1396         if (disabled != newValue)
1397         {
1398             disabled = newValue;
1399             [self adjustFeaturesForState];
1400         }
1401     }
1403     - (BOOL) needsTransparency
1404     {
1405         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1406     }
1408     - (void) checkTransparency
1409     {
1410         if (![self isOpaque] && !self.needsTransparency)
1411         {
1412             self.shapeChangedSinceLastDraw = TRUE;
1413             [[self contentView] setNeedsDisplay:YES];
1414             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1415             [self setOpaque:YES];
1416         }
1417         else if ([self isOpaque] && self.needsTransparency)
1418         {
1419             self.shapeChangedSinceLastDraw = TRUE;
1420             [[self contentView] setNeedsDisplay:YES];
1421             [self setBackgroundColor:[NSColor clearColor]];
1422             [self setOpaque:NO];
1423         }
1424     }
1426     - (void) setShape:(NSBezierPath*)newShape
1427     {
1428         if (shape == newShape) return;
1430         if (shape)
1431         {
1432             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1433             [shape release];
1434         }
1435         if (newShape)
1436             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1438         shape = [newShape copy];
1439         self.shapeChangedSinceLastDraw = TRUE;
1441         [self checkTransparency];
1442     }
1444     - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1445     {
1446         if (newTimer != liveResizeDisplayTimer)
1447         {
1448             [liveResizeDisplayTimer invalidate];
1449             [liveResizeDisplayTimer release];
1450             liveResizeDisplayTimer = [newTimer retain];
1451         }
1452     }
1454     - (void) makeFocused:(BOOL)activate
1455     {
1456         if (activate)
1457         {
1458             [[WineApplicationController sharedController] transformProcessToForeground];
1459             [NSApp activateIgnoringOtherApps:YES];
1460         }
1462         causing_becomeKeyWindow = self;
1463         [self makeKeyWindow];
1464         causing_becomeKeyWindow = nil;
1466         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1467                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1468                                forWindow:self];
1469     }
1471     - (void) postKey:(uint16_t)keyCode
1472              pressed:(BOOL)pressed
1473            modifiers:(NSUInteger)modifiers
1474                event:(NSEvent*)theEvent
1475     {
1476         macdrv_event* event;
1477         CGEventRef cgevent;
1478         WineApplicationController* controller = [WineApplicationController sharedController];
1480         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1481         event->key.keycode   = keyCode;
1482         event->key.modifiers = modifiers;
1483         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1485         if ((cgevent = [theEvent CGEvent]))
1486         {
1487             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1488                                                         kCGKeyboardEventKeyboardType);
1489             if (keyboardType != controller.keyboardType)
1490             {
1491                 controller.keyboardType = keyboardType;
1492                 [controller keyboardSelectionDidChange];
1493             }
1494         }
1496         [queue postEvent:event];
1498         macdrv_release_event(event);
1500         [controller noteKey:keyCode pressed:pressed];
1501     }
1503     - (void) postKeyEvent:(NSEvent *)theEvent
1504     {
1505         [self flagsChanged:theEvent];
1506         [self postKey:[theEvent keyCode]
1507               pressed:[theEvent type] == NSKeyDown
1508             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1509                 event:theEvent];
1510     }
1512     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1513     {
1514         savedContentMinSize = minSize;
1515         savedContentMaxSize = maxSize;
1516         if (![self preventResizing])
1517         {
1518             [self setContentMinSize:minSize];
1519             [self setContentMaxSize:maxSize];
1520         }
1521     }
1523     - (WineWindow*) ancestorWineWindow
1524     {
1525         WineWindow* ancestor = self;
1526         for (;;)
1527         {
1528             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1529             if ([parent isKindOfClass:[WineWindow class]])
1530                 ancestor = parent;
1531             else
1532                 break;
1533         }
1534         return ancestor;
1535     }
1537     - (void) postBroughtForwardEvent
1538     {
1539         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1540         [queue postEvent:event];
1541         macdrv_release_event(event);
1542     }
1544     - (void) updateForCursorClipping
1545     {
1546         [self adjustFeaturesForState];
1547     }
1549     - (void) endWindowDragging
1550     {
1551         if (draggingPhase)
1552         {
1553             if (draggingPhase == 3)
1554             {
1555                 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
1556                 [queue postEvent:event];
1557                 macdrv_release_event(event);
1558             }
1560             draggingPhase = 0;
1561             [[WineApplicationController sharedController] window:self isBeingDragged:NO];
1562         }
1563     }
1566     /*
1567      * ---------- NSWindow method overrides ----------
1568      */
1569     - (BOOL) canBecomeKeyWindow
1570     {
1571         if (causing_becomeKeyWindow == self) return YES;
1572         if (self.disabled || self.noActivate) return NO;
1573         return [self isKeyWindow];
1574     }
1576     - (BOOL) canBecomeMainWindow
1577     {
1578         return [self canBecomeKeyWindow];
1579     }
1581     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1582     {
1583         // If a window is sized to completely cover a screen, then it's in
1584         // full-screen mode.  In that case, we don't allow NSWindow to constrain
1585         // it.
1586         NSArray* screens = [NSScreen screens];
1587         NSRect contentRect = [self contentRectForFrameRect:frameRect];
1588         if (!screen_covered_by_rect(contentRect, screens) &&
1589             frame_intersects_screens(frameRect, screens))
1590             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1591         return frameRect;
1592     }
1594     - (BOOL) isExcludedFromWindowsMenu
1595     {
1596         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1597     }
1599     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1600     {
1601         BOOL ret = [super validateMenuItem:menuItem];
1603         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1604             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1605         if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
1606             ret = NO;
1608         return ret;
1609     }
1611     /* We don't call this.  It's the action method of the items in the Window menu. */
1612     - (void) makeKeyAndOrderFront:(id)sender
1613     {
1614         if ([self isMiniaturized])
1615             [self deminiaturize:nil];
1616         [self orderBelow:nil orAbove:nil activate:NO];
1617         [[self ancestorWineWindow] postBroughtForwardEvent];
1619         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1620             [[WineApplicationController sharedController] windowGotFocus:self];
1621     }
1623     - (void) sendEvent:(NSEvent*)event
1624     {
1625         NSEventType type = event.type;
1627         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1628            interface control.  For example, Control-Tab switches focus among
1629            views.  We want to bypass that feature, so directly route key-down
1630            events to -keyDown:. */
1631         if (type == NSKeyDown)
1632             [[self firstResponder] keyDown:event];
1633         else
1634         {
1635             if (!draggingPhase && maximized && ![self isMovable] &&
1636                 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
1637                 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
1638             {
1639                 NSRect titleBar = self.frame;
1640                 NSRect contentRect = [self contentRectForFrameRect:titleBar];
1641                 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
1642                 titleBar.origin.y = NSMaxY(contentRect);
1644                 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
1646                 if (NSMouseInRect(dragStartPosition, titleBar, NO))
1647                 {
1648                     static const NSWindowButton buttons[] = {
1649                         NSWindowCloseButton,
1650                         NSWindowMiniaturizeButton,
1651                         NSWindowZoomButton,
1652                         NSWindowFullScreenButton,
1653                     };
1654                     BOOL hitButton = NO;
1655                     int i;
1657                     for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
1658                     {
1659                         NSButton* button;
1661                         if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
1662                             continue;
1664                         button = [self standardWindowButton:buttons[i]];
1665                         if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
1666                         {
1667                             hitButton = YES;
1668                             break;
1669                         }
1670                     }
1672                     if (!hitButton)
1673                     {
1674                         draggingPhase = 1;
1675                         dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
1676                         [[WineApplicationController sharedController] window:self isBeingDragged:YES];
1677                     }
1678                 }
1679             }
1680             else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
1681             {
1682                 if ([self isMovable])
1683                 {
1684                     NSPoint point = [self convertBaseToScreen:event.locationInWindow];
1685                     NSPoint newTopLeft = dragWindowStartPosition;
1687                     newTopLeft.x += point.x - dragStartPosition.x;
1688                     newTopLeft.y += point.y - dragStartPosition.y;
1690                     if (draggingPhase == 2)
1691                     {
1692                         macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
1693                         [queue postEvent:event];
1694                         macdrv_release_event(event);
1696                         draggingPhase = 3;
1697                     }
1699                     [self setFrameTopLeftPoint:newTopLeft];
1700                 }
1701                 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
1702                 {
1703                     macdrv_event* event;
1704                     NSRect frame = [self contentRectForFrameRect:self.frame];
1706                     [[WineApplicationController sharedController] flipRect:&frame];
1708                     event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1709                     event->window_restore_requested.keep_frame = TRUE;
1710                     event->window_restore_requested.frame = NSRectToCGRect(frame);
1711                     [queue postEvent:event];
1712                     macdrv_release_event(event);
1714                     draggingPhase = 2;
1715                 }
1717                 if (type == NSLeftMouseUp)
1718                     [self endWindowDragging];
1719             }
1721             [super sendEvent:event];
1722         }
1723     }
1725     - (void) miniaturize:(id)sender
1726     {
1727         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1728         [queue postEvent:event];
1729         macdrv_release_event(event);
1730     }
1732     - (void) toggleFullScreen:(id)sender
1733     {
1734         if (!self.disabled && !maximized)
1735             [super toggleFullScreen:sender];
1736     }
1738     - (NSArray*) childWineWindows
1739     {
1740         NSArray* childWindows = self.childWindows;
1741         NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
1742             return [child isKindOfClass:[WineWindow class]];
1743         }];
1744         return [childWindows objectsAtIndexes:indexes];
1745     }
1747     // We normally use the generic/calibrated RGB color space for the window,
1748     // rather than the device color space, to avoid expensive color conversion
1749     // which slows down drawing.  However, for windows displaying OpenGL, having
1750     // a different color space than the screen greatly reduces frame rates, often
1751     // limiting it to the display refresh rate.
1752     //
1753     // To avoid this, we switch back to the screen color space whenever the
1754     // window is covered by a view with an attached OpenGL context.
1755     - (void) updateColorSpace
1756     {
1757         NSRect contentRect = [[self contentView] frame];
1758         BOOL coveredByGLView = FALSE;
1759         for (WineContentView* view in [[self contentView] subviews])
1760         {
1761             if ([view hasGLContext])
1762             {
1763                 NSRect frame = [view convertRect:[view bounds] toView:nil];
1764                 if (NSContainsRect(frame, contentRect))
1765                 {
1766                     coveredByGLView = TRUE;
1767                     break;
1768                 }
1769             }
1770         }
1772         if (coveredByGLView)
1773             [self setColorSpace:nil];
1774         else
1775             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1776     }
1779     /*
1780      * ---------- NSResponder method overrides ----------
1781      */
1782     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1784     - (void) flagsChanged:(NSEvent *)theEvent
1785     {
1786         static const struct {
1787             NSUInteger  mask;
1788             uint16_t    keycode;
1789         } modifiers[] = {
1790             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
1791             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
1792             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
1793             { NX_DEVICELCTLKEYMASK,     kVK_Control },
1794             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
1795             { NX_DEVICELALTKEYMASK,     kVK_Option },
1796             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
1797             { NX_DEVICELCMDKEYMASK,     kVK_Command },
1798             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
1799         };
1801         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1802         NSUInteger changed;
1803         int i, last_changed;
1805         fix_device_modifiers_by_generic(&modifierFlags);
1806         changed = modifierFlags ^ lastModifierFlags;
1808         last_changed = -1;
1809         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1810             if (changed & modifiers[i].mask)
1811                 last_changed = i;
1813         for (i = 0; i <= last_changed; i++)
1814         {
1815             if (changed & modifiers[i].mask)
1816             {
1817                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1819                 if (i == last_changed)
1820                     lastModifierFlags = modifierFlags;
1821                 else
1822                 {
1823                     lastModifierFlags ^= modifiers[i].mask;
1824                     fix_generic_modifiers_by_device(&lastModifierFlags);
1825                 }
1827                 // Caps lock generates one event for each press-release action.
1828                 // We need to simulate a pair of events for each actual event.
1829                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1830                 {
1831                     [self postKey:modifiers[i].keycode
1832                           pressed:TRUE
1833                         modifiers:lastModifierFlags
1834                             event:(NSEvent*)theEvent];
1835                     pressed = FALSE;
1836                 }
1838                 [self postKey:modifiers[i].keycode
1839                       pressed:pressed
1840                     modifiers:lastModifierFlags
1841                         event:(NSEvent*)theEvent];
1842             }
1843         }
1844     }
1846     - (void) applicationWillHide
1847     {
1848         savedVisibleState = [self isVisible];
1849     }
1851     - (void) applicationDidUnhide
1852     {
1853         if ([self isVisible])
1854             [self becameEligibleParentOrChild];
1855     }
1858     /*
1859      * ---------- NSWindowDelegate methods ----------
1860      */
1861     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1862     {
1863         macdrv_query* query;
1864         NSSize size;
1866         query = macdrv_create_query();
1867         query->type = QUERY_MIN_MAX_INFO;
1868         query->window = (macdrv_window)[self retain];
1869         [self.queue query:query timeout:0.5];
1870         macdrv_release_query(query);
1872         size = [self contentMaxSize];
1873         if (proposedSize.width < size.width)
1874             size.width = proposedSize.width;
1875         if (proposedSize.height < size.height)
1876             size.height = proposedSize.height;
1877         return size;
1878     }
1880     - (void)windowDidBecomeKey:(NSNotification *)notification
1881     {
1882         WineApplicationController* controller = [WineApplicationController sharedController];
1883         NSEvent* event = [controller lastFlagsChanged];
1884         if (event)
1885             [self flagsChanged:event];
1887         if (causing_becomeKeyWindow == self) return;
1889         [controller windowGotFocus:self];
1890     }
1892     - (void)windowDidDeminiaturize:(NSNotification *)notification
1893     {
1894         WineApplicationController* controller = [WineApplicationController sharedController];
1896         if (!ignore_windowDeminiaturize)
1897             [self postDidUnminimizeEvent];
1898         ignore_windowDeminiaturize = FALSE;
1900         [self becameEligibleParentOrChild];
1902         if (fullscreen && [self isOnActiveSpace])
1903             [controller updateFullscreenWindows];
1904         [controller adjustWindowLevels];
1906         if (![self parentWindow])
1907             [self postBroughtForwardEvent];
1909         if (!self.disabled && !self.noActivate)
1910         {
1911             causing_becomeKeyWindow = self;
1912             [self makeKeyWindow];
1913             causing_becomeKeyWindow = nil;
1914             [controller windowGotFocus:self];
1915         }
1917         [self windowDidResize:notification];
1918     }
1920     - (void) windowDidEndLiveResize:(NSNotification *)notification
1921     {
1922         if (!maximized)
1923         {
1924             macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1925             [queue postEvent:event];
1926             macdrv_release_event(event);
1927         }
1929         self.liveResizeDisplayTimer = nil;
1930     }
1932     - (void) windowDidEnterFullScreen:(NSNotification*)notification
1933     {
1934         enteringFullScreen = FALSE;
1935         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1936     }
1938     - (void) windowDidExitFullScreen:(NSNotification*)notification
1939     {
1940         exitingFullScreen = FALSE;
1941         [self setFrame:nonFullscreenFrame display:YES animate:NO];
1942         [self windowDidResize:nil];
1943     }
1945     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1946     {
1947         enteringFullScreen = FALSE;
1948         enteredFullScreenTime = 0;
1949     }
1951     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1952     {
1953         exitingFullScreen = FALSE;
1954         [self windowDidResize:nil];
1955     }
1957     - (void)windowDidMiniaturize:(NSNotification *)notification
1958     {
1959         if (fullscreen && [self isOnActiveSpace])
1960             [[WineApplicationController sharedController] updateFullscreenWindows];
1961     }
1963     - (void)windowDidMove:(NSNotification *)notification
1964     {
1965         [self windowDidResize:notification];
1966     }
1968     - (void)windowDidResignKey:(NSNotification *)notification
1969     {
1970         macdrv_event* event;
1972         if (causing_becomeKeyWindow) return;
1974         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1975         [queue postEvent:event];
1976         macdrv_release_event(event);
1977     }
1979     - (void)windowDidResize:(NSNotification *)notification
1980     {
1981         macdrv_event* event;
1982         NSRect frame = [self frame];
1984         if ([self inLiveResize])
1985         {
1986             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1987                 resizingFromLeft = TRUE;
1988             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1989                 resizingFromTop = TRUE;
1990         }
1992         frame = [self contentRectForFrameRect:frame];
1994         if (ignore_windowResize || exitingFullScreen) return;
1996         if ([self preventResizing])
1997         {
1998             [self setContentMinSize:frame.size];
1999             [self setContentMaxSize:frame.size];
2000         }
2002         [[WineApplicationController sharedController] flipRect:&frame];
2004         /* Coalesce events by discarding any previous ones still in the queue. */
2005         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2006                                forWindow:self];
2008         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2009         event->window_frame_changed.frame = NSRectToCGRect(frame);
2010         event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
2011         event->window_frame_changed.in_resize = [self inLiveResize];
2012         [queue postEvent:event];
2013         macdrv_release_event(event);
2015         [[[self contentView] inputContext] invalidateCharacterCoordinates];
2016         [self updateFullscreen];
2017     }
2019     - (BOOL)windowShouldClose:(id)sender
2020     {
2021         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2022         [queue postEvent:event];
2023         macdrv_release_event(event);
2024         return NO;
2025     }
2027     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2028     {
2029         if (maximized)
2030         {
2031             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2032             [queue postEvent:event];
2033             macdrv_release_event(event);
2034             return NO;
2035         }
2036         else if (!resizable)
2037         {
2038             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2039             [queue postEvent:event];
2040             macdrv_release_event(event);
2041             return NO;
2042         }
2044         return YES;
2045     }
2047     - (void) windowWillClose:(NSNotification*)notification
2048     {
2049         WineWindow* child;
2051         if (fakingClose) return;
2052         if (latentParentWindow)
2053         {
2054             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2055             self.latentParentWindow = nil;
2056         }
2058         for (child in latentChildWindows)
2059         {
2060             if (child.latentParentWindow == self)
2061                 child.latentParentWindow = nil;
2062         }
2063         [latentChildWindows removeAllObjects];
2064     }
2066     - (void) windowWillEnterFullScreen:(NSNotification*)notification
2067     {
2068         enteringFullScreen = TRUE;
2069         nonFullscreenFrame = [self frame];
2070     }
2072     - (void) windowWillExitFullScreen:(NSNotification*)notification
2073     {
2074         exitingFullScreen = TRUE;
2075     }
2077     - (void)windowWillMiniaturize:(NSNotification *)notification
2078     {
2079         [self becameIneligibleParentOrChild];
2080     }
2082     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2083     {
2084         if ([self inLiveResize])
2085         {
2086             if (maximized)
2087                 return self.frame.size;
2089             NSRect rect;
2090             macdrv_query* query;
2092             rect = [self frame];
2093             if (resizingFromLeft)
2094                 rect.origin.x = NSMaxX(rect) - frameSize.width;
2095             if (!resizingFromTop)
2096                 rect.origin.y = NSMaxY(rect) - frameSize.height;
2097             rect.size = frameSize;
2098             rect = [self contentRectForFrameRect:rect];
2099             [[WineApplicationController sharedController] flipRect:&rect];
2101             query = macdrv_create_query();
2102             query->type = QUERY_RESIZE_SIZE;
2103             query->window = (macdrv_window)[self retain];
2104             query->resize_size.rect = NSRectToCGRect(rect);
2105             query->resize_size.from_left = resizingFromLeft;
2106             query->resize_size.from_top = resizingFromTop;
2108             if ([self.queue query:query timeout:0.1])
2109             {
2110                 rect = NSRectFromCGRect(query->resize_size.rect);
2111                 rect = [self frameRectForContentRect:rect];
2112                 frameSize = rect.size;
2113             }
2115             macdrv_release_query(query);
2116         }
2118         return frameSize;
2119     }
2121     - (void) windowWillStartLiveResize:(NSNotification *)notification
2122     {
2123         [self endWindowDragging];
2125         if (maximized)
2126         {
2127             macdrv_event* event;
2128             NSRect frame = [self contentRectForFrameRect:self.frame];
2130             [[WineApplicationController sharedController] flipRect:&frame];
2132             event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2133             event->window_restore_requested.keep_frame = TRUE;
2134             event->window_restore_requested.frame = NSRectToCGRect(frame);
2135             [queue postEvent:event];
2136             macdrv_release_event(event);
2137         }
2138         else
2139             [self sendResizeStartQuery];
2141         frameAtResizeStart = [self frame];
2142         resizingFromLeft = resizingFromTop = FALSE;
2144         // There's a strange restriction in window redrawing during Cocoa-
2145         // managed window resizing.  Only calls to -[NSView setNeedsDisplay...]
2146         // that happen synchronously when Cocoa tells us that our window size
2147         // has changed or asynchronously in a short interval thereafter provoke
2148         // the window to redraw.  Calls to those methods that happen asynchronously
2149         // a half second or more after the last change of the window size aren't
2150         // heeded until the next resize-related user event (e.g. mouse movement).
2151         //
2152         // Wine often has a significant delay between when it's been told that
2153         // the window has changed size and when it can flush completed drawing.
2154         // So, our windows would get stuck with incomplete drawing for as long
2155         // as the user holds the mouse button down and doesn't move it.
2156         //
2157         // We address this by "manually" asking our windows to check if they need
2158         // redrawing every so often (during live resize only).
2159         self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
2160                                                                        target:self
2161                                                                      selector:@selector(displayIfNeeded)
2162                                                                      userInfo:nil
2163                                                                       repeats:YES];
2164         [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
2165                                      forMode:NSRunLoopCommonModes];
2166     }
2168     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2169     {
2170         macdrv_query* query;
2171         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2172         NSSize maxSize;
2174         query = macdrv_create_query();
2175         query->type = QUERY_MIN_MAX_INFO;
2176         query->window = (macdrv_window)[self retain];
2177         [self.queue query:query timeout:0.5];
2178         macdrv_release_query(query);
2180         currentContentRect = [self contentRectForFrameRect:[self frame]];
2181         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2183         maxSize = [self contentMaxSize];
2184         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2185         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2187         // Try to keep the top-left corner where it is.
2188         newContentRect.origin.x = NSMinX(currentContentRect);
2189         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2191         // If that pushes the bottom or right off the screen, pull it up and to the left.
2192         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2193         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2194             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2195         if (NSMinY(newContentRect) < NSMinY(screenRect))
2196             newContentRect.origin.y = NSMinY(screenRect);
2198         // If that pushes the top or left off the screen, push it down and the right
2199         // again.  Do this last because the top-left corner is more important than the
2200         // bottom-right.
2201         if (NSMinX(newContentRect) < NSMinX(screenRect))
2202             newContentRect.origin.x = NSMinX(screenRect);
2203         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2204             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2206         return [self frameRectForContentRect:newContentRect];
2207     }
2210     /*
2211      * ---------- NSPasteboardOwner methods ----------
2212      */
2213     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2214     {
2215         macdrv_query* query = macdrv_create_query();
2216         query->type = QUERY_PASTEBOARD_DATA;
2217         query->window = (macdrv_window)[self retain];
2218         query->pasteboard_data.type = (CFStringRef)[type copy];
2220         [self.queue query:query timeout:3];
2221         macdrv_release_query(query);
2222     }
2225     /*
2226      * ---------- NSDraggingDestination methods ----------
2227      */
2228     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2229     {
2230         return [self draggingUpdated:sender];
2231     }
2233     - (void) draggingExited:(id <NSDraggingInfo>)sender
2234     {
2235         // This isn't really a query.  We don't need any response.  However, it
2236         // has to be processed in a similar manner as the other drag-and-drop
2237         // queries in order to maintain the proper order of operations.
2238         macdrv_query* query = macdrv_create_query();
2239         query->type = QUERY_DRAG_EXITED;
2240         query->window = (macdrv_window)[self retain];
2242         [self.queue query:query timeout:0.1];
2243         macdrv_release_query(query);
2244     }
2246     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2247     {
2248         NSDragOperation ret;
2249         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2250         NSPasteboard* pb = [sender draggingPasteboard];
2252         macdrv_query* query = macdrv_create_query();
2253         query->type = QUERY_DRAG_OPERATION;
2254         query->window = (macdrv_window)[self retain];
2255         query->drag_operation.x = pt.x;
2256         query->drag_operation.y = pt.y;
2257         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2258         query->drag_operation.accepted_op = NSDragOperationNone;
2259         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2261         [self.queue query:query timeout:3];
2262         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2263         macdrv_release_query(query);
2265         return ret;
2266     }
2268     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2269     {
2270         BOOL ret;
2271         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2272         NSPasteboard* pb = [sender draggingPasteboard];
2274         macdrv_query* query = macdrv_create_query();
2275         query->type = QUERY_DRAG_DROP;
2276         query->window = (macdrv_window)[self retain];
2277         query->drag_drop.x = pt.x;
2278         query->drag_drop.y = pt.y;
2279         query->drag_drop.op = [sender draggingSourceOperationMask];
2280         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2282         [self.queue query:query timeout:3 * 60 processEvents:YES];
2283         ret = query->status;
2284         macdrv_release_query(query);
2286         return ret;
2287     }
2289     - (BOOL) wantsPeriodicDraggingUpdates
2290     {
2291         return NO;
2292     }
2294 @end
2297 /***********************************************************************
2298  *              macdrv_create_cocoa_window
2300  * Create a Cocoa window with the given content frame and features (e.g.
2301  * title bar, close box, etc.).
2302  */
2303 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2304         CGRect frame, void* hwnd, macdrv_event_queue queue)
2306     __block WineWindow* window;
2308     OnMainThread(^{
2309         window = [[WineWindow createWindowWithFeatures:wf
2310                                            windowFrame:NSRectFromCGRect(frame)
2311                                                   hwnd:hwnd
2312                                                  queue:(WineEventQueue*)queue] retain];
2313     });
2315     return (macdrv_window)window;
2318 /***********************************************************************
2319  *              macdrv_destroy_cocoa_window
2321  * Destroy a Cocoa window.
2322  */
2323 void macdrv_destroy_cocoa_window(macdrv_window w)
2325     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2326     WineWindow* window = (WineWindow*)w;
2328     OnMainThread(^{
2329         [window doOrderOut];
2330         [window close];
2331     });
2332     [window.queue discardEventsMatchingMask:-1 forWindow:window];
2333     [window release];
2335     [pool release];
2338 /***********************************************************************
2339  *              macdrv_get_window_hwnd
2341  * Get the hwnd that was set for the window at creation.
2342  */
2343 void* macdrv_get_window_hwnd(macdrv_window w)
2345     WineWindow* window = (WineWindow*)w;
2346     return window.hwnd;
2349 /***********************************************************************
2350  *              macdrv_set_cocoa_window_features
2352  * Update a Cocoa window's features.
2353  */
2354 void macdrv_set_cocoa_window_features(macdrv_window w,
2355         const struct macdrv_window_features* wf)
2357     WineWindow* window = (WineWindow*)w;
2359     OnMainThread(^{
2360         [window setWindowFeatures:wf];
2361     });
2364 /***********************************************************************
2365  *              macdrv_set_cocoa_window_state
2367  * Update a Cocoa window's state.
2368  */
2369 void macdrv_set_cocoa_window_state(macdrv_window w,
2370         const struct macdrv_window_state* state)
2372     WineWindow* window = (WineWindow*)w;
2374     OnMainThread(^{
2375         [window setMacDrvState:state];
2376     });
2379 /***********************************************************************
2380  *              macdrv_set_cocoa_window_title
2382  * Set a Cocoa window's title.
2383  */
2384 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2385         size_t length)
2387     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2388     WineWindow* window = (WineWindow*)w;
2389     NSString* titleString;
2391     if (title)
2392         titleString = [NSString stringWithCharacters:title length:length];
2393     else
2394         titleString = @"";
2395     OnMainThreadAsync(^{
2396         [window setTitle:titleString];
2397         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2398             [NSApp changeWindowsItem:window title:titleString filename:NO];
2399     });
2401     [pool release];
2404 /***********************************************************************
2405  *              macdrv_order_cocoa_window
2407  * Reorder a Cocoa window relative to other windows.  If prev is
2408  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
2409  * it is ordered above that window.  Otherwise, it is ordered to the
2410  * front.
2411  */
2412 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2413         macdrv_window n, int activate)
2415     WineWindow* window = (WineWindow*)w;
2416     WineWindow* prev = (WineWindow*)p;
2417     WineWindow* next = (WineWindow*)n;
2419     OnMainThreadAsync(^{
2420         [window orderBelow:prev
2421                    orAbove:next
2422                   activate:activate];
2423     });
2424     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2425                                   forWindow:window];
2426     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2427                                 forWindow:next];
2430 /***********************************************************************
2431  *              macdrv_hide_cocoa_window
2433  * Hides a Cocoa window.
2434  */
2435 void macdrv_hide_cocoa_window(macdrv_window w)
2437     WineWindow* window = (WineWindow*)w;
2439     OnMainThread(^{
2440         [window doOrderOut];
2441     });
2444 /***********************************************************************
2445  *              macdrv_set_cocoa_window_frame
2447  * Move a Cocoa window.
2448  */
2449 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2451     WineWindow* window = (WineWindow*)w;
2453     OnMainThread(^{
2454         [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2455     });
2458 /***********************************************************************
2459  *              macdrv_get_cocoa_window_frame
2461  * Gets the frame of a Cocoa window.
2462  */
2463 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2465     WineWindow* window = (WineWindow*)w;
2467     OnMainThread(^{
2468         NSRect frame;
2470         frame = [window contentRectForFrameRect:[window frame]];
2471         [[WineApplicationController sharedController] flipRect:&frame];
2472         *out_frame = NSRectToCGRect(frame);
2473     });
2476 /***********************************************************************
2477  *              macdrv_set_cocoa_parent_window
2479  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
2480  * the parent window.
2481  */
2482 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2484     WineWindow* window = (WineWindow*)w;
2486     OnMainThread(^{
2487         [window setMacDrvParentWindow:(WineWindow*)parent];
2488     });
2491 /***********************************************************************
2492  *              macdrv_set_window_surface
2493  */
2494 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2496     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2497     WineWindow* window = (WineWindow*)w;
2499     OnMainThread(^{
2500         window.surface = surface;
2501         window.surface_mutex = mutex;
2502     });
2504     [pool release];
2507 /***********************************************************************
2508  *              macdrv_window_needs_display
2510  * Mark a window as needing display in a specified rect (in non-client
2511  * area coordinates).
2512  */
2513 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2515     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2516     WineWindow* window = (WineWindow*)w;
2518     OnMainThreadAsync(^{
2519         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2520     });
2522     [pool release];
2525 /***********************************************************************
2526  *              macdrv_set_window_shape
2528  * Sets the shape of a Cocoa window from an array of rectangles.  If
2529  * rects is NULL, resets the window's shape to its frame.
2530  */
2531 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2533     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2534     WineWindow* window = (WineWindow*)w;
2536     OnMainThread(^{
2537         if (!rects || !count)
2538         {
2539             window.shape = nil;
2540             window.shapeData = nil;
2541         }
2542         else
2543         {
2544             size_t length = sizeof(*rects) * count;
2545             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
2546             {
2547                 NSBezierPath* path;
2548                 unsigned int i;
2550                 path = [NSBezierPath bezierPath];
2551                 for (i = 0; i < count; i++)
2552                     [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2553                 window.shape = path;
2554                 window.shapeData = [NSData dataWithBytes:rects length:length];
2555             }
2556         }
2557     });
2559     [pool release];
2562 /***********************************************************************
2563  *              macdrv_set_window_alpha
2564  */
2565 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2567     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2568     WineWindow* window = (WineWindow*)w;
2570     [window setAlphaValue:alpha];
2572     [pool release];
2575 /***********************************************************************
2576  *              macdrv_set_window_color_key
2577  */
2578 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2579                                  CGFloat keyBlue)
2581     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2582     WineWindow* window = (WineWindow*)w;
2584     OnMainThread(^{
2585         window.colorKeyed       = TRUE;
2586         window.colorKeyRed      = keyRed;
2587         window.colorKeyGreen    = keyGreen;
2588         window.colorKeyBlue     = keyBlue;
2589         [window checkTransparency];
2590     });
2592     [pool release];
2595 /***********************************************************************
2596  *              macdrv_clear_window_color_key
2597  */
2598 void macdrv_clear_window_color_key(macdrv_window w)
2600     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2601     WineWindow* window = (WineWindow*)w;
2603     OnMainThread(^{
2604         window.colorKeyed = FALSE;
2605         [window checkTransparency];
2606     });
2608     [pool release];
2611 /***********************************************************************
2612  *              macdrv_window_use_per_pixel_alpha
2613  */
2614 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2616     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2617     WineWindow* window = (WineWindow*)w;
2619     OnMainThread(^{
2620         window.usePerPixelAlpha = use_per_pixel_alpha;
2621         [window checkTransparency];
2622     });
2624     [pool release];
2627 /***********************************************************************
2628  *              macdrv_give_cocoa_window_focus
2630  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
2631  * orders it front and, if its frame was not within the desktop bounds,
2632  * Cocoa will typically move it on-screen.
2633  */
2634 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2636     WineWindow* window = (WineWindow*)w;
2638     OnMainThread(^{
2639         [window makeFocused:activate];
2640     });
2643 /***********************************************************************
2644  *              macdrv_set_window_min_max_sizes
2646  * Sets the window's minimum and maximum content sizes.
2647  */
2648 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2650     WineWindow* window = (WineWindow*)w;
2652     OnMainThread(^{
2653         [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2654     });
2657 /***********************************************************************
2658  *              macdrv_create_view
2660  * Creates and returns a view in the specified rect of the window.  The
2661  * caller is responsible for calling macdrv_dispose_view() on the view
2662  * when it is done with it.
2663  */
2664 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2666     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2667     WineWindow* window = (WineWindow*)w;
2668     __block WineContentView* view;
2670     if (CGRectIsNull(rect)) rect = CGRectZero;
2672     OnMainThread(^{
2673         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2675         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2676         [view setAutoresizesSubviews:NO];
2677         [nc addObserver:view
2678                selector:@selector(updateGLContexts)
2679                    name:NSViewGlobalFrameDidChangeNotification
2680                  object:view];
2681         [nc addObserver:view
2682                selector:@selector(updateGLContexts)
2683                    name:NSApplicationDidChangeScreenParametersNotification
2684                  object:NSApp];
2685         [[window contentView] addSubview:view];
2686         [window updateColorSpace];
2687     });
2689     [pool release];
2690     return (macdrv_view)view;
2693 /***********************************************************************
2694  *              macdrv_dispose_view
2696  * Destroys a view previously returned by macdrv_create_view.
2697  */
2698 void macdrv_dispose_view(macdrv_view v)
2700     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2701     WineContentView* view = (WineContentView*)v;
2703     OnMainThread(^{
2704         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2705         WineWindow* window = (WineWindow*)[view window];
2707         [nc removeObserver:view
2708                       name:NSViewGlobalFrameDidChangeNotification
2709                     object:view];
2710         [nc removeObserver:view
2711                       name:NSApplicationDidChangeScreenParametersNotification
2712                     object:NSApp];
2713         [view removeFromSuperview];
2714         [view release];
2715         [window updateColorSpace];
2716     });
2718     [pool release];
2721 /***********************************************************************
2722  *              macdrv_set_view_window_and_frame
2724  * Move a view to a new window and/or position within its window.  If w
2725  * is NULL, leave the view in its current window and just change its
2726  * frame.
2727  */
2728 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2730     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2731     WineContentView* view = (WineContentView*)v;
2732     WineWindow* window = (WineWindow*)w;
2734     if (CGRectIsNull(rect)) rect = CGRectZero;
2736     OnMainThread(^{
2737         BOOL changedWindow = (window && window != [view window]);
2738         NSRect newFrame = NSRectFromCGRect(rect);
2739         NSRect oldFrame = [view frame];
2740         BOOL needUpdateWindowColorSpace = FALSE;
2742         if (changedWindow)
2743         {
2744             WineWindow* oldWindow = (WineWindow*)[view window];
2745             [view removeFromSuperview];
2746             [oldWindow updateColorSpace];
2747             [[window contentView] addSubview:view];
2748             needUpdateWindowColorSpace = TRUE;
2749         }
2751         if (!NSEqualRects(oldFrame, newFrame))
2752         {
2753             if (!changedWindow)
2754                 [[view superview] setNeedsDisplayInRect:oldFrame];
2755             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2756                 [view setFrameSize:newFrame.size];
2757             else if (NSEqualSizes(oldFrame.size, newFrame.size))
2758                 [view setFrameOrigin:newFrame.origin];
2759             else
2760                 [view setFrame:newFrame];
2761             [view setNeedsDisplay:YES];
2762             needUpdateWindowColorSpace = TRUE;
2763         }
2765         if (needUpdateWindowColorSpace)
2766             [(WineWindow*)[view window] updateColorSpace];
2767     });
2769     [pool release];
2772 /***********************************************************************
2773  *              macdrv_add_view_opengl_context
2775  * Add an OpenGL context to the list being tracked for each view.
2776  */
2777 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2779     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2780     WineContentView* view = (WineContentView*)v;
2781     WineOpenGLContext *context = (WineOpenGLContext*)c;
2783     OnMainThread(^{
2784         [view addGLContext:context];
2785     });
2787     [pool release];
2790 /***********************************************************************
2791  *              macdrv_remove_view_opengl_context
2793  * Add an OpenGL context to the list being tracked for each view.
2794  */
2795 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2797     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2798     WineContentView* view = (WineContentView*)v;
2799     WineOpenGLContext *context = (WineOpenGLContext*)c;
2801     OnMainThreadAsync(^{
2802         [view removeGLContext:context];
2803     });
2805     [pool release];
2808 /***********************************************************************
2809  *              macdrv_window_background_color
2811  * Returns the standard Mac window background color as a 32-bit value of
2812  * the form 0x00rrggbb.
2813  */
2814 uint32_t macdrv_window_background_color(void)
2816     static uint32_t result;
2817     static dispatch_once_t once;
2819     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2820     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
2821     // of it is to draw with it.
2822     dispatch_once(&once, ^{
2823         OnMainThread(^{
2824             unsigned char rgbx[4];
2825             unsigned char *planes = rgbx;
2826             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2827                                                                                pixelsWide:1
2828                                                                                pixelsHigh:1
2829                                                                             bitsPerSample:8
2830                                                                           samplesPerPixel:3
2831                                                                                  hasAlpha:NO
2832                                                                                  isPlanar:NO
2833                                                                            colorSpaceName:NSCalibratedRGBColorSpace
2834                                                                              bitmapFormat:0
2835                                                                               bytesPerRow:4
2836                                                                              bitsPerPixel:32];
2837             [NSGraphicsContext saveGraphicsState];
2838             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2839             [[NSColor windowBackgroundColor] set];
2840             NSRectFill(NSMakeRect(0, 0, 1, 1));
2841             [NSGraphicsContext restoreGraphicsState];
2842             [bitmap release];
2843             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2844         });
2845     });
2847     return result;
2850 /***********************************************************************
2851  *              macdrv_send_text_input_event
2852  */
2853 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2855     __block BOOL ret;
2857     OnMainThread(^{
2858         WineWindow* window = (WineWindow*)[NSApp keyWindow];
2859         if (![window isKindOfClass:[WineWindow class]])
2860         {
2861             window = (WineWindow*)[NSApp mainWindow];
2862             if (![window isKindOfClass:[WineWindow class]])
2863                 window = [[WineApplicationController sharedController] frontWineWindow];
2864         }
2866         if (window)
2867         {
2868             NSUInteger localFlags = flags;
2869             CGEventRef c;
2870             NSEvent* event;
2872             window.imeData = data;
2873             fix_device_modifiers_by_generic(&localFlags);
2875             // An NSEvent created with +keyEventWithType:... is internally marked
2876             // as synthetic and doesn't get sent through input methods.  But one
2877             // created from a CGEvent doesn't have that problem.
2878             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2879             CGEventSetFlags(c, localFlags);
2880             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2881             event = [NSEvent eventWithCGEvent:c];
2882             CFRelease(c);
2884             window.commandDone = FALSE;
2885             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
2886         }
2887         else
2888             ret = FALSE;
2889     });
2891     return ret;