user32/tests: Add test for SendMessage race condition.
[wine/multimedia.git] / dlls / winemac.drv / cocoa_window.m
blob48d0651c2999e84f5f6f20ce191c9c6d4d396a23
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 (nonatomic) BOOL shapeChangedSinceLastDraw;
186 @property (readonly, nonatomic) BOOL needsTransparency;
188 @property (nonatomic) BOOL colorKeyed;
189 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
190 @property (nonatomic) BOOL usePerPixelAlpha;
192 @property (assign, nonatomic) void* imeData;
193 @property (nonatomic) BOOL commandDone;
195 @property (retain, nonatomic) NSTimer* liveResizeDisplayTimer;
197     - (void) updateColorSpace;
199     - (BOOL) becameEligibleParentOrChild;
200     - (void) becameIneligibleChild;
202 @end
205 @implementation WineContentView
207     - (void) dealloc
208     {
209         [markedText release];
210         [glContexts release];
211         [pendingGlContexts release];
212         [super dealloc];
213     }
215     - (BOOL) isFlipped
216     {
217         return YES;
218     }
220     - (void) drawRect:(NSRect)rect
221     {
222         WineWindow* window = (WineWindow*)[self window];
224         for (WineOpenGLContext* context in pendingGlContexts)
225         {
226             if (!clearedGlSurface)
227             {
228                 context.shouldClearToBlack = TRUE;
229                 clearedGlSurface = TRUE;
230             }
231             context.needsUpdate = TRUE;
232         }
233         [glContexts addObjectsFromArray:pendingGlContexts];
234         [pendingGlContexts removeAllObjects];
236         if ([window contentView] != self)
237             return;
239         if (window.surface && window.surface_mutex &&
240             !pthread_mutex_lock(window.surface_mutex))
241         {
242             const CGRect* rects;
243             int count;
245             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
246             {
247                 CGContextRef context;
248                 int i;
250                 [window.shape addClip];
252                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
253                 CGContextSetBlendMode(context, kCGBlendModeCopy);
255                 for (i = 0; i < count; i++)
256                 {
257                     CGRect imageRect;
258                     CGImageRef image;
260                     imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
261                     image = create_surface_image(window.surface, &imageRect, FALSE);
263                     if (image)
264                     {
265                         if (window.colorKeyed)
266                         {
267                             CGImageRef maskedImage;
268                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
269                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
270                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
271                             maskedImage = CGImageCreateWithMaskingColors(image, components);
272                             if (maskedImage)
273                             {
274                                 CGImageRelease(image);
275                                 image = maskedImage;
276                             }
277                         }
279                         CGContextDrawImage(context, imageRect, image);
281                         CGImageRelease(image);
282                     }
283                 }
284             }
286             pthread_mutex_unlock(window.surface_mutex);
287         }
289         // If the window may be transparent, then we have to invalidate the
290         // shadow every time we draw.  Also, if this is the first time we've
291         // drawn since changing from transparent to opaque.
292         if (![window isOpaque] || window.shapeChangedSinceLastDraw)
293         {
294             window.shapeChangedSinceLastDraw = FALSE;
295             [window invalidateShadow];
296         }
297     }
299     - (void) addGLContext:(WineOpenGLContext*)context
300     {
301         if (!glContexts)
302             glContexts = [[NSMutableArray alloc] init];
303         if (!pendingGlContexts)
304             pendingGlContexts = [[NSMutableArray alloc] init];
306         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
307         {
308             [glContexts addObject:context];
309             if (!clearedGlSurface)
310             {
311                 context.shouldClearToBlack = TRUE;
312                 clearedGlSurface = TRUE;
313             }
314             context.needsUpdate = TRUE;
315         }
316         else
317         {
318             [pendingGlContexts addObject:context];
319             [self setNeedsDisplay:YES];
320         }
322         [(WineWindow*)[self window] updateColorSpace];
323     }
325     - (void) removeGLContext:(WineOpenGLContext*)context
326     {
327         [glContexts removeObjectIdenticalTo:context];
328         [pendingGlContexts removeObjectIdenticalTo:context];
329         [(WineWindow*)[self window] updateColorSpace];
330     }
332     - (void) updateGLContexts
333     {
334         for (WineOpenGLContext* context in glContexts)
335             context.needsUpdate = TRUE;
336     }
338     - (BOOL) hasGLContext
339     {
340         return [glContexts count] || [pendingGlContexts count];
341     }
343     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
344     {
345         return YES;
346     }
348     - (BOOL) preservesContentDuringLiveResize
349     {
350         // Returning YES from this tells Cocoa to keep our view's content during
351         // a Cocoa-driven resize.  In theory, we're also supposed to override
352         // -setFrameSize: to mark exposed sections as needing redisplay, but
353         // user32 will take care of that in a roundabout way.  This way, we don't
354         // redraw until the window surface is flushed.
355         //
356         // This doesn't do anything when we resize the window ourselves.
357         return YES;
358     }
360     - (BOOL)acceptsFirstResponder
361     {
362         return [[self window] contentView] == self;
363     }
365     - (void) completeText:(NSString*)text
366     {
367         macdrv_event* event;
368         WineWindow* window = (WineWindow*)[self window];
370         event = macdrv_create_event(IM_SET_TEXT, window);
371         event->im_set_text.data = [window imeData];
372         event->im_set_text.text = (CFStringRef)[text copy];
373         event->im_set_text.complete = TRUE;
375         [[window queue] postEvent:event];
377         macdrv_release_event(event);
379         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
380         markedTextSelection = NSMakeRange(0, 0);
381         [[self inputContext] discardMarkedText];
382     }
384     - (NSFocusRingType) focusRingType
385     {
386         return NSFocusRingTypeNone;
387     }
389     /*
390      * ---------- NSTextInputClient methods ----------
391      */
392     - (NSTextInputContext*) inputContext
393     {
394         if (!markedText)
395             markedText = [[NSMutableAttributedString alloc] init];
396         return [super inputContext];
397     }
399     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
400     {
401         if ([string isKindOfClass:[NSAttributedString class]])
402             string = [string string];
404         if ([string isKindOfClass:[NSString class]])
405             [self completeText:string];
406     }
408     - (void) doCommandBySelector:(SEL)aSelector
409     {
410         [(WineWindow*)[self window] setCommandDone:TRUE];
411     }
413     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
414     {
415         if ([string isKindOfClass:[NSAttributedString class]])
416             string = [string string];
418         if ([string isKindOfClass:[NSString class]])
419         {
420             macdrv_event* event;
421             WineWindow* window = (WineWindow*)[self window];
423             if (replacementRange.location == NSNotFound)
424                 replacementRange = NSMakeRange(0, [markedText length]);
426             [markedText replaceCharactersInRange:replacementRange withString:string];
427             markedTextSelection = selectedRange;
428             markedTextSelection.location += replacementRange.location;
430             event = macdrv_create_event(IM_SET_TEXT, window);
431             event->im_set_text.data = [window imeData];
432             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
433             event->im_set_text.complete = FALSE;
434             event->im_set_text.cursor_pos = markedTextSelection.location;
436             [[window queue] postEvent:event];
438             macdrv_release_event(event);
440             [[self inputContext] invalidateCharacterCoordinates];
441         }
442     }
444     - (void) unmarkText
445     {
446         [self completeText:nil];
447     }
449     - (NSRange) selectedRange
450     {
451         return markedTextSelection;
452     }
454     - (NSRange) markedRange
455     {
456         NSRange range = NSMakeRange(0, [markedText length]);
457         if (!range.length)
458             range.location = NSNotFound;
459         return range;
460     }
462     - (BOOL) hasMarkedText
463     {
464         return [markedText length] > 0;
465     }
467     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
468     {
469         if (aRange.location >= [markedText length])
470             return nil;
472         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
473         if (actualRange)
474             *actualRange = aRange;
475         return [markedText attributedSubstringFromRange:aRange];
476     }
478     - (NSArray*) validAttributesForMarkedText
479     {
480         return [NSArray array];
481     }
483     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
484     {
485         macdrv_query* query;
486         WineWindow* window = (WineWindow*)[self window];
487         NSRect ret;
489         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
491         query = macdrv_create_query();
492         query->type = QUERY_IME_CHAR_RECT;
493         query->window = (macdrv_window)[window retain];
494         query->ime_char_rect.data = [window imeData];
495         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
497         if ([window.queue query:query timeout:1])
498         {
499             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
500             ret = NSRectFromCGRect(query->ime_char_rect.rect);
501             [[WineApplicationController sharedController] flipRect:&ret];
502         }
503         else
504             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
506         macdrv_release_query(query);
508         if (actualRange)
509             *actualRange = aRange;
510         return ret;
511     }
513     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
514     {
515         return NSNotFound;
516     }
518     - (NSInteger) windowLevel
519     {
520         return [[self window] level];
521     }
523 @end
526 @implementation WineWindow
528     static WineWindow* causing_becomeKeyWindow;
530     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
531     @synthesize surface, surface_mutex;
532     @synthesize shape, shapeChangedSinceLastDraw;
533     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
534     @synthesize usePerPixelAlpha;
535     @synthesize imeData, commandDone;
536     @synthesize liveResizeDisplayTimer;
538     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
539                                  windowFrame:(NSRect)window_frame
540                                         hwnd:(void*)hwnd
541                                        queue:(WineEventQueue*)queue
542     {
543         WineWindow* window;
544         WineContentView* contentView;
545         NSTrackingArea* trackingArea;
546         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
548         [[WineApplicationController sharedController] flipRect:&window_frame];
550         window = [[[self alloc] initWithContentRect:window_frame
551                                           styleMask:style_mask_for_features(wf)
552                                             backing:NSBackingStoreBuffered
553                                               defer:YES] autorelease];
555         if (!window) return nil;
557         /* Standardize windows to eliminate differences between titled and
558            borderless windows and between NSWindow and NSPanel. */
559         [window setHidesOnDeactivate:NO];
560         [window setReleasedWhenClosed:NO];
562         [window setOneShot:YES];
563         [window disableCursorRects];
564         [window setShowsResizeIndicator:NO];
565         [window setHasShadow:wf->shadow];
566         [window setAcceptsMouseMovedEvents:YES];
567         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
568         [window setDelegate:window];
569         window.hwnd = hwnd;
570         window.queue = queue;
571         window->savedContentMinSize = NSZeroSize;
572         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
573         window->resizable = wf->resizable;
575         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
576                                                                   (NSString*)kUTTypeContent,
577                                                                   nil]];
579         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
580         if (!contentView)
581             return nil;
582         [contentView setAutoresizesSubviews:NO];
584         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
585            because they give us mouse moves in the background. */
586         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
587                                                      options:(NSTrackingMouseMoved |
588                                                               NSTrackingActiveAlways |
589                                                               NSTrackingInVisibleRect)
590                                                        owner:window
591                                                     userInfo:nil] autorelease];
592         if (!trackingArea)
593             return nil;
594         [contentView addTrackingArea:trackingArea];
596         [window setContentView:contentView];
597         [window setInitialFirstResponder:contentView];
599         [nc addObserver:window
600                selector:@selector(updateFullscreen)
601                    name:NSApplicationDidChangeScreenParametersNotification
602                  object:NSApp];
603         [window updateFullscreen];
605         [nc addObserver:window
606                selector:@selector(applicationWillHide)
607                    name:NSApplicationWillHideNotification
608                  object:NSApp];
609         [nc addObserver:window
610                selector:@selector(applicationDidUnhide)
611                    name:NSApplicationDidUnhideNotification
612                  object:NSApp];
614         return window;
615     }
617     - (void) dealloc
618     {
619         [[NSNotificationCenter defaultCenter] removeObserver:self];
620         [liveResizeDisplayTimer invalidate];
621         [liveResizeDisplayTimer release];
622         [queue release];
623         [latentChildWindows release];
624         [latentParentWindow release];
625         [shape release];
626         [super dealloc];
627     }
629     - (BOOL) preventResizing
630     {
631         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
632         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || maximized || preventForClipping);
633     }
635     - (void) adjustFeaturesForState
636     {
637         NSUInteger style = [self styleMask];
639         if (style & NSClosableWindowMask)
640             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
641         if (style & NSMiniaturizableWindowMask)
642             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
643         if (style & NSResizableWindowMask)
644             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
645         if ([self respondsToSelector:@selector(toggleFullScreen:)])
646         {
647             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
648                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
649         }
651         if ([self preventResizing])
652         {
653             NSSize size = [self contentRectForFrameRect:[self frame]].size;
654             [self setContentMinSize:size];
655             [self setContentMaxSize:size];
656         }
657         else
658         {
659             [self setContentMaxSize:savedContentMaxSize];
660             [self setContentMinSize:savedContentMinSize];
661         }
663         if (allow_immovable_windows || cursor_clipping_locks_windows)
664         {
665             if (allow_immovable_windows && (disabled || maximized))
666                 [self setMovable:NO];
667             else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
668                 [self setMovable:NO];
669             else
670                 [self setMovable:YES];
671         }
672     }
674     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
675     {
676         if ([self respondsToSelector:@selector(toggleFullScreen:)])
677         {
678             NSUInteger style = [self styleMask];
680             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
681                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask))
682             {
683                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
684                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
685             }
686             else
687             {
688                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
689                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
690                 if (style & NSFullScreenWindowMask)
691                     [self toggleFullScreen:nil];
692             }
693         }
695         if (behavior != [self collectionBehavior])
696         {
697             [self setCollectionBehavior:behavior];
698             [self adjustFeaturesForState];
699         }
700     }
702     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
703     {
704         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
705                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
706         NSUInteger currentStyle = [self styleMask];
707         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
709         if (newStyle != currentStyle)
710         {
711             NSString* title = [[[self title] copy] autorelease];
712             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
713             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
714             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
715             {
716                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
717                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
718                 // just NSTitledWindowMask, the window buttons should disappear rather
719                 // than just being disabled.  But they don't.  Similarly in reverse.
720                 // The workaround is to also toggle NSClosableWindowMask at the same time.
721                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
722             }
723             [self setStyleMask:newStyle];
725             // -setStyleMask: resets the firstResponder to the window.  Set it
726             // back to the content view.
727             if ([[self contentView] acceptsFirstResponder])
728                 [self makeFirstResponder:[self contentView]];
730             [self adjustFullScreenBehavior:[self collectionBehavior]];
732             if ([[self title] length] == 0 && [title length] > 0)
733                 [self setTitle:title];
734         }
736         resizable = wf->resizable;
737         [self adjustFeaturesForState];
738         [self setHasShadow:wf->shadow];
739     }
741     // Indicates if the window would be visible if the app were not hidden.
742     - (BOOL) wouldBeVisible
743     {
744         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
745     }
747     - (BOOL) isOrderedIn
748     {
749         return [self wouldBeVisible] || [self isMiniaturized];
750     }
752     - (NSInteger) minimumLevelForActive:(BOOL)active
753     {
754         NSInteger level;
756         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
757                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
758             level = NSFloatingWindowLevel;
759         else
760             level = NSNormalWindowLevel;
762         if (active)
763         {
764             BOOL captured;
766             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
768             if (captured || fullscreen)
769             {
770                 if (captured)
771                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
772                 else
773                     level = NSMainMenuWindowLevel + 1;
775                 if (self.floating)
776                     level++;
777             }
778         }
780         return level;
781     }
783     - (void) postDidUnminimizeEvent
784     {
785         macdrv_event* event;
787         /* Coalesce events by discarding any previous ones still in the queue. */
788         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
789                                forWindow:self];
791         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
792         [queue postEvent:event];
793         macdrv_release_event(event);
794     }
796     - (void) setMacDrvState:(const struct macdrv_window_state*)state
797     {
798         NSWindowCollectionBehavior behavior;
800         self.disabled = state->disabled;
801         self.noActivate = state->no_activate;
803         if (self.floating != state->floating)
804         {
805             self.floating = state->floating;
806             if (state->floating)
807             {
808                 // Became floating.  If child of non-floating window, make that
809                 // relationship latent.
810                 WineWindow* parent = (WineWindow*)[self parentWindow];
811                 if (parent && !parent.floating)
812                     [self becameIneligibleChild];
813             }
814             else
815             {
816                 // Became non-floating.  If parent of floating children, make that
817                 // relationship latent.
818                 WineWindow* child;
819                 for (child in [[[self childWindows] copy] autorelease])
820                 {
821                     if (child.floating)
822                         [child becameIneligibleChild];
823                 }
824             }
826             // Check our latent relationships.  If floating status was the only
827             // reason they were latent, then make them active.
828             if ([self isVisible])
829                 [self becameEligibleParentOrChild];
831             [[WineApplicationController sharedController] adjustWindowLevels];
832         }
834         behavior = NSWindowCollectionBehaviorDefault;
835         if (state->excluded_by_expose)
836             behavior |= NSWindowCollectionBehaviorTransient;
837         else
838             behavior |= NSWindowCollectionBehaviorManaged;
839         if (state->excluded_by_cycle)
840         {
841             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
842             if ([self isOrderedIn])
843                 [NSApp removeWindowsItem:self];
844         }
845         else
846         {
847             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
848             if ([self isOrderedIn])
849                 [NSApp addWindowsItem:self title:[self title] filename:NO];
850         }
851         [self adjustFullScreenBehavior:behavior];
853         if (state->minimized_valid)
854         {
855             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
857             pendingMinimize = FALSE;
858             if (state->minimized && ![self isMiniaturized])
859             {
860                 if ([self wouldBeVisible])
861                 {
862                     if ([self styleMask] & NSFullScreenWindowMask)
863                     {
864                         [self postDidUnminimizeEvent];
865                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
866                     }
867                     else
868                     {
869                         [super miniaturize:nil];
870                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
871                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
872                                    event_mask_for_type(WINDOW_LOST_FOCUS);
873                     }
874                 }
875                 else
876                     pendingMinimize = TRUE;
877             }
878             else if (!state->minimized && [self isMiniaturized])
879             {
880                 ignore_windowDeminiaturize = TRUE;
881                 [self deminiaturize:nil];
882                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
883             }
885             if (discard)
886                 [queue discardEventsMatchingMask:discard forWindow:self];
887         }
889         if (state->maximized != maximized)
890         {
891             maximized = state->maximized;
892             [self adjustFeaturesForState];
893         }
894     }
896     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
897     {
898         BOOL reordered = FALSE;
900         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
901         {
902             if ([self level] > [child level])
903                 [child setLevel:[self level]];
904             [self addChildWindow:child ordered:NSWindowAbove];
905             [latentChildWindows removeObjectIdenticalTo:child];
906             child.latentParentWindow = nil;
907             reordered = TRUE;
908         }
909         else
910         {
911             if (!latentChildWindows)
912                 latentChildWindows = [[NSMutableArray alloc] init];
913             if (![latentChildWindows containsObject:child])
914                 [latentChildWindows addObject:child];
915             child.latentParentWindow = self;
916         }
918         return reordered;
919     }
921     - (BOOL) addChildWineWindow:(WineWindow*)child
922     {
923         return [self addChildWineWindow:child assumeVisible:FALSE];
924     }
926     - (void) removeChildWineWindow:(WineWindow*)child
927     {
928         [self removeChildWindow:child];
929         if (child.latentParentWindow == self)
930             child.latentParentWindow = nil;
931         [latentChildWindows removeObjectIdenticalTo:child];
932     }
934     - (BOOL) becameEligibleParentOrChild
935     {
936         BOOL reordered = FALSE;
937         NSUInteger count;
939         if (latentParentWindow.floating || !self.floating)
940         {
941             // If we aren't visible currently, we assume that we should be and soon
942             // will be.  So, if the latent parent is visible that's enough to assume
943             // we can establish the parent-child relationship in Cocoa.  That will
944             // actually make us visible, which is fine.
945             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
946                 reordered = TRUE;
947         }
949         // Here, though, we may not actually be visible yet and adding a child
950         // won't make us visible.  The caller will have to call this method
951         // again after actually making us visible.
952         if ([self isVisible] && (count = [latentChildWindows count]))
953         {
954             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
955             NSUInteger i;
957             for (i = 0; i < count; i++)
958             {
959                 WineWindow* child = [latentChildWindows objectAtIndex:i];
960                 if ([child isVisible] && (self.floating || !child.floating))
961                 {
962                     if (child.latentParentWindow == self)
963                     {
964                         if ([self level] > [child level])
965                             [child setLevel:[self level]];
966                         [self addChildWindow:child ordered:NSWindowAbove];
967                         child.latentParentWindow = nil;
968                         reordered = TRUE;
969                     }
970                     else
971                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
972                     [indexesToRemove addIndex:i];
973                 }
974             }
976             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
977         }
979         return reordered;
980     }
982     - (void) becameIneligibleChild
983     {
984         WineWindow* parent = (WineWindow*)[self parentWindow];
985         if (parent)
986         {
987             if (!parent->latentChildWindows)
988                 parent->latentChildWindows = [[NSMutableArray alloc] init];
989             [parent->latentChildWindows insertObject:self atIndex:0];
990             self.latentParentWindow = parent;
991             [parent removeChildWindow:self];
992         }
993     }
995     - (void) becameIneligibleParentOrChild
996     {
997         NSArray* childWindows = [self childWindows];
999         [self becameIneligibleChild];
1001         if ([childWindows count])
1002         {
1003             WineWindow* child;
1005             childWindows = [[childWindows copy] autorelease];
1006             for (child in childWindows)
1007             {
1008                 child.latentParentWindow = self;
1009                 [self removeChildWindow:child];
1010             }
1012             if (latentChildWindows)
1013                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1014             else
1015                 latentChildWindows = [childWindows mutableCopy];
1016         }
1017     }
1019     // Determine if, among Wine windows, this window is directly above or below
1020     // a given other Wine window with no other Wine window intervening.
1021     // Intervening non-Wine windows are ignored.
1022     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1023     {
1024         NSNumber* windowNumber;
1025         NSNumber* otherWindowNumber;
1026         NSArray* windowNumbers;
1027         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1029         if (![self isVisible] || ![otherWindow isVisible])
1030             return FALSE;
1032         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1033         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1034         windowNumbers = [[self class] windowNumbersWithOptions:0];
1035         windowIndex = [windowNumbers indexOfObject:windowNumber];
1036         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1038         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1039             return FALSE;
1041         if (orderingMode == NSWindowAbove)
1042         {
1043             lowIndex = windowIndex;
1044             highIndex = otherWindowIndex;
1045         }
1046         else if (orderingMode == NSWindowBelow)
1047         {
1048             lowIndex = otherWindowIndex;
1049             highIndex = windowIndex;
1050         }
1051         else
1052             return FALSE;
1054         if (highIndex <= lowIndex)
1055             return FALSE;
1057         for (i = lowIndex + 1; i < highIndex; i++)
1058         {
1059             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1060             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1061             if ([interveningWindow isKindOfClass:[WineWindow class]])
1062                 return FALSE;
1063         }
1065         return TRUE;
1066     }
1068     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1069     {
1070         NSMutableArray* windowNumbers;
1071         NSNumber* childWindowNumber;
1072         NSUInteger otherIndex, limit;
1073         NSArray* origChildren;
1074         NSMutableArray* children;
1076         // Get the z-order from the window server and modify it to reflect the
1077         // requested window ordering.
1078         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1079         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1080         [windowNumbers removeObject:childWindowNumber];
1081         otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1082         [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1084         // Get our child windows and sort them in the reverse of the desired
1085         // z-order (back-to-front).
1086         origChildren = [self childWindows];
1087         children = [[origChildren mutableCopy] autorelease];
1088         [children sortWithOptions:NSSortStable
1089                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1090             NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1091             NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1092             NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1093             NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1094             if (index1 == NSNotFound)
1095             {
1096                 if (index2 == NSNotFound)
1097                     return NSOrderedSame;
1098                 else
1099                     return NSOrderedAscending;
1100             }
1101             else if (index2 == NSNotFound)
1102                 return NSOrderedDescending;
1103             else if (index1 < index2)
1104                 return NSOrderedDescending;
1105             else if (index2 < index1)
1106                 return NSOrderedAscending;
1108             return NSOrderedSame;
1109         }];
1111         // If the current and desired children arrays match up to a point, leave
1112         // those matching children alone.
1113         limit = MIN([origChildren count], [children count]);
1114         for (otherIndex = 0; otherIndex < limit; otherIndex++)
1115         {
1116             if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1117                 break;
1118         }
1119         [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1121         // Remove all of the child windows and re-add them back-to-front so they
1122         // are in the desired order.
1123         for (other in children)
1124             [self removeChildWindow:other];
1125         for (other in children)
1126             [self addChildWindow:other ordered:NSWindowAbove];
1127     }
1129     /* Returns whether or not the window was ordered in, which depends on if
1130        its frame intersects any screen. */
1131     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1132     {
1133         WineApplicationController* controller = [WineApplicationController sharedController];
1134         if (![self isMiniaturized])
1135         {
1136             BOOL needAdjustWindowLevels = FALSE;
1137             BOOL wasVisible;
1139             [controller transformProcessToForeground];
1140             [NSApp unhide:nil];
1141             wasVisible = [self isVisible];
1143             if (activate)
1144                 [NSApp activateIgnoringOtherApps:YES];
1146             NSDisableScreenUpdates();
1148             if ([self becameEligibleParentOrChild])
1149                 needAdjustWindowLevels = TRUE;
1151             if (prev || next)
1152             {
1153                 WineWindow* other = [prev isVisible] ? prev : next;
1154                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1156                 if (![self isOrdered:orderingMode relativeTo:other])
1157                 {
1158                     WineWindow* parent = (WineWindow*)[self parentWindow];
1159                     WineWindow* otherParent = (WineWindow*)[other parentWindow];
1161                     // This window level may not be right for this window based
1162                     // on floating-ness, fullscreen-ness, etc.  But we set it
1163                     // temporarily to allow us to order the windows properly.
1164                     // Then the levels get fixed by -adjustWindowLevels.
1165                     if ([self level] != [other level])
1166                         [self setLevel:[other level]];
1167                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1169                     // The above call to -[NSWindow orderWindow:relativeTo:] won't
1170                     // reorder windows which are both children of the same parent
1171                     // relative to each other, so do that separately.
1172                     if (parent && parent == otherParent)
1173                         [parent order:orderingMode childWindow:self relativeTo:other];
1175                     needAdjustWindowLevels = TRUE;
1176                 }
1177             }
1178             else
1179             {
1180                 // Again, temporarily set level to make sure we can order to
1181                 // the right place.
1182                 next = [controller frontWineWindow];
1183                 if (next && [self level] < [next level])
1184                     [self setLevel:[next level]];
1185                 [self orderFront:nil];
1186                 needAdjustWindowLevels = TRUE;
1187             }
1189             if ([self becameEligibleParentOrChild])
1190                 needAdjustWindowLevels = TRUE;
1192             if (needAdjustWindowLevels)
1193             {
1194                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1195                     [controller updateFullscreenWindows];
1196                 [controller adjustWindowLevels];
1197             }
1199             if (pendingMinimize)
1200             {
1201                 [super miniaturize:nil];
1202                 pendingMinimize = FALSE;
1203             }
1205             NSEnableScreenUpdates();
1207             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1208                Generate a frame-changed event just in case.  The back end will ignore
1209                it if nothing actually changed. */
1210             [self windowDidResize:nil];
1212             if (![self isExcludedFromWindowsMenu])
1213                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1214         }
1215     }
1217     - (void) doOrderOut
1218     {
1219         WineApplicationController* controller = [WineApplicationController sharedController];
1220         BOOL wasVisible = [self isVisible];
1221         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1223         if ([self isMiniaturized])
1224             pendingMinimize = TRUE;
1226         [self becameIneligibleParentOrChild];
1227         if ([self isMiniaturized])
1228         {
1229             fakingClose = TRUE;
1230             [self close];
1231             fakingClose = FALSE;
1232         }
1233         else
1234             [self orderOut:nil];
1235         savedVisibleState = FALSE;
1236         if (wasVisible && wasOnActiveSpace && fullscreen)
1237             [controller updateFullscreenWindows];
1238         [controller adjustWindowLevels];
1239         [NSApp removeWindowsItem:self];
1241         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1242                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1243                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1244                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1245                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1246                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1247                                forWindow:self];
1248     }
1250     - (void) updateFullscreen
1251     {
1252         NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1253         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1255         if (nowFullscreen != fullscreen)
1256         {
1257             WineApplicationController* controller = [WineApplicationController sharedController];
1259             fullscreen = nowFullscreen;
1260             if ([self isVisible] && [self isOnActiveSpace])
1261                 [controller updateFullscreenWindows];
1263             [controller adjustWindowLevels];
1264         }
1265     }
1267     - (void) setFrameFromWine:(NSRect)contentRect
1268     {
1269         /* Origin is (left, top) in a top-down space.  Need to convert it to
1270            (left, bottom) in a bottom-up space. */
1271         [[WineApplicationController sharedController] flipRect:&contentRect];
1273         /* The back end is establishing a new window size and position.  It's
1274            not interested in any stale events regarding those that may be sitting
1275            in the queue. */
1276         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1277                                forWindow:self];
1279         if (!NSIsEmptyRect(contentRect))
1280         {
1281             NSRect frame, oldFrame;
1283             oldFrame = [self frame];
1284             frame = [self frameRectForContentRect:contentRect];
1285             if (!NSEqualRects(frame, oldFrame))
1286             {
1287                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1288                 BOOL needEnableScreenUpdates = FALSE;
1290                 if ([self preventResizing])
1291                 {
1292                     // Allow the following calls to -setFrame:display: to work even
1293                     // if they would violate the content size constraints. This
1294                     // shouldn't be necessary since the content size constraints are
1295                     // documented to not constrain that method, but it seems to be.
1296                     [self setContentMinSize:NSZeroSize];
1297                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1298                 }
1300                 if (equalSizes && [[self childWindows] count])
1301                 {
1302                     // If we change the window frame such that the origin moves
1303                     // but the size doesn't change, then Cocoa moves child
1304                     // windows with the parent.  We don't want that so we fake
1305                     // a change of the size and then change it back.
1306                     NSRect bogusFrame = frame;
1307                     bogusFrame.size.width++;
1309                     NSDisableScreenUpdates();
1310                     needEnableScreenUpdates = TRUE;
1312                     ignore_windowResize = TRUE;
1313                     [self setFrame:bogusFrame display:NO];
1314                     ignore_windowResize = FALSE;
1315                 }
1317                 [self setFrame:frame display:YES];
1318                 if ([self preventResizing])
1319                 {
1320                     [self setContentMinSize:contentRect.size];
1321                     [self setContentMaxSize:contentRect.size];
1322                 }
1324                 if (needEnableScreenUpdates)
1325                     NSEnableScreenUpdates();
1327                 if (!equalSizes)
1328                     [self updateColorSpace];
1330                 if (!enteringFullScreen &&
1331                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1332                     nonFullscreenFrame = frame;
1334                 [self updateFullscreen];
1336                 if ([self isOrderedIn])
1337                 {
1338                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1339                        event.  The back end will ignore it if nothing actually changed. */
1340                     [self windowDidResize:nil];
1341                 }
1342             }
1343         }
1344     }
1346     - (void) setMacDrvParentWindow:(WineWindow*)parent
1347     {
1348         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1349         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1350         {
1351             [oldParent removeChildWineWindow:self];
1352             [latentParentWindow removeChildWineWindow:self];
1353             if ([parent addChildWineWindow:self])
1354                 [[WineApplicationController sharedController] adjustWindowLevels];
1355         }
1356     }
1358     - (void) setDisabled:(BOOL)newValue
1359     {
1360         if (disabled != newValue)
1361         {
1362             disabled = newValue;
1363             [self adjustFeaturesForState];
1364         }
1365     }
1367     - (BOOL) needsTransparency
1368     {
1369         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1370     }
1372     - (void) checkTransparency
1373     {
1374         if (![self isOpaque] && !self.needsTransparency)
1375         {
1376             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1377             [self setOpaque:YES];
1378         }
1379         else if ([self isOpaque] && self.needsTransparency)
1380         {
1381             [self setBackgroundColor:[NSColor clearColor]];
1382             [self setOpaque:NO];
1383         }
1384     }
1386     - (void) setShape:(NSBezierPath*)newShape
1387     {
1388         if (shape == newShape) return;
1389         if (shape && newShape && [shape isEqual:newShape]) return;
1391         if (shape)
1392         {
1393             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1394             [shape release];
1395         }
1396         if (newShape)
1397             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1399         shape = [newShape copy];
1400         self.shapeChangedSinceLastDraw = TRUE;
1402         [self checkTransparency];
1403     }
1405     - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1406     {
1407         if (newTimer != liveResizeDisplayTimer)
1408         {
1409             [liveResizeDisplayTimer invalidate];
1410             [liveResizeDisplayTimer release];
1411             liveResizeDisplayTimer = [newTimer retain];
1412         }
1413     }
1415     - (void) makeFocused:(BOOL)activate
1416     {
1417         if (activate)
1418         {
1419             [[WineApplicationController sharedController] transformProcessToForeground];
1420             [NSApp activateIgnoringOtherApps:YES];
1421         }
1423         causing_becomeKeyWindow = self;
1424         [self makeKeyWindow];
1425         causing_becomeKeyWindow = nil;
1427         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1428                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1429                                forWindow:self];
1430     }
1432     - (void) postKey:(uint16_t)keyCode
1433              pressed:(BOOL)pressed
1434            modifiers:(NSUInteger)modifiers
1435                event:(NSEvent*)theEvent
1436     {
1437         macdrv_event* event;
1438         CGEventRef cgevent;
1439         WineApplicationController* controller = [WineApplicationController sharedController];
1441         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1442         event->key.keycode   = keyCode;
1443         event->key.modifiers = modifiers;
1444         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1446         if ((cgevent = [theEvent CGEvent]))
1447         {
1448             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1449                                                         kCGKeyboardEventKeyboardType);
1450             if (keyboardType != controller.keyboardType)
1451             {
1452                 controller.keyboardType = keyboardType;
1453                 [controller keyboardSelectionDidChange];
1454             }
1455         }
1457         [queue postEvent:event];
1459         macdrv_release_event(event);
1461         [controller noteKey:keyCode pressed:pressed];
1462     }
1464     - (void) postKeyEvent:(NSEvent *)theEvent
1465     {
1466         [self flagsChanged:theEvent];
1467         [self postKey:[theEvent keyCode]
1468               pressed:[theEvent type] == NSKeyDown
1469             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1470                 event:theEvent];
1471     }
1473     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1474     {
1475         savedContentMinSize = minSize;
1476         savedContentMaxSize = maxSize;
1477         if (![self preventResizing])
1478         {
1479             [self setContentMinSize:minSize];
1480             [self setContentMaxSize:maxSize];
1481         }
1482     }
1484     - (WineWindow*) ancestorWineWindow
1485     {
1486         WineWindow* ancestor = self;
1487         for (;;)
1488         {
1489             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1490             if ([parent isKindOfClass:[WineWindow class]])
1491                 ancestor = parent;
1492             else
1493                 break;
1494         }
1495         return ancestor;
1496     }
1498     - (void) postBroughtForwardEvent
1499     {
1500         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1501         [queue postEvent:event];
1502         macdrv_release_event(event);
1503     }
1505     - (void) updateForCursorClipping
1506     {
1507         [self adjustFeaturesForState];
1508     }
1511     /*
1512      * ---------- NSWindow method overrides ----------
1513      */
1514     - (BOOL) canBecomeKeyWindow
1515     {
1516         if (causing_becomeKeyWindow == self) return YES;
1517         if (self.disabled || self.noActivate) return NO;
1518         return [self isKeyWindow];
1519     }
1521     - (BOOL) canBecomeMainWindow
1522     {
1523         return [self canBecomeKeyWindow];
1524     }
1526     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1527     {
1528         // If a window is sized to completely cover a screen, then it's in
1529         // full-screen mode.  In that case, we don't allow NSWindow to constrain
1530         // it.
1531         NSArray* screens = [NSScreen screens];
1532         NSRect contentRect = [self contentRectForFrameRect:frameRect];
1533         if (!screen_covered_by_rect(contentRect, screens) &&
1534             frame_intersects_screens(frameRect, screens))
1535             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1536         return frameRect;
1537     }
1539     - (BOOL) isExcludedFromWindowsMenu
1540     {
1541         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1542     }
1544     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1545     {
1546         BOOL ret = [super validateMenuItem:menuItem];
1548         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1549             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1550         if ([menuItem action] == @selector(toggleFullScreen:) && self.disabled)
1551             ret = NO;
1553         return ret;
1554     }
1556     /* We don't call this.  It's the action method of the items in the Window menu. */
1557     - (void) makeKeyAndOrderFront:(id)sender
1558     {
1559         if ([self isMiniaturized])
1560             [self deminiaturize:nil];
1561         [self orderBelow:nil orAbove:nil activate:NO];
1562         [[self ancestorWineWindow] postBroughtForwardEvent];
1564         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1565             [[WineApplicationController sharedController] windowGotFocus:self];
1566     }
1568     - (void) sendEvent:(NSEvent*)event
1569     {
1570         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1571            interface control.  For example, Control-Tab switches focus among
1572            views.  We want to bypass that feature, so directly route key-down
1573            events to -keyDown:. */
1574         if ([event type] == NSKeyDown)
1575             [[self firstResponder] keyDown:event];
1576         else
1577             [super sendEvent:event];
1578     }
1580     - (void) miniaturize:(id)sender
1581     {
1582         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1583         [queue postEvent:event];
1584         macdrv_release_event(event);
1585     }
1587     - (void) toggleFullScreen:(id)sender
1588     {
1589         if (!self.disabled)
1590             [super toggleFullScreen:sender];
1591     }
1593     // We normally use the generic/calibrated RGB color space for the window,
1594     // rather than the device color space, to avoid expensive color conversion
1595     // which slows down drawing.  However, for windows displaying OpenGL, having
1596     // a different color space than the screen greatly reduces frame rates, often
1597     // limiting it to the display refresh rate.
1598     //
1599     // To avoid this, we switch back to the screen color space whenever the
1600     // window is covered by a view with an attached OpenGL context.
1601     - (void) updateColorSpace
1602     {
1603         NSRect contentRect = [[self contentView] frame];
1604         BOOL coveredByGLView = FALSE;
1605         for (WineContentView* view in [[self contentView] subviews])
1606         {
1607             if ([view hasGLContext])
1608             {
1609                 NSRect frame = [view convertRect:[view bounds] toView:nil];
1610                 if (NSContainsRect(frame, contentRect))
1611                 {
1612                     coveredByGLView = TRUE;
1613                     break;
1614                 }
1615             }
1616         }
1618         if (coveredByGLView)
1619             [self setColorSpace:nil];
1620         else
1621             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1622     }
1625     /*
1626      * ---------- NSResponder method overrides ----------
1627      */
1628     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1630     - (void) flagsChanged:(NSEvent *)theEvent
1631     {
1632         static const struct {
1633             NSUInteger  mask;
1634             uint16_t    keycode;
1635         } modifiers[] = {
1636             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
1637             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
1638             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
1639             { NX_DEVICELCTLKEYMASK,     kVK_Control },
1640             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
1641             { NX_DEVICELALTKEYMASK,     kVK_Option },
1642             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
1643             { NX_DEVICELCMDKEYMASK,     kVK_Command },
1644             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
1645         };
1647         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1648         NSUInteger changed;
1649         int i, last_changed;
1651         fix_device_modifiers_by_generic(&modifierFlags);
1652         changed = modifierFlags ^ lastModifierFlags;
1654         last_changed = -1;
1655         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1656             if (changed & modifiers[i].mask)
1657                 last_changed = i;
1659         for (i = 0; i <= last_changed; i++)
1660         {
1661             if (changed & modifiers[i].mask)
1662             {
1663                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1665                 if (i == last_changed)
1666                     lastModifierFlags = modifierFlags;
1667                 else
1668                 {
1669                     lastModifierFlags ^= modifiers[i].mask;
1670                     fix_generic_modifiers_by_device(&lastModifierFlags);
1671                 }
1673                 // Caps lock generates one event for each press-release action.
1674                 // We need to simulate a pair of events for each actual event.
1675                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1676                 {
1677                     [self postKey:modifiers[i].keycode
1678                           pressed:TRUE
1679                         modifiers:lastModifierFlags
1680                             event:(NSEvent*)theEvent];
1681                     pressed = FALSE;
1682                 }
1684                 [self postKey:modifiers[i].keycode
1685                       pressed:pressed
1686                     modifiers:lastModifierFlags
1687                         event:(NSEvent*)theEvent];
1688             }
1689         }
1690     }
1692     - (void) applicationWillHide
1693     {
1694         savedVisibleState = [self isVisible];
1695     }
1697     - (void) applicationDidUnhide
1698     {
1699         if ([self isVisible])
1700             [self becameEligibleParentOrChild];
1701     }
1704     /*
1705      * ---------- NSWindowDelegate methods ----------
1706      */
1707     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1708     {
1709         macdrv_query* query;
1710         NSSize size;
1712         query = macdrv_create_query();
1713         query->type = QUERY_MIN_MAX_INFO;
1714         query->window = (macdrv_window)[self retain];
1715         [self.queue query:query timeout:0.5];
1716         macdrv_release_query(query);
1718         size = [self contentMaxSize];
1719         if (proposedSize.width < size.width)
1720             size.width = proposedSize.width;
1721         if (proposedSize.height < size.height)
1722             size.height = proposedSize.height;
1723         return size;
1724     }
1726     - (void)windowDidBecomeKey:(NSNotification *)notification
1727     {
1728         WineApplicationController* controller = [WineApplicationController sharedController];
1729         NSEvent* event = [controller lastFlagsChanged];
1730         if (event)
1731             [self flagsChanged:event];
1733         if (causing_becomeKeyWindow == self) return;
1735         [controller windowGotFocus:self];
1736     }
1738     - (void)windowDidDeminiaturize:(NSNotification *)notification
1739     {
1740         WineApplicationController* controller = [WineApplicationController sharedController];
1742         if (!ignore_windowDeminiaturize)
1743             [self postDidUnminimizeEvent];
1744         ignore_windowDeminiaturize = FALSE;
1746         [self becameEligibleParentOrChild];
1748         if (fullscreen && [self isOnActiveSpace])
1749             [controller updateFullscreenWindows];
1750         [controller adjustWindowLevels];
1752         if (![self parentWindow])
1753             [self postBroughtForwardEvent];
1755         if (!self.disabled && !self.noActivate)
1756         {
1757             causing_becomeKeyWindow = self;
1758             [self makeKeyWindow];
1759             causing_becomeKeyWindow = nil;
1760             [controller windowGotFocus:self];
1761         }
1763         [self windowDidResize:notification];
1764     }
1766     - (void) windowDidEndLiveResize:(NSNotification *)notification
1767     {
1768         macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1769         [queue postEvent:event];
1770         macdrv_release_event(event);
1772         self.liveResizeDisplayTimer = nil;
1773     }
1775     - (void) windowDidEnterFullScreen:(NSNotification*)notification
1776     {
1777         enteringFullScreen = FALSE;
1778         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1779     }
1781     - (void) windowDidExitFullScreen:(NSNotification*)notification
1782     {
1783         exitingFullScreen = FALSE;
1784         [self setFrame:nonFullscreenFrame display:YES animate:NO];
1785         [self windowDidResize:nil];
1786     }
1788     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1789     {
1790         enteringFullScreen = FALSE;
1791         enteredFullScreenTime = 0;
1792     }
1794     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1795     {
1796         exitingFullScreen = FALSE;
1797         [self windowDidResize:nil];
1798     }
1800     - (void)windowDidMiniaturize:(NSNotification *)notification
1801     {
1802         if (fullscreen && [self isOnActiveSpace])
1803             [[WineApplicationController sharedController] updateFullscreenWindows];
1804     }
1806     - (void)windowDidMove:(NSNotification *)notification
1807     {
1808         [self windowDidResize:notification];
1809     }
1811     - (void)windowDidResignKey:(NSNotification *)notification
1812     {
1813         macdrv_event* event;
1815         if (causing_becomeKeyWindow) return;
1817         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1818         [queue postEvent:event];
1819         macdrv_release_event(event);
1820     }
1822     - (void)windowDidResize:(NSNotification *)notification
1823     {
1824         macdrv_event* event;
1825         NSRect frame = [self frame];
1827         if ([self inLiveResize])
1828         {
1829             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1830                 resizingFromLeft = TRUE;
1831             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1832                 resizingFromTop = TRUE;
1833         }
1835         frame = [self contentRectForFrameRect:frame];
1837         if (ignore_windowResize || exitingFullScreen) return;
1839         if ([self preventResizing])
1840         {
1841             [self setContentMinSize:frame.size];
1842             [self setContentMaxSize:frame.size];
1843         }
1845         [[WineApplicationController sharedController] flipRect:&frame];
1847         /* Coalesce events by discarding any previous ones still in the queue. */
1848         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1849                                forWindow:self];
1851         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1852         event->window_frame_changed.frame = NSRectToCGRect(frame);
1853         event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
1854         event->window_frame_changed.in_resize = [self inLiveResize];
1855         [queue postEvent:event];
1856         macdrv_release_event(event);
1858         [[[self contentView] inputContext] invalidateCharacterCoordinates];
1859         [self updateFullscreen];
1860     }
1862     - (BOOL)windowShouldClose:(id)sender
1863     {
1864         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1865         [queue postEvent:event];
1866         macdrv_release_event(event);
1867         return NO;
1868     }
1870     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
1871     {
1872         if (maximized)
1873         {
1874             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1875             [queue postEvent:event];
1876             macdrv_release_event(event);
1877             return NO;
1878         }
1879         else if (!resizable)
1880         {
1881             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
1882             [queue postEvent:event];
1883             macdrv_release_event(event);
1884             return NO;
1885         }
1887         return YES;
1888     }
1890     - (void) windowWillClose:(NSNotification*)notification
1891     {
1892         WineWindow* child;
1894         if (fakingClose) return;
1895         if (latentParentWindow)
1896         {
1897             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
1898             self.latentParentWindow = nil;
1899         }
1901         for (child in latentChildWindows)
1902         {
1903             if (child.latentParentWindow == self)
1904                 child.latentParentWindow = nil;
1905         }
1906         [latentChildWindows removeAllObjects];
1907     }
1909     - (void) windowWillEnterFullScreen:(NSNotification*)notification
1910     {
1911         enteringFullScreen = TRUE;
1912         nonFullscreenFrame = [self frame];
1913     }
1915     - (void) windowWillExitFullScreen:(NSNotification*)notification
1916     {
1917         exitingFullScreen = TRUE;
1918     }
1920     - (void)windowWillMiniaturize:(NSNotification *)notification
1921     {
1922         [self becameIneligibleParentOrChild];
1923     }
1925     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
1926     {
1927         if ([self inLiveResize])
1928         {
1929             NSRect rect;
1930             macdrv_query* query;
1932             rect = [self frame];
1933             if (resizingFromLeft)
1934                 rect.origin.x = NSMaxX(rect) - frameSize.width;
1935             if (!resizingFromTop)
1936                 rect.origin.y = NSMaxY(rect) - frameSize.height;
1937             rect.size = frameSize;
1938             rect = [self contentRectForFrameRect:rect];
1939             [[WineApplicationController sharedController] flipRect:&rect];
1941             query = macdrv_create_query();
1942             query->type = QUERY_RESIZE_SIZE;
1943             query->window = (macdrv_window)[self retain];
1944             query->resize_size.rect = NSRectToCGRect(rect);
1945             query->resize_size.from_left = resizingFromLeft;
1946             query->resize_size.from_top = resizingFromTop;
1948             if ([self.queue query:query timeout:0.1])
1949             {
1950                 rect = NSRectFromCGRect(query->resize_size.rect);
1951                 rect = [self frameRectForContentRect:rect];
1952                 frameSize = rect.size;
1953             }
1955             macdrv_release_query(query);
1956         }
1958         return frameSize;
1959     }
1961     - (void) windowWillStartLiveResize:(NSNotification *)notification
1962     {
1963         macdrv_query* query = macdrv_create_query();
1964         query->type = QUERY_RESIZE_START;
1965         query->window = (macdrv_window)[self retain];
1967         [self.queue query:query timeout:0.3];
1968         macdrv_release_query(query);
1970         frameAtResizeStart = [self frame];
1971         resizingFromLeft = resizingFromTop = FALSE;
1973         // There's a strange restriction in window redrawing during Cocoa-
1974         // managed window resizing.  Only calls to -[NSView setNeedsDisplay...]
1975         // that happen synchronously when Cocoa tells us that our window size
1976         // has changed or asynchronously in a short interval thereafter provoke
1977         // the window to redraw.  Calls to those methods that happen asynchronously
1978         // a half second or more after the last change of the window size aren't
1979         // heeded until the next resize-related user event (e.g. mouse movement).
1980         //
1981         // Wine often has a significant delay between when it's been told that
1982         // the window has changed size and when it can flush completed drawing.
1983         // So, our windows would get stuck with incomplete drawing for as long
1984         // as the user holds the mouse button down and doesn't move it.
1985         //
1986         // We address this by "manually" asking our windows to check if they need
1987         // redrawing every so often (during live resize only).
1988         self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1989                                                                        target:self
1990                                                                      selector:@selector(displayIfNeeded)
1991                                                                      userInfo:nil
1992                                                                       repeats:YES];
1993         [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1994                                      forMode:NSRunLoopCommonModes];
1995     }
1997     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
1998     {
1999         macdrv_query* query;
2000         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2001         NSSize maxSize;
2003         query = macdrv_create_query();
2004         query->type = QUERY_MIN_MAX_INFO;
2005         query->window = (macdrv_window)[self retain];
2006         [self.queue query:query timeout:0.5];
2007         macdrv_release_query(query);
2009         currentContentRect = [self contentRectForFrameRect:[self frame]];
2010         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2012         maxSize = [self contentMaxSize];
2013         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2014         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2016         // Try to keep the top-left corner where it is.
2017         newContentRect.origin.x = NSMinX(currentContentRect);
2018         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2020         // If that pushes the bottom or right off the screen, pull it up and to the left.
2021         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2022         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2023             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2024         if (NSMinY(newContentRect) < NSMinY(screenRect))
2025             newContentRect.origin.y = NSMinY(screenRect);
2027         // If that pushes the top or left off the screen, push it down and the right
2028         // again.  Do this last because the top-left corner is more important than the
2029         // bottom-right.
2030         if (NSMinX(newContentRect) < NSMinX(screenRect))
2031             newContentRect.origin.x = NSMinX(screenRect);
2032         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2033             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2035         return [self frameRectForContentRect:newContentRect];
2036     }
2039     /*
2040      * ---------- NSPasteboardOwner methods ----------
2041      */
2042     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2043     {
2044         macdrv_query* query = macdrv_create_query();
2045         query->type = QUERY_PASTEBOARD_DATA;
2046         query->window = (macdrv_window)[self retain];
2047         query->pasteboard_data.type = (CFStringRef)[type copy];
2049         [self.queue query:query timeout:3];
2050         macdrv_release_query(query);
2051     }
2054     /*
2055      * ---------- NSDraggingDestination methods ----------
2056      */
2057     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2058     {
2059         return [self draggingUpdated:sender];
2060     }
2062     - (void) draggingExited:(id <NSDraggingInfo>)sender
2063     {
2064         // This isn't really a query.  We don't need any response.  However, it
2065         // has to be processed in a similar manner as the other drag-and-drop
2066         // queries in order to maintain the proper order of operations.
2067         macdrv_query* query = macdrv_create_query();
2068         query->type = QUERY_DRAG_EXITED;
2069         query->window = (macdrv_window)[self retain];
2071         [self.queue query:query timeout:0.1];
2072         macdrv_release_query(query);
2073     }
2075     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2076     {
2077         NSDragOperation ret;
2078         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2079         NSPasteboard* pb = [sender draggingPasteboard];
2081         macdrv_query* query = macdrv_create_query();
2082         query->type = QUERY_DRAG_OPERATION;
2083         query->window = (macdrv_window)[self retain];
2084         query->drag_operation.x = pt.x;
2085         query->drag_operation.y = pt.y;
2086         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2087         query->drag_operation.accepted_op = NSDragOperationNone;
2088         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2090         [self.queue query:query timeout:3];
2091         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2092         macdrv_release_query(query);
2094         return ret;
2095     }
2097     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2098     {
2099         BOOL ret;
2100         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2101         NSPasteboard* pb = [sender draggingPasteboard];
2103         macdrv_query* query = macdrv_create_query();
2104         query->type = QUERY_DRAG_DROP;
2105         query->window = (macdrv_window)[self retain];
2106         query->drag_drop.x = pt.x;
2107         query->drag_drop.y = pt.y;
2108         query->drag_drop.op = [sender draggingSourceOperationMask];
2109         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2111         [self.queue query:query timeout:3 * 60 processEvents:YES];
2112         ret = query->status;
2113         macdrv_release_query(query);
2115         return ret;
2116     }
2118     - (BOOL) wantsPeriodicDraggingUpdates
2119     {
2120         return NO;
2121     }
2123 @end
2126 /***********************************************************************
2127  *              macdrv_create_cocoa_window
2129  * Create a Cocoa window with the given content frame and features (e.g.
2130  * title bar, close box, etc.).
2131  */
2132 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2133         CGRect frame, void* hwnd, macdrv_event_queue queue)
2135     __block WineWindow* window;
2137     OnMainThread(^{
2138         window = [[WineWindow createWindowWithFeatures:wf
2139                                            windowFrame:NSRectFromCGRect(frame)
2140                                                   hwnd:hwnd
2141                                                  queue:(WineEventQueue*)queue] retain];
2142     });
2144     return (macdrv_window)window;
2147 /***********************************************************************
2148  *              macdrv_destroy_cocoa_window
2150  * Destroy a Cocoa window.
2151  */
2152 void macdrv_destroy_cocoa_window(macdrv_window w)
2154     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2155     WineWindow* window = (WineWindow*)w;
2157     OnMainThread(^{
2158         [window doOrderOut];
2159         [window close];
2160     });
2161     [window.queue discardEventsMatchingMask:-1 forWindow:window];
2162     [window release];
2164     [pool release];
2167 /***********************************************************************
2168  *              macdrv_get_window_hwnd
2170  * Get the hwnd that was set for the window at creation.
2171  */
2172 void* macdrv_get_window_hwnd(macdrv_window w)
2174     WineWindow* window = (WineWindow*)w;
2175     return window.hwnd;
2178 /***********************************************************************
2179  *              macdrv_set_cocoa_window_features
2181  * Update a Cocoa window's features.
2182  */
2183 void macdrv_set_cocoa_window_features(macdrv_window w,
2184         const struct macdrv_window_features* wf)
2186     WineWindow* window = (WineWindow*)w;
2188     OnMainThread(^{
2189         [window setWindowFeatures:wf];
2190     });
2193 /***********************************************************************
2194  *              macdrv_set_cocoa_window_state
2196  * Update a Cocoa window's state.
2197  */
2198 void macdrv_set_cocoa_window_state(macdrv_window w,
2199         const struct macdrv_window_state* state)
2201     WineWindow* window = (WineWindow*)w;
2203     OnMainThread(^{
2204         [window setMacDrvState:state];
2205     });
2208 /***********************************************************************
2209  *              macdrv_set_cocoa_window_title
2211  * Set a Cocoa window's title.
2212  */
2213 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2214         size_t length)
2216     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2217     WineWindow* window = (WineWindow*)w;
2218     NSString* titleString;
2220     if (title)
2221         titleString = [NSString stringWithCharacters:title length:length];
2222     else
2223         titleString = @"";
2224     OnMainThreadAsync(^{
2225         [window setTitle:titleString];
2226         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2227             [NSApp changeWindowsItem:window title:titleString filename:NO];
2228     });
2230     [pool release];
2233 /***********************************************************************
2234  *              macdrv_order_cocoa_window
2236  * Reorder a Cocoa window relative to other windows.  If prev is
2237  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
2238  * it is ordered above that window.  Otherwise, it is ordered to the
2239  * front.
2240  */
2241 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2242         macdrv_window n, int activate)
2244     WineWindow* window = (WineWindow*)w;
2245     WineWindow* prev = (WineWindow*)p;
2246     WineWindow* next = (WineWindow*)n;
2248     OnMainThreadAsync(^{
2249         [window orderBelow:prev
2250                    orAbove:next
2251                   activate:activate];
2252     });
2253     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2254                                   forWindow:window];
2255     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2256                                 forWindow:next];
2259 /***********************************************************************
2260  *              macdrv_hide_cocoa_window
2262  * Hides a Cocoa window.
2263  */
2264 void macdrv_hide_cocoa_window(macdrv_window w)
2266     WineWindow* window = (WineWindow*)w;
2268     OnMainThread(^{
2269         [window doOrderOut];
2270     });
2273 /***********************************************************************
2274  *              macdrv_set_cocoa_window_frame
2276  * Move a Cocoa window.
2277  */
2278 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2280     WineWindow* window = (WineWindow*)w;
2282     OnMainThread(^{
2283         [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2284     });
2287 /***********************************************************************
2288  *              macdrv_get_cocoa_window_frame
2290  * Gets the frame of a Cocoa window.
2291  */
2292 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2294     WineWindow* window = (WineWindow*)w;
2296     OnMainThread(^{
2297         NSRect frame;
2299         frame = [window contentRectForFrameRect:[window frame]];
2300         [[WineApplicationController sharedController] flipRect:&frame];
2301         *out_frame = NSRectToCGRect(frame);
2302     });
2305 /***********************************************************************
2306  *              macdrv_set_cocoa_parent_window
2308  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
2309  * the parent window.
2310  */
2311 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2313     WineWindow* window = (WineWindow*)w;
2315     OnMainThread(^{
2316         [window setMacDrvParentWindow:(WineWindow*)parent];
2317     });
2320 /***********************************************************************
2321  *              macdrv_set_window_surface
2322  */
2323 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2325     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2326     WineWindow* window = (WineWindow*)w;
2328     OnMainThread(^{
2329         window.surface = surface;
2330         window.surface_mutex = mutex;
2331     });
2333     [pool release];
2336 /***********************************************************************
2337  *              macdrv_window_needs_display
2339  * Mark a window as needing display in a specified rect (in non-client
2340  * area coordinates).
2341  */
2342 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2344     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2345     WineWindow* window = (WineWindow*)w;
2347     OnMainThreadAsync(^{
2348         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2349     });
2351     [pool release];
2354 /***********************************************************************
2355  *              macdrv_set_window_shape
2357  * Sets the shape of a Cocoa window from an array of rectangles.  If
2358  * rects is NULL, resets the window's shape to its frame.
2359  */
2360 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2362     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2363     WineWindow* window = (WineWindow*)w;
2365     OnMainThread(^{
2366         if (!rects || !count)
2367             window.shape = nil;
2368         else
2369         {
2370             NSBezierPath* path;
2371             unsigned int i;
2373             path = [NSBezierPath bezierPath];
2374             for (i = 0; i < count; i++)
2375                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2376             window.shape = path;
2377         }
2378     });
2380     [pool release];
2383 /***********************************************************************
2384  *              macdrv_set_window_alpha
2385  */
2386 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2388     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2389     WineWindow* window = (WineWindow*)w;
2391     [window setAlphaValue:alpha];
2393     [pool release];
2396 /***********************************************************************
2397  *              macdrv_set_window_color_key
2398  */
2399 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2400                                  CGFloat keyBlue)
2402     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2403     WineWindow* window = (WineWindow*)w;
2405     OnMainThread(^{
2406         window.colorKeyed       = TRUE;
2407         window.colorKeyRed      = keyRed;
2408         window.colorKeyGreen    = keyGreen;
2409         window.colorKeyBlue     = keyBlue;
2410         [window checkTransparency];
2411     });
2413     [pool release];
2416 /***********************************************************************
2417  *              macdrv_clear_window_color_key
2418  */
2419 void macdrv_clear_window_color_key(macdrv_window w)
2421     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2422     WineWindow* window = (WineWindow*)w;
2424     OnMainThread(^{
2425         window.colorKeyed = FALSE;
2426         [window checkTransparency];
2427     });
2429     [pool release];
2432 /***********************************************************************
2433  *              macdrv_window_use_per_pixel_alpha
2434  */
2435 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2437     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2438     WineWindow* window = (WineWindow*)w;
2440     OnMainThread(^{
2441         window.usePerPixelAlpha = use_per_pixel_alpha;
2442         [window checkTransparency];
2443     });
2445     [pool release];
2448 /***********************************************************************
2449  *              macdrv_give_cocoa_window_focus
2451  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
2452  * orders it front and, if its frame was not within the desktop bounds,
2453  * Cocoa will typically move it on-screen.
2454  */
2455 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2457     WineWindow* window = (WineWindow*)w;
2459     OnMainThread(^{
2460         [window makeFocused:activate];
2461     });
2464 /***********************************************************************
2465  *              macdrv_set_window_min_max_sizes
2467  * Sets the window's minimum and maximum content sizes.
2468  */
2469 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2471     WineWindow* window = (WineWindow*)w;
2473     OnMainThread(^{
2474         [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2475     });
2478 /***********************************************************************
2479  *              macdrv_create_view
2481  * Creates and returns a view in the specified rect of the window.  The
2482  * caller is responsible for calling macdrv_dispose_view() on the view
2483  * when it is done with it.
2484  */
2485 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2487     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2488     WineWindow* window = (WineWindow*)w;
2489     __block WineContentView* view;
2491     if (CGRectIsNull(rect)) rect = CGRectZero;
2493     OnMainThread(^{
2494         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2496         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2497         [view setAutoresizesSubviews:NO];
2498         [nc addObserver:view
2499                selector:@selector(updateGLContexts)
2500                    name:NSViewGlobalFrameDidChangeNotification
2501                  object:view];
2502         [nc addObserver:view
2503                selector:@selector(updateGLContexts)
2504                    name:NSApplicationDidChangeScreenParametersNotification
2505                  object:NSApp];
2506         [[window contentView] addSubview:view];
2507         [window updateColorSpace];
2508     });
2510     [pool release];
2511     return (macdrv_view)view;
2514 /***********************************************************************
2515  *              macdrv_dispose_view
2517  * Destroys a view previously returned by macdrv_create_view.
2518  */
2519 void macdrv_dispose_view(macdrv_view v)
2521     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2522     WineContentView* view = (WineContentView*)v;
2524     OnMainThread(^{
2525         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2526         WineWindow* window = (WineWindow*)[view window];
2528         [nc removeObserver:view
2529                       name:NSViewGlobalFrameDidChangeNotification
2530                     object:view];
2531         [nc removeObserver:view
2532                       name:NSApplicationDidChangeScreenParametersNotification
2533                     object:NSApp];
2534         [view removeFromSuperview];
2535         [view release];
2536         [window updateColorSpace];
2537     });
2539     [pool release];
2542 /***********************************************************************
2543  *              macdrv_set_view_window_and_frame
2545  * Move a view to a new window and/or position within its window.  If w
2546  * is NULL, leave the view in its current window and just change its
2547  * frame.
2548  */
2549 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2551     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2552     WineContentView* view = (WineContentView*)v;
2553     WineWindow* window = (WineWindow*)w;
2555     if (CGRectIsNull(rect)) rect = CGRectZero;
2557     OnMainThread(^{
2558         BOOL changedWindow = (window && window != [view window]);
2559         NSRect newFrame = NSRectFromCGRect(rect);
2560         NSRect oldFrame = [view frame];
2561         BOOL needUpdateWindowColorSpace = FALSE;
2563         if (changedWindow)
2564         {
2565             WineWindow* oldWindow = (WineWindow*)[view window];
2566             [view removeFromSuperview];
2567             [oldWindow updateColorSpace];
2568             [[window contentView] addSubview:view];
2569             needUpdateWindowColorSpace = TRUE;
2570         }
2572         if (!NSEqualRects(oldFrame, newFrame))
2573         {
2574             if (!changedWindow)
2575                 [[view superview] setNeedsDisplayInRect:oldFrame];
2576             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2577                 [view setFrameSize:newFrame.size];
2578             else if (NSEqualSizes(oldFrame.size, newFrame.size))
2579                 [view setFrameOrigin:newFrame.origin];
2580             else
2581                 [view setFrame:newFrame];
2582             [view setNeedsDisplay:YES];
2583             needUpdateWindowColorSpace = TRUE;
2584         }
2586         if (needUpdateWindowColorSpace)
2587             [(WineWindow*)[view window] updateColorSpace];
2588     });
2590     [pool release];
2593 /***********************************************************************
2594  *              macdrv_add_view_opengl_context
2596  * Add an OpenGL context to the list being tracked for each view.
2597  */
2598 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2600     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2601     WineContentView* view = (WineContentView*)v;
2602     WineOpenGLContext *context = (WineOpenGLContext*)c;
2604     OnMainThread(^{
2605         [view addGLContext:context];
2606     });
2608     [pool release];
2611 /***********************************************************************
2612  *              macdrv_remove_view_opengl_context
2614  * Add an OpenGL context to the list being tracked for each view.
2615  */
2616 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2618     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2619     WineContentView* view = (WineContentView*)v;
2620     WineOpenGLContext *context = (WineOpenGLContext*)c;
2622     OnMainThreadAsync(^{
2623         [view removeGLContext:context];
2624     });
2626     [pool release];
2629 /***********************************************************************
2630  *              macdrv_window_background_color
2632  * Returns the standard Mac window background color as a 32-bit value of
2633  * the form 0x00rrggbb.
2634  */
2635 uint32_t macdrv_window_background_color(void)
2637     static uint32_t result;
2638     static dispatch_once_t once;
2640     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2641     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
2642     // of it is to draw with it.
2643     dispatch_once(&once, ^{
2644         OnMainThread(^{
2645             unsigned char rgbx[4];
2646             unsigned char *planes = rgbx;
2647             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2648                                                                                pixelsWide:1
2649                                                                                pixelsHigh:1
2650                                                                             bitsPerSample:8
2651                                                                           samplesPerPixel:3
2652                                                                                  hasAlpha:NO
2653                                                                                  isPlanar:NO
2654                                                                            colorSpaceName:NSCalibratedRGBColorSpace
2655                                                                              bitmapFormat:0
2656                                                                               bytesPerRow:4
2657                                                                              bitsPerPixel:32];
2658             [NSGraphicsContext saveGraphicsState];
2659             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2660             [[NSColor windowBackgroundColor] set];
2661             NSRectFill(NSMakeRect(0, 0, 1, 1));
2662             [NSGraphicsContext restoreGraphicsState];
2663             [bitmap release];
2664             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2665         });
2666     });
2668     return result;
2671 /***********************************************************************
2672  *              macdrv_send_text_input_event
2673  */
2674 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2676     __block BOOL ret;
2678     OnMainThread(^{
2679         WineWindow* window = (WineWindow*)[NSApp keyWindow];
2680         if (![window isKindOfClass:[WineWindow class]])
2681         {
2682             window = (WineWindow*)[NSApp mainWindow];
2683             if (![window isKindOfClass:[WineWindow class]])
2684                 window = [[WineApplicationController sharedController] frontWineWindow];
2685         }
2687         if (window)
2688         {
2689             NSUInteger localFlags = flags;
2690             CGEventRef c;
2691             NSEvent* event;
2693             window.imeData = data;
2694             fix_device_modifiers_by_generic(&localFlags);
2696             // An NSEvent created with +keyEventWithType:... is internally marked
2697             // as synthetic and doesn't get sent through input methods.  But one
2698             // created from a CGEvent doesn't have that problem.
2699             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2700             CGEventSetFlags(c, localFlags);
2701             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2702             event = [NSEvent eventWithCGEvent:c];
2703             CFRelease(c);
2705             window.commandDone = FALSE;
2706             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
2707         }
2708         else
2709             ret = FALSE;
2710     });
2712     return ret;