ieframe: Send command state change notifications from history navigation handlers.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blobb2238cb205f9b177a1b290e391e1d4d6de35a6fd
1 /*
2  * MACDRV Cocoa window code
3  *
4  * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  */
21 #import <Carbon/Carbon.h>
23 #import "cocoa_window.h"
25 #include "macdrv_cocoa.h"
26 #import "cocoa_app.h"
27 #import "cocoa_event.h"
28 #import "cocoa_opengl.h"
31 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
32 enum {
33     NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
34     NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
35     NSWindowFullScreenButton = 7,
36     NSFullScreenWindowMask = 1 << 14,
39 @interface NSWindow (WineFullScreenExtensions)
40     - (void) toggleFullScreen:(id)sender;
41 @end
42 #endif
45 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
46 enum {
47     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
51 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
53     NSUInteger style_mask;
55     if (wf->title_bar)
56     {
57         style_mask = NSTitledWindowMask;
58         if (wf->close_button) style_mask |= NSClosableWindowMask;
59         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
60         if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
61         if (wf->utility) style_mask |= NSUtilityWindowMask;
62     }
63     else style_mask = NSBorderlessWindowMask;
65     return style_mask;
69 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
71     NSScreen* screen;
72     for (screen in screens)
73     {
74         if (NSIntersectsRect(frame, [screen frame]))
75             return TRUE;
76     }
77     return FALSE;
81 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
83     for (NSScreen* screen in screens)
84     {
85         if (NSContainsRect(rect, [screen frame]))
86             return screen;
87     }
88     return nil;
92 /* We rely on the supposedly device-dependent modifier flags to distinguish the
93    keys on the left side of the keyboard from those on the right.  Some event
94    sources don't set those device-depdendent flags.  If we see a device-independent
95    flag for a modifier without either corresponding device-dependent flag, assume
96    the left one. */
97 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
99     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
100         *modifiers |= NX_DEVICELCMDKEYMASK;
101     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
102         *modifiers |= NX_DEVICELSHIFTKEYMASK;
103     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
104         *modifiers |= NX_DEVICELCTLKEYMASK;
105     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
106         *modifiers |= NX_DEVICELALTKEYMASK;
109 /* As we manipulate individual bits of a modifier mask, we can end up with
110    inconsistent sets of flags.  In particular, we might set or clear one of the
111    left/right-specific bits, but not the corresponding non-side-specific bit.
112    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
113    otherwise clear it. */
114 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
116     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
117         *modifiers |= NX_COMMANDMASK;
118     else
119         *modifiers &= ~NX_COMMANDMASK;
120     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
121         *modifiers |= NX_SHIFTMASK;
122     else
123         *modifiers &= ~NX_SHIFTMASK;
124     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
125         *modifiers |= NX_CONTROLMASK;
126     else
127         *modifiers &= ~NX_CONTROLMASK;
128     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
129         *modifiers |= NX_ALTERNATEMASK;
130     else
131         *modifiers &= ~NX_ALTERNATEMASK;
134 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
136     fix_device_modifiers_by_generic(&modifiers);
137     if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
138     {
139         modifiers |= NX_DEVICELCMDKEYMASK;
140         modifiers &= ~NX_DEVICELALTKEYMASK;
141     }
142     if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
143     {
144         modifiers |= NX_DEVICERCMDKEYMASK;
145         modifiers &= ~NX_DEVICERALTKEYMASK;
146     }
147     fix_generic_modifiers_by_device(&modifiers);
149     return modifiers;
153 @interface WineContentView : NSView <NSTextInputClient>
155     NSMutableArray* glContexts;
156     NSMutableArray* pendingGlContexts;
157     BOOL clearedGlSurface;
159     NSMutableAttributedString* markedText;
160     NSRange markedTextSelection;
163     - (void) addGLContext:(WineOpenGLContext*)context;
164     - (void) removeGLContext:(WineOpenGLContext*)context;
165     - (void) updateGLContexts;
167 @end
170 @interface WineWindow ()
172 @property (readwrite, nonatomic) BOOL disabled;
173 @property (readwrite, nonatomic) BOOL noActivate;
174 @property (readwrite, nonatomic) BOOL floating;
175 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
176 @property (retain, nonatomic) NSWindow* latentParentWindow;
178 @property (nonatomic) void* hwnd;
179 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
181 @property (nonatomic) void* surface;
182 @property (nonatomic) pthread_mutex_t* surface_mutex;
184 @property (copy, nonatomic) NSBezierPath* shape;
185 @property (copy, nonatomic) NSData* shapeData;
186 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
187 @property (readonly, nonatomic) BOOL needsTransparency;
189 @property (nonatomic) BOOL colorKeyed;
190 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
191 @property (nonatomic) BOOL usePerPixelAlpha;
193 @property (assign, nonatomic) void* imeData;
194 @property (nonatomic) BOOL commandDone;
196 @property (retain, nonatomic) NSTimer* liveResizeDisplayTimer;
198     - (void) updateColorSpace;
200     - (BOOL) becameEligibleParentOrChild;
201     - (void) becameIneligibleChild;
203 @end
206 @implementation WineContentView
208     - (void) dealloc
209     {
210         [markedText release];
211         [glContexts release];
212         [pendingGlContexts release];
213         [super dealloc];
214     }
216     - (BOOL) isFlipped
217     {
218         return YES;
219     }
221     - (void) drawRect:(NSRect)rect
222     {
223         WineWindow* window = (WineWindow*)[self window];
225         for (WineOpenGLContext* context in pendingGlContexts)
226         {
227             if (!clearedGlSurface)
228             {
229                 context.shouldClearToBlack = TRUE;
230                 clearedGlSurface = TRUE;
231             }
232             context.needsUpdate = TRUE;
233         }
234         [glContexts addObjectsFromArray:pendingGlContexts];
235         [pendingGlContexts removeAllObjects];
237         if ([window contentView] != self)
238             return;
240         if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
241         {
242             [[NSColor clearColor] setFill];
243             NSRectFill(rect);
245             [window.shape addClip];
247             [[NSColor windowBackgroundColor] setFill];
248             NSRectFill(rect);
249         }
251         if (window.surface && window.surface_mutex &&
252             !pthread_mutex_lock(window.surface_mutex))
253         {
254             const CGRect* rects;
255             int count;
257             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
258             {
259                 CGContextRef context;
260                 int i;
262                 [window.shape addClip];
264                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
265                 CGContextSetBlendMode(context, kCGBlendModeCopy);
267                 for (i = 0; i < count; i++)
268                 {
269                     CGRect imageRect;
270                     CGImageRef image;
272                     imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
273                     image = create_surface_image(window.surface, &imageRect, FALSE);
275                     if (image)
276                     {
277                         if (window.colorKeyed)
278                         {
279                             CGImageRef maskedImage;
280                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
281                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
282                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
283                             maskedImage = CGImageCreateWithMaskingColors(image, components);
284                             if (maskedImage)
285                             {
286                                 CGImageRelease(image);
287                                 image = maskedImage;
288                             }
289                         }
291                         CGContextDrawImage(context, imageRect, image);
293                         CGImageRelease(image);
294                     }
295                 }
296             }
298             pthread_mutex_unlock(window.surface_mutex);
299         }
301         // If the window may be transparent, then we have to invalidate the
302         // shadow every time we draw.  Also, if this is the first time we've
303         // drawn since changing from transparent to opaque.
304         if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
305         {
306             window.shapeChangedSinceLastDraw = FALSE;
307             [window invalidateShadow];
308         }
309     }
311     - (void) addGLContext:(WineOpenGLContext*)context
312     {
313         if (!glContexts)
314             glContexts = [[NSMutableArray alloc] init];
315         if (!pendingGlContexts)
316             pendingGlContexts = [[NSMutableArray alloc] init];
318         if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
319         {
320             [glContexts addObject:context];
321             if (!clearedGlSurface)
322             {
323                 context.shouldClearToBlack = TRUE;
324                 clearedGlSurface = TRUE;
325             }
326             context.needsUpdate = TRUE;
327         }
328         else
329         {
330             [pendingGlContexts addObject:context];
331             [self setNeedsDisplay:YES];
332         }
334         [(WineWindow*)[self window] updateColorSpace];
335     }
337     - (void) removeGLContext:(WineOpenGLContext*)context
338     {
339         [glContexts removeObjectIdenticalTo:context];
340         [pendingGlContexts removeObjectIdenticalTo:context];
341         [(WineWindow*)[self window] updateColorSpace];
342     }
344     - (void) updateGLContexts
345     {
346         for (WineOpenGLContext* context in glContexts)
347             context.needsUpdate = TRUE;
348     }
350     - (BOOL) hasGLContext
351     {
352         return [glContexts count] || [pendingGlContexts count];
353     }
355     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
356     {
357         return YES;
358     }
360     - (BOOL) preservesContentDuringLiveResize
361     {
362         // Returning YES from this tells Cocoa to keep our view's content during
363         // a Cocoa-driven resize.  In theory, we're also supposed to override
364         // -setFrameSize: to mark exposed sections as needing redisplay, but
365         // user32 will take care of that in a roundabout way.  This way, we don't
366         // redraw until the window surface is flushed.
367         //
368         // This doesn't do anything when we resize the window ourselves.
369         return YES;
370     }
372     - (BOOL)acceptsFirstResponder
373     {
374         return [[self window] contentView] == self;
375     }
377     - (BOOL) mouseDownCanMoveWindow
378     {
379         return NO;
380     }
382     - (void) completeText:(NSString*)text
383     {
384         macdrv_event* event;
385         WineWindow* window = (WineWindow*)[self window];
387         event = macdrv_create_event(IM_SET_TEXT, window);
388         event->im_set_text.data = [window imeData];
389         event->im_set_text.text = (CFStringRef)[text copy];
390         event->im_set_text.complete = TRUE;
392         [[window queue] postEvent:event];
394         macdrv_release_event(event);
396         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
397         markedTextSelection = NSMakeRange(0, 0);
398         [[self inputContext] discardMarkedText];
399     }
401     - (NSFocusRingType) focusRingType
402     {
403         return NSFocusRingTypeNone;
404     }
406     /*
407      * ---------- NSTextInputClient methods ----------
408      */
409     - (NSTextInputContext*) inputContext
410     {
411         if (!markedText)
412             markedText = [[NSMutableAttributedString alloc] init];
413         return [super inputContext];
414     }
416     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
417     {
418         if ([string isKindOfClass:[NSAttributedString class]])
419             string = [string string];
421         if ([string isKindOfClass:[NSString class]])
422             [self completeText:string];
423     }
425     - (void) doCommandBySelector:(SEL)aSelector
426     {
427         [(WineWindow*)[self window] setCommandDone:TRUE];
428     }
430     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
431     {
432         if ([string isKindOfClass:[NSAttributedString class]])
433             string = [string string];
435         if ([string isKindOfClass:[NSString class]])
436         {
437             macdrv_event* event;
438             WineWindow* window = (WineWindow*)[self window];
440             if (replacementRange.location == NSNotFound)
441                 replacementRange = NSMakeRange(0, [markedText length]);
443             [markedText replaceCharactersInRange:replacementRange withString:string];
444             markedTextSelection = selectedRange;
445             markedTextSelection.location += replacementRange.location;
447             event = macdrv_create_event(IM_SET_TEXT, window);
448             event->im_set_text.data = [window imeData];
449             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
450             event->im_set_text.complete = FALSE;
451             event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
453             [[window queue] postEvent:event];
455             macdrv_release_event(event);
457             [[self inputContext] invalidateCharacterCoordinates];
458         }
459     }
461     - (void) unmarkText
462     {
463         [self completeText:nil];
464     }
466     - (NSRange) selectedRange
467     {
468         return markedTextSelection;
469     }
471     - (NSRange) markedRange
472     {
473         NSRange range = NSMakeRange(0, [markedText length]);
474         if (!range.length)
475             range.location = NSNotFound;
476         return range;
477     }
479     - (BOOL) hasMarkedText
480     {
481         return [markedText length] > 0;
482     }
484     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
485     {
486         if (aRange.location >= [markedText length])
487             return nil;
489         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
490         if (actualRange)
491             *actualRange = aRange;
492         return [markedText attributedSubstringFromRange:aRange];
493     }
495     - (NSArray*) validAttributesForMarkedText
496     {
497         return [NSArray array];
498     }
500     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
501     {
502         macdrv_query* query;
503         WineWindow* window = (WineWindow*)[self window];
504         NSRect ret;
506         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
508         query = macdrv_create_query();
509         query->type = QUERY_IME_CHAR_RECT;
510         query->window = (macdrv_window)[window retain];
511         query->ime_char_rect.data = [window imeData];
512         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
514         if ([window.queue query:query timeout:1])
515         {
516             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
517             ret = NSRectFromCGRect(query->ime_char_rect.rect);
518             [[WineApplicationController sharedController] flipRect:&ret];
519         }
520         else
521             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
523         macdrv_release_query(query);
525         if (actualRange)
526             *actualRange = aRange;
527         return ret;
528     }
530     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
531     {
532         return NSNotFound;
533     }
535     - (NSInteger) windowLevel
536     {
537         return [[self window] level];
538     }
540 @end
543 @implementation WineWindow
545     static WineWindow* causing_becomeKeyWindow;
547     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
548     @synthesize surface, surface_mutex;
549     @synthesize shape, shapeData, shapeChangedSinceLastDraw;
550     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
551     @synthesize usePerPixelAlpha;
552     @synthesize imeData, commandDone;
553     @synthesize liveResizeDisplayTimer;
555     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
556                                  windowFrame:(NSRect)window_frame
557                                         hwnd:(void*)hwnd
558                                        queue:(WineEventQueue*)queue
559     {
560         WineWindow* window;
561         WineContentView* contentView;
562         NSTrackingArea* trackingArea;
563         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
565         [[WineApplicationController sharedController] flipRect:&window_frame];
567         window = [[[self alloc] initWithContentRect:window_frame
568                                           styleMask:style_mask_for_features(wf)
569                                             backing:NSBackingStoreBuffered
570                                               defer:YES] autorelease];
572         if (!window) return nil;
574         /* Standardize windows to eliminate differences between titled and
575            borderless windows and between NSWindow and NSPanel. */
576         [window setHidesOnDeactivate:NO];
577         [window setReleasedWhenClosed:NO];
579         [window setOneShot:YES];
580         [window disableCursorRects];
581         [window setShowsResizeIndicator:NO];
582         [window setHasShadow:wf->shadow];
583         [window setAcceptsMouseMovedEvents:YES];
584         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
585         [window setDelegate:window];
586         window.hwnd = hwnd;
587         window.queue = queue;
588         window->savedContentMinSize = NSZeroSize;
589         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
590         window->resizable = wf->resizable;
592         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
593                                                                   (NSString*)kUTTypeContent,
594                                                                   nil]];
596         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
597         if (!contentView)
598             return nil;
599         [contentView setAutoresizesSubviews:NO];
601         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
602            because they give us mouse moves in the background. */
603         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
604                                                      options:(NSTrackingMouseMoved |
605                                                               NSTrackingActiveAlways |
606                                                               NSTrackingInVisibleRect)
607                                                        owner:window
608                                                     userInfo:nil] autorelease];
609         if (!trackingArea)
610             return nil;
611         [contentView addTrackingArea:trackingArea];
613         [window setContentView:contentView];
614         [window setInitialFirstResponder:contentView];
616         [nc addObserver:window
617                selector:@selector(updateFullscreen)
618                    name:NSApplicationDidChangeScreenParametersNotification
619                  object:NSApp];
620         [window updateFullscreen];
622         [nc addObserver:window
623                selector:@selector(applicationWillHide)
624                    name:NSApplicationWillHideNotification
625                  object:NSApp];
626         [nc addObserver:window
627                selector:@selector(applicationDidUnhide)
628                    name:NSApplicationDidUnhideNotification
629                  object:NSApp];
631         return window;
632     }
634     - (void) dealloc
635     {
636         [[NSNotificationCenter defaultCenter] removeObserver:self];
637         [liveResizeDisplayTimer invalidate];
638         [liveResizeDisplayTimer release];
639         [queue release];
640         [latentChildWindows release];
641         [latentParentWindow release];
642         [shape release];
643         [shapeData release];
644         [super dealloc];
645     }
647     - (BOOL) preventResizing
648     {
649         BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
650         return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || maximized || preventForClipping);
651     }
653     - (void) adjustFeaturesForState
654     {
655         NSUInteger style = [self styleMask];
657         if (style & NSClosableWindowMask)
658             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
659         if (style & NSMiniaturizableWindowMask)
660             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
661         if (style & NSResizableWindowMask)
662             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
663         if ([self respondsToSelector:@selector(toggleFullScreen:)])
664         {
665             if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
666                 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
667         }
669         if ([self preventResizing])
670         {
671             NSSize size = [self contentRectForFrameRect:[self frame]].size;
672             [self setContentMinSize:size];
673             [self setContentMaxSize:size];
674         }
675         else
676         {
677             [self setContentMaxSize:savedContentMaxSize];
678             [self setContentMinSize:savedContentMinSize];
679         }
681         if (allow_immovable_windows || cursor_clipping_locks_windows)
682         {
683             if (allow_immovable_windows && (disabled || maximized))
684                 [self setMovable:NO];
685             else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
686                 [self setMovable:NO];
687             else
688                 [self setMovable:YES];
689         }
690     }
692     - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
693     {
694         if ([self respondsToSelector:@selector(toggleFullScreen:)])
695         {
696             NSUInteger style = [self styleMask];
698             if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
699                 style & NSResizableWindowMask && !(style & NSUtilityWindowMask))
700             {
701                 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
702                 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
703             }
704             else
705             {
706                 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
707                 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
708                 if (style & NSFullScreenWindowMask)
709                     [self toggleFullScreen:nil];
710             }
711         }
713         if (behavior != [self collectionBehavior])
714         {
715             [self setCollectionBehavior:behavior];
716             [self adjustFeaturesForState];
717         }
718     }
720     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
721     {
722         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
723                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
724         NSUInteger currentStyle = [self styleMask];
725         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
727         if (newStyle != currentStyle)
728         {
729             NSString* title = [[[self title] copy] autorelease];
730             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
731             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
732             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
733             {
734                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
735                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
736                 // just NSTitledWindowMask, the window buttons should disappear rather
737                 // than just being disabled.  But they don't.  Similarly in reverse.
738                 // The workaround is to also toggle NSClosableWindowMask at the same time.
739                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
740             }
741             [self setStyleMask:newStyle];
743             // -setStyleMask: resets the firstResponder to the window.  Set it
744             // back to the content view.
745             if ([[self contentView] acceptsFirstResponder])
746                 [self makeFirstResponder:[self contentView]];
748             [self adjustFullScreenBehavior:[self collectionBehavior]];
750             if ([[self title] length] == 0 && [title length] > 0)
751                 [self setTitle:title];
752         }
754         resizable = wf->resizable;
755         [self adjustFeaturesForState];
756         [self setHasShadow:wf->shadow];
757     }
759     // Indicates if the window would be visible if the app were not hidden.
760     - (BOOL) wouldBeVisible
761     {
762         return [NSApp isHidden] ? savedVisibleState : [self isVisible];
763     }
765     - (BOOL) isOrderedIn
766     {
767         return [self wouldBeVisible] || [self isMiniaturized];
768     }
770     - (NSInteger) minimumLevelForActive:(BOOL)active
771     {
772         NSInteger level;
774         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
775                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
776             level = NSFloatingWindowLevel;
777         else
778             level = NSNormalWindowLevel;
780         if (active)
781         {
782             BOOL captured;
784             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
786             if (captured || fullscreen)
787             {
788                 if (captured)
789                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
790                 else
791                     level = NSMainMenuWindowLevel + 1;
793                 if (self.floating)
794                     level++;
795             }
796         }
798         return level;
799     }
801     - (void) postDidUnminimizeEvent
802     {
803         macdrv_event* event;
805         /* Coalesce events by discarding any previous ones still in the queue. */
806         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
807                                forWindow:self];
809         event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
810         [queue postEvent:event];
811         macdrv_release_event(event);
812     }
814     - (void) setMacDrvState:(const struct macdrv_window_state*)state
815     {
816         NSWindowCollectionBehavior behavior;
818         self.disabled = state->disabled;
819         self.noActivate = state->no_activate;
821         if (self.floating != state->floating)
822         {
823             self.floating = state->floating;
824             if (state->floating)
825             {
826                 // Became floating.  If child of non-floating window, make that
827                 // relationship latent.
828                 WineWindow* parent = (WineWindow*)[self parentWindow];
829                 if (parent && !parent.floating)
830                     [self becameIneligibleChild];
831             }
832             else
833             {
834                 // Became non-floating.  If parent of floating children, make that
835                 // relationship latent.
836                 WineWindow* child;
837                 for (child in [[[self childWindows] copy] autorelease])
838                 {
839                     if (child.floating)
840                         [child becameIneligibleChild];
841                 }
842             }
844             // Check our latent relationships.  If floating status was the only
845             // reason they were latent, then make them active.
846             if ([self isVisible])
847                 [self becameEligibleParentOrChild];
849             [[WineApplicationController sharedController] adjustWindowLevels];
850         }
852         behavior = NSWindowCollectionBehaviorDefault;
853         if (state->excluded_by_expose)
854             behavior |= NSWindowCollectionBehaviorTransient;
855         else
856             behavior |= NSWindowCollectionBehaviorManaged;
857         if (state->excluded_by_cycle)
858         {
859             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
860             if ([self isOrderedIn])
861                 [NSApp removeWindowsItem:self];
862         }
863         else
864         {
865             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
866             if ([self isOrderedIn])
867                 [NSApp addWindowsItem:self title:[self title] filename:NO];
868         }
869         [self adjustFullScreenBehavior:behavior];
871         if (state->minimized_valid)
872         {
873             macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
875             pendingMinimize = FALSE;
876             if (state->minimized && ![self isMiniaturized])
877             {
878                 if ([self wouldBeVisible])
879                 {
880                     if ([self styleMask] & NSFullScreenWindowMask)
881                     {
882                         [self postDidUnminimizeEvent];
883                         discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
884                     }
885                     else
886                     {
887                         [super miniaturize:nil];
888                         discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
889                                    event_mask_for_type(WINDOW_GOT_FOCUS) |
890                                    event_mask_for_type(WINDOW_LOST_FOCUS);
891                     }
892                 }
893                 else
894                     pendingMinimize = TRUE;
895             }
896             else if (!state->minimized && [self isMiniaturized])
897             {
898                 ignore_windowDeminiaturize = TRUE;
899                 [self deminiaturize:nil];
900                 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
901             }
903             if (discard)
904                 [queue discardEventsMatchingMask:discard forWindow:self];
905         }
907         if (state->maximized != maximized)
908         {
909             maximized = state->maximized;
910             [self adjustFeaturesForState];
911         }
912     }
914     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
915     {
916         BOOL reordered = FALSE;
918         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
919         {
920             if ([self level] > [child level])
921                 [child setLevel:[self level]];
922             [self addChildWindow:child ordered:NSWindowAbove];
923             [latentChildWindows removeObjectIdenticalTo:child];
924             child.latentParentWindow = nil;
925             reordered = TRUE;
926         }
927         else
928         {
929             if (!latentChildWindows)
930                 latentChildWindows = [[NSMutableArray alloc] init];
931             if (![latentChildWindows containsObject:child])
932                 [latentChildWindows addObject:child];
933             child.latentParentWindow = self;
934         }
936         return reordered;
937     }
939     - (BOOL) addChildWineWindow:(WineWindow*)child
940     {
941         return [self addChildWineWindow:child assumeVisible:FALSE];
942     }
944     - (void) removeChildWineWindow:(WineWindow*)child
945     {
946         [self removeChildWindow:child];
947         if (child.latentParentWindow == self)
948             child.latentParentWindow = nil;
949         [latentChildWindows removeObjectIdenticalTo:child];
950     }
952     - (BOOL) becameEligibleParentOrChild
953     {
954         BOOL reordered = FALSE;
955         NSUInteger count;
957         if (latentParentWindow.floating || !self.floating)
958         {
959             // If we aren't visible currently, we assume that we should be and soon
960             // will be.  So, if the latent parent is visible that's enough to assume
961             // we can establish the parent-child relationship in Cocoa.  That will
962             // actually make us visible, which is fine.
963             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
964                 reordered = TRUE;
965         }
967         // Here, though, we may not actually be visible yet and adding a child
968         // won't make us visible.  The caller will have to call this method
969         // again after actually making us visible.
970         if ([self isVisible] && (count = [latentChildWindows count]))
971         {
972             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
973             NSUInteger i;
975             for (i = 0; i < count; i++)
976             {
977                 WineWindow* child = [latentChildWindows objectAtIndex:i];
978                 if ([child isVisible] && (self.floating || !child.floating))
979                 {
980                     if (child.latentParentWindow == self)
981                     {
982                         if ([self level] > [child level])
983                             [child setLevel:[self level]];
984                         [self addChildWindow:child ordered:NSWindowAbove];
985                         child.latentParentWindow = nil;
986                         reordered = TRUE;
987                     }
988                     else
989                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
990                     [indexesToRemove addIndex:i];
991                 }
992             }
994             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
995         }
997         return reordered;
998     }
1000     - (void) becameIneligibleChild
1001     {
1002         WineWindow* parent = (WineWindow*)[self parentWindow];
1003         if (parent)
1004         {
1005             if (!parent->latentChildWindows)
1006                 parent->latentChildWindows = [[NSMutableArray alloc] init];
1007             [parent->latentChildWindows insertObject:self atIndex:0];
1008             self.latentParentWindow = parent;
1009             [parent removeChildWindow:self];
1010         }
1011     }
1013     - (void) becameIneligibleParentOrChild
1014     {
1015         NSArray* childWindows = [self childWindows];
1017         [self becameIneligibleChild];
1019         if ([childWindows count])
1020         {
1021             WineWindow* child;
1023             childWindows = [[childWindows copy] autorelease];
1024             for (child in childWindows)
1025             {
1026                 child.latentParentWindow = self;
1027                 [self removeChildWindow:child];
1028             }
1030             if (latentChildWindows)
1031                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1032             else
1033                 latentChildWindows = [childWindows mutableCopy];
1034         }
1035     }
1037     // Determine if, among Wine windows, this window is directly above or below
1038     // a given other Wine window with no other Wine window intervening.
1039     // Intervening non-Wine windows are ignored.
1040     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1041     {
1042         NSNumber* windowNumber;
1043         NSNumber* otherWindowNumber;
1044         NSArray* windowNumbers;
1045         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1047         if (![self isVisible] || ![otherWindow isVisible])
1048             return FALSE;
1050         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1051         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1052         windowNumbers = [[self class] windowNumbersWithOptions:0];
1053         windowIndex = [windowNumbers indexOfObject:windowNumber];
1054         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1056         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1057             return FALSE;
1059         if (orderingMode == NSWindowAbove)
1060         {
1061             lowIndex = windowIndex;
1062             highIndex = otherWindowIndex;
1063         }
1064         else if (orderingMode == NSWindowBelow)
1065         {
1066             lowIndex = otherWindowIndex;
1067             highIndex = windowIndex;
1068         }
1069         else
1070             return FALSE;
1072         if (highIndex <= lowIndex)
1073             return FALSE;
1075         for (i = lowIndex + 1; i < highIndex; i++)
1076         {
1077             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1078             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1079             if ([interveningWindow isKindOfClass:[WineWindow class]])
1080                 return FALSE;
1081         }
1083         return TRUE;
1084     }
1086     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1087     {
1088         NSMutableArray* windowNumbers;
1089         NSNumber* childWindowNumber;
1090         NSUInteger otherIndex, limit;
1091         NSArray* origChildren;
1092         NSMutableArray* children;
1094         // Get the z-order from the window server and modify it to reflect the
1095         // requested window ordering.
1096         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1097         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1098         [windowNumbers removeObject:childWindowNumber];
1099         otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1100         [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1102         // Get our child windows and sort them in the reverse of the desired
1103         // z-order (back-to-front).
1104         origChildren = [self childWindows];
1105         children = [[origChildren mutableCopy] autorelease];
1106         [children sortWithOptions:NSSortStable
1107                   usingComparator:^NSComparisonResult(id obj1, id obj2){
1108             NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1109             NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1110             NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1111             NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1112             if (index1 == NSNotFound)
1113             {
1114                 if (index2 == NSNotFound)
1115                     return NSOrderedSame;
1116                 else
1117                     return NSOrderedAscending;
1118             }
1119             else if (index2 == NSNotFound)
1120                 return NSOrderedDescending;
1121             else if (index1 < index2)
1122                 return NSOrderedDescending;
1123             else if (index2 < index1)
1124                 return NSOrderedAscending;
1126             return NSOrderedSame;
1127         }];
1129         // If the current and desired children arrays match up to a point, leave
1130         // those matching children alone.
1131         limit = MIN([origChildren count], [children count]);
1132         for (otherIndex = 0; otherIndex < limit; otherIndex++)
1133         {
1134             if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1135                 break;
1136         }
1137         [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1139         // Remove all of the child windows and re-add them back-to-front so they
1140         // are in the desired order.
1141         for (other in children)
1142             [self removeChildWindow:other];
1143         for (other in children)
1144             [self addChildWindow:other ordered:NSWindowAbove];
1145     }
1147     /* Returns whether or not the window was ordered in, which depends on if
1148        its frame intersects any screen. */
1149     - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1150     {
1151         WineApplicationController* controller = [WineApplicationController sharedController];
1152         if (![self isMiniaturized])
1153         {
1154             BOOL needAdjustWindowLevels = FALSE;
1155             BOOL wasVisible;
1157             [controller transformProcessToForeground];
1158             [NSApp unhide:nil];
1159             wasVisible = [self isVisible];
1161             if (activate)
1162                 [NSApp activateIgnoringOtherApps:YES];
1164             NSDisableScreenUpdates();
1166             if ([self becameEligibleParentOrChild])
1167                 needAdjustWindowLevels = TRUE;
1169             if (prev || next)
1170             {
1171                 WineWindow* other = [prev isVisible] ? prev : next;
1172                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1174                 if (![self isOrdered:orderingMode relativeTo:other])
1175                 {
1176                     WineWindow* parent = (WineWindow*)[self parentWindow];
1177                     WineWindow* otherParent = (WineWindow*)[other parentWindow];
1179                     // This window level may not be right for this window based
1180                     // on floating-ness, fullscreen-ness, etc.  But we set it
1181                     // temporarily to allow us to order the windows properly.
1182                     // Then the levels get fixed by -adjustWindowLevels.
1183                     if ([self level] != [other level])
1184                         [self setLevel:[other level]];
1185                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1187                     // The above call to -[NSWindow orderWindow:relativeTo:] won't
1188                     // reorder windows which are both children of the same parent
1189                     // relative to each other, so do that separately.
1190                     if (parent && parent == otherParent)
1191                         [parent order:orderingMode childWindow:self relativeTo:other];
1193                     needAdjustWindowLevels = TRUE;
1194                 }
1195             }
1196             else
1197             {
1198                 // Again, temporarily set level to make sure we can order to
1199                 // the right place.
1200                 next = [controller frontWineWindow];
1201                 if (next && [self level] < [next level])
1202                     [self setLevel:[next level]];
1203                 [self orderFront:nil];
1204                 needAdjustWindowLevels = TRUE;
1205             }
1207             if ([self becameEligibleParentOrChild])
1208                 needAdjustWindowLevels = TRUE;
1210             if (needAdjustWindowLevels)
1211             {
1212                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1213                     [controller updateFullscreenWindows];
1214                 [controller adjustWindowLevels];
1215             }
1217             if (pendingMinimize)
1218             {
1219                 [super miniaturize:nil];
1220                 pendingMinimize = FALSE;
1221             }
1223             NSEnableScreenUpdates();
1225             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1226                Generate a frame-changed event just in case.  The back end will ignore
1227                it if nothing actually changed. */
1228             [self windowDidResize:nil];
1230             if (![self isExcludedFromWindowsMenu])
1231                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1232         }
1233     }
1235     - (void) doOrderOut
1236     {
1237         WineApplicationController* controller = [WineApplicationController sharedController];
1238         BOOL wasVisible = [self isVisible];
1239         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1241         if ([self isMiniaturized])
1242             pendingMinimize = TRUE;
1244         [self becameIneligibleParentOrChild];
1245         if ([self isMiniaturized])
1246         {
1247             fakingClose = TRUE;
1248             [self close];
1249             fakingClose = FALSE;
1250         }
1251         else
1252             [self orderOut:nil];
1253         savedVisibleState = FALSE;
1254         if (wasVisible && wasOnActiveSpace && fullscreen)
1255             [controller updateFullscreenWindows];
1256         [controller adjustWindowLevels];
1257         [NSApp removeWindowsItem:self];
1259         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1260                                          event_mask_for_type(WINDOW_GOT_FOCUS) |
1261                                          event_mask_for_type(WINDOW_LOST_FOCUS) |
1262                                          event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1263                                          event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1264                                          event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1265                                forWindow:self];
1266     }
1268     - (void) updateFullscreen
1269     {
1270         NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1271         BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1273         if (nowFullscreen != fullscreen)
1274         {
1275             WineApplicationController* controller = [WineApplicationController sharedController];
1277             fullscreen = nowFullscreen;
1278             if ([self isVisible] && [self isOnActiveSpace])
1279                 [controller updateFullscreenWindows];
1281             [controller adjustWindowLevels];
1282         }
1283     }
1285     - (void) setFrameFromWine:(NSRect)contentRect
1286     {
1287         /* Origin is (left, top) in a top-down space.  Need to convert it to
1288            (left, bottom) in a bottom-up space. */
1289         [[WineApplicationController sharedController] flipRect:&contentRect];
1291         /* The back end is establishing a new window size and position.  It's
1292            not interested in any stale events regarding those that may be sitting
1293            in the queue. */
1294         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1295                                forWindow:self];
1297         if (!NSIsEmptyRect(contentRect))
1298         {
1299             NSRect frame, oldFrame;
1301             oldFrame = [self frame];
1302             frame = [self frameRectForContentRect:contentRect];
1303             if (!NSEqualRects(frame, oldFrame))
1304             {
1305                 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1306                 BOOL needEnableScreenUpdates = FALSE;
1308                 if ([self preventResizing])
1309                 {
1310                     // Allow the following calls to -setFrame:display: to work even
1311                     // if they would violate the content size constraints. This
1312                     // shouldn't be necessary since the content size constraints are
1313                     // documented to not constrain that method, but it seems to be.
1314                     [self setContentMinSize:NSZeroSize];
1315                     [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1316                 }
1318                 if (equalSizes && [[self childWindows] count])
1319                 {
1320                     // If we change the window frame such that the origin moves
1321                     // but the size doesn't change, then Cocoa moves child
1322                     // windows with the parent.  We don't want that so we fake
1323                     // a change of the size and then change it back.
1324                     NSRect bogusFrame = frame;
1325                     bogusFrame.size.width++;
1327                     NSDisableScreenUpdates();
1328                     needEnableScreenUpdates = TRUE;
1330                     ignore_windowResize = TRUE;
1331                     [self setFrame:bogusFrame display:NO];
1332                     ignore_windowResize = FALSE;
1333                 }
1335                 [self setFrame:frame display:YES];
1336                 if ([self preventResizing])
1337                 {
1338                     [self setContentMinSize:contentRect.size];
1339                     [self setContentMaxSize:contentRect.size];
1340                 }
1342                 if (needEnableScreenUpdates)
1343                     NSEnableScreenUpdates();
1345                 if (!equalSizes)
1346                     [self updateColorSpace];
1348                 if (!enteringFullScreen &&
1349                     [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1350                     nonFullscreenFrame = frame;
1352                 [self updateFullscreen];
1354                 if ([self isOrderedIn])
1355                 {
1356                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1357                        event.  The back end will ignore it if nothing actually changed. */
1358                     [self windowDidResize:nil];
1359                 }
1360             }
1361         }
1362     }
1364     - (void) setMacDrvParentWindow:(WineWindow*)parent
1365     {
1366         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1367         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1368         {
1369             [oldParent removeChildWineWindow:self];
1370             [latentParentWindow removeChildWineWindow:self];
1371             if ([parent addChildWineWindow:self])
1372                 [[WineApplicationController sharedController] adjustWindowLevels];
1373         }
1374     }
1376     - (void) setDisabled:(BOOL)newValue
1377     {
1378         if (disabled != newValue)
1379         {
1380             disabled = newValue;
1381             [self adjustFeaturesForState];
1382         }
1383     }
1385     - (BOOL) needsTransparency
1386     {
1387         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1388     }
1390     - (void) checkTransparency
1391     {
1392         if (![self isOpaque] && !self.needsTransparency)
1393         {
1394             self.shapeChangedSinceLastDraw = TRUE;
1395             [[self contentView] setNeedsDisplay:YES];
1396             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1397             [self setOpaque:YES];
1398         }
1399         else if ([self isOpaque] && self.needsTransparency)
1400         {
1401             self.shapeChangedSinceLastDraw = TRUE;
1402             [[self contentView] setNeedsDisplay:YES];
1403             [self setBackgroundColor:[NSColor clearColor]];
1404             [self setOpaque:NO];
1405         }
1406     }
1408     - (void) setShape:(NSBezierPath*)newShape
1409     {
1410         if (shape == newShape) return;
1412         if (shape)
1413         {
1414             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1415             [shape release];
1416         }
1417         if (newShape)
1418             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1420         shape = [newShape copy];
1421         self.shapeChangedSinceLastDraw = TRUE;
1423         [self checkTransparency];
1424     }
1426     - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1427     {
1428         if (newTimer != liveResizeDisplayTimer)
1429         {
1430             [liveResizeDisplayTimer invalidate];
1431             [liveResizeDisplayTimer release];
1432             liveResizeDisplayTimer = [newTimer retain];
1433         }
1434     }
1436     - (void) makeFocused:(BOOL)activate
1437     {
1438         if (activate)
1439         {
1440             [[WineApplicationController sharedController] transformProcessToForeground];
1441             [NSApp activateIgnoringOtherApps:YES];
1442         }
1444         causing_becomeKeyWindow = self;
1445         [self makeKeyWindow];
1446         causing_becomeKeyWindow = nil;
1448         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1449                                          event_mask_for_type(WINDOW_LOST_FOCUS)
1450                                forWindow:self];
1451     }
1453     - (void) postKey:(uint16_t)keyCode
1454              pressed:(BOOL)pressed
1455            modifiers:(NSUInteger)modifiers
1456                event:(NSEvent*)theEvent
1457     {
1458         macdrv_event* event;
1459         CGEventRef cgevent;
1460         WineApplicationController* controller = [WineApplicationController sharedController];
1462         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1463         event->key.keycode   = keyCode;
1464         event->key.modifiers = modifiers;
1465         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1467         if ((cgevent = [theEvent CGEvent]))
1468         {
1469             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1470                                                         kCGKeyboardEventKeyboardType);
1471             if (keyboardType != controller.keyboardType)
1472             {
1473                 controller.keyboardType = keyboardType;
1474                 [controller keyboardSelectionDidChange];
1475             }
1476         }
1478         [queue postEvent:event];
1480         macdrv_release_event(event);
1482         [controller noteKey:keyCode pressed:pressed];
1483     }
1485     - (void) postKeyEvent:(NSEvent *)theEvent
1486     {
1487         [self flagsChanged:theEvent];
1488         [self postKey:[theEvent keyCode]
1489               pressed:[theEvent type] == NSKeyDown
1490             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1491                 event:theEvent];
1492     }
1494     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1495     {
1496         savedContentMinSize = minSize;
1497         savedContentMaxSize = maxSize;
1498         if (![self preventResizing])
1499         {
1500             [self setContentMinSize:minSize];
1501             [self setContentMaxSize:maxSize];
1502         }
1503     }
1505     - (WineWindow*) ancestorWineWindow
1506     {
1507         WineWindow* ancestor = self;
1508         for (;;)
1509         {
1510             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1511             if ([parent isKindOfClass:[WineWindow class]])
1512                 ancestor = parent;
1513             else
1514                 break;
1515         }
1516         return ancestor;
1517     }
1519     - (void) postBroughtForwardEvent
1520     {
1521         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1522         [queue postEvent:event];
1523         macdrv_release_event(event);
1524     }
1526     - (void) updateForCursorClipping
1527     {
1528         [self adjustFeaturesForState];
1529     }
1532     /*
1533      * ---------- NSWindow method overrides ----------
1534      */
1535     - (BOOL) canBecomeKeyWindow
1536     {
1537         if (causing_becomeKeyWindow == self) return YES;
1538         if (self.disabled || self.noActivate) return NO;
1539         return [self isKeyWindow];
1540     }
1542     - (BOOL) canBecomeMainWindow
1543     {
1544         return [self canBecomeKeyWindow];
1545     }
1547     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1548     {
1549         // If a window is sized to completely cover a screen, then it's in
1550         // full-screen mode.  In that case, we don't allow NSWindow to constrain
1551         // it.
1552         NSArray* screens = [NSScreen screens];
1553         NSRect contentRect = [self contentRectForFrameRect:frameRect];
1554         if (!screen_covered_by_rect(contentRect, screens) &&
1555             frame_intersects_screens(frameRect, screens))
1556             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1557         return frameRect;
1558     }
1560     - (BOOL) isExcludedFromWindowsMenu
1561     {
1562         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1563     }
1565     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1566     {
1567         BOOL ret = [super validateMenuItem:menuItem];
1569         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1570             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1571         if ([menuItem action] == @selector(toggleFullScreen:) && self.disabled)
1572             ret = NO;
1574         return ret;
1575     }
1577     /* We don't call this.  It's the action method of the items in the Window menu. */
1578     - (void) makeKeyAndOrderFront:(id)sender
1579     {
1580         if ([self isMiniaturized])
1581             [self deminiaturize:nil];
1582         [self orderBelow:nil orAbove:nil activate:NO];
1583         [[self ancestorWineWindow] postBroughtForwardEvent];
1585         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1586             [[WineApplicationController sharedController] windowGotFocus:self];
1587     }
1589     - (void) sendEvent:(NSEvent*)event
1590     {
1591         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1592            interface control.  For example, Control-Tab switches focus among
1593            views.  We want to bypass that feature, so directly route key-down
1594            events to -keyDown:. */
1595         if ([event type] == NSKeyDown)
1596             [[self firstResponder] keyDown:event];
1597         else
1598             [super sendEvent:event];
1599     }
1601     - (void) miniaturize:(id)sender
1602     {
1603         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1604         [queue postEvent:event];
1605         macdrv_release_event(event);
1606     }
1608     - (void) toggleFullScreen:(id)sender
1609     {
1610         if (!self.disabled)
1611             [super toggleFullScreen:sender];
1612     }
1614     // We normally use the generic/calibrated RGB color space for the window,
1615     // rather than the device color space, to avoid expensive color conversion
1616     // which slows down drawing.  However, for windows displaying OpenGL, having
1617     // a different color space than the screen greatly reduces frame rates, often
1618     // limiting it to the display refresh rate.
1619     //
1620     // To avoid this, we switch back to the screen color space whenever the
1621     // window is covered by a view with an attached OpenGL context.
1622     - (void) updateColorSpace
1623     {
1624         NSRect contentRect = [[self contentView] frame];
1625         BOOL coveredByGLView = FALSE;
1626         for (WineContentView* view in [[self contentView] subviews])
1627         {
1628             if ([view hasGLContext])
1629             {
1630                 NSRect frame = [view convertRect:[view bounds] toView:nil];
1631                 if (NSContainsRect(frame, contentRect))
1632                 {
1633                     coveredByGLView = TRUE;
1634                     break;
1635                 }
1636             }
1637         }
1639         if (coveredByGLView)
1640             [self setColorSpace:nil];
1641         else
1642             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1643     }
1646     /*
1647      * ---------- NSResponder method overrides ----------
1648      */
1649     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1651     - (void) flagsChanged:(NSEvent *)theEvent
1652     {
1653         static const struct {
1654             NSUInteger  mask;
1655             uint16_t    keycode;
1656         } modifiers[] = {
1657             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
1658             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
1659             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
1660             { NX_DEVICELCTLKEYMASK,     kVK_Control },
1661             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
1662             { NX_DEVICELALTKEYMASK,     kVK_Option },
1663             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
1664             { NX_DEVICELCMDKEYMASK,     kVK_Command },
1665             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
1666         };
1668         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1669         NSUInteger changed;
1670         int i, last_changed;
1672         fix_device_modifiers_by_generic(&modifierFlags);
1673         changed = modifierFlags ^ lastModifierFlags;
1675         last_changed = -1;
1676         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1677             if (changed & modifiers[i].mask)
1678                 last_changed = i;
1680         for (i = 0; i <= last_changed; i++)
1681         {
1682             if (changed & modifiers[i].mask)
1683             {
1684                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1686                 if (i == last_changed)
1687                     lastModifierFlags = modifierFlags;
1688                 else
1689                 {
1690                     lastModifierFlags ^= modifiers[i].mask;
1691                     fix_generic_modifiers_by_device(&lastModifierFlags);
1692                 }
1694                 // Caps lock generates one event for each press-release action.
1695                 // We need to simulate a pair of events for each actual event.
1696                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1697                 {
1698                     [self postKey:modifiers[i].keycode
1699                           pressed:TRUE
1700                         modifiers:lastModifierFlags
1701                             event:(NSEvent*)theEvent];
1702                     pressed = FALSE;
1703                 }
1705                 [self postKey:modifiers[i].keycode
1706                       pressed:pressed
1707                     modifiers:lastModifierFlags
1708                         event:(NSEvent*)theEvent];
1709             }
1710         }
1711     }
1713     - (void) applicationWillHide
1714     {
1715         savedVisibleState = [self isVisible];
1716     }
1718     - (void) applicationDidUnhide
1719     {
1720         if ([self isVisible])
1721             [self becameEligibleParentOrChild];
1722     }
1725     /*
1726      * ---------- NSWindowDelegate methods ----------
1727      */
1728     - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1729     {
1730         macdrv_query* query;
1731         NSSize size;
1733         query = macdrv_create_query();
1734         query->type = QUERY_MIN_MAX_INFO;
1735         query->window = (macdrv_window)[self retain];
1736         [self.queue query:query timeout:0.5];
1737         macdrv_release_query(query);
1739         size = [self contentMaxSize];
1740         if (proposedSize.width < size.width)
1741             size.width = proposedSize.width;
1742         if (proposedSize.height < size.height)
1743             size.height = proposedSize.height;
1744         return size;
1745     }
1747     - (void)windowDidBecomeKey:(NSNotification *)notification
1748     {
1749         WineApplicationController* controller = [WineApplicationController sharedController];
1750         NSEvent* event = [controller lastFlagsChanged];
1751         if (event)
1752             [self flagsChanged:event];
1754         if (causing_becomeKeyWindow == self) return;
1756         [controller windowGotFocus:self];
1757     }
1759     - (void)windowDidDeminiaturize:(NSNotification *)notification
1760     {
1761         WineApplicationController* controller = [WineApplicationController sharedController];
1763         if (!ignore_windowDeminiaturize)
1764             [self postDidUnminimizeEvent];
1765         ignore_windowDeminiaturize = FALSE;
1767         [self becameEligibleParentOrChild];
1769         if (fullscreen && [self isOnActiveSpace])
1770             [controller updateFullscreenWindows];
1771         [controller adjustWindowLevels];
1773         if (![self parentWindow])
1774             [self postBroughtForwardEvent];
1776         if (!self.disabled && !self.noActivate)
1777         {
1778             causing_becomeKeyWindow = self;
1779             [self makeKeyWindow];
1780             causing_becomeKeyWindow = nil;
1781             [controller windowGotFocus:self];
1782         }
1784         [self windowDidResize:notification];
1785     }
1787     - (void) windowDidEndLiveResize:(NSNotification *)notification
1788     {
1789         macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1790         [queue postEvent:event];
1791         macdrv_release_event(event);
1793         self.liveResizeDisplayTimer = nil;
1794     }
1796     - (void) windowDidEnterFullScreen:(NSNotification*)notification
1797     {
1798         enteringFullScreen = FALSE;
1799         enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1800     }
1802     - (void) windowDidExitFullScreen:(NSNotification*)notification
1803     {
1804         exitingFullScreen = FALSE;
1805         [self setFrame:nonFullscreenFrame display:YES animate:NO];
1806         [self windowDidResize:nil];
1807     }
1809     - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1810     {
1811         enteringFullScreen = FALSE;
1812         enteredFullScreenTime = 0;
1813     }
1815     - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1816     {
1817         exitingFullScreen = FALSE;
1818         [self windowDidResize:nil];
1819     }
1821     - (void)windowDidMiniaturize:(NSNotification *)notification
1822     {
1823         if (fullscreen && [self isOnActiveSpace])
1824             [[WineApplicationController sharedController] updateFullscreenWindows];
1825     }
1827     - (void)windowDidMove:(NSNotification *)notification
1828     {
1829         [self windowDidResize:notification];
1830     }
1832     - (void)windowDidResignKey:(NSNotification *)notification
1833     {
1834         macdrv_event* event;
1836         if (causing_becomeKeyWindow) return;
1838         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1839         [queue postEvent:event];
1840         macdrv_release_event(event);
1841     }
1843     - (void)windowDidResize:(NSNotification *)notification
1844     {
1845         macdrv_event* event;
1846         NSRect frame = [self frame];
1848         if ([self inLiveResize])
1849         {
1850             if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1851                 resizingFromLeft = TRUE;
1852             if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1853                 resizingFromTop = TRUE;
1854         }
1856         frame = [self contentRectForFrameRect:frame];
1858         if (ignore_windowResize || exitingFullScreen) return;
1860         if ([self preventResizing])
1861         {
1862             [self setContentMinSize:frame.size];
1863             [self setContentMaxSize:frame.size];
1864         }
1866         [[WineApplicationController sharedController] flipRect:&frame];
1868         /* Coalesce events by discarding any previous ones still in the queue. */
1869         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1870                                forWindow:self];
1872         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1873         event->window_frame_changed.frame = NSRectToCGRect(frame);
1874         event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
1875         event->window_frame_changed.in_resize = [self inLiveResize];
1876         [queue postEvent:event];
1877         macdrv_release_event(event);
1879         [[[self contentView] inputContext] invalidateCharacterCoordinates];
1880         [self updateFullscreen];
1881     }
1883     - (BOOL)windowShouldClose:(id)sender
1884     {
1885         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1886         [queue postEvent:event];
1887         macdrv_release_event(event);
1888         return NO;
1889     }
1891     - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
1892     {
1893         if (maximized)
1894         {
1895             macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1896             [queue postEvent:event];
1897             macdrv_release_event(event);
1898             return NO;
1899         }
1900         else if (!resizable)
1901         {
1902             macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
1903             [queue postEvent:event];
1904             macdrv_release_event(event);
1905             return NO;
1906         }
1908         return YES;
1909     }
1911     - (void) windowWillClose:(NSNotification*)notification
1912     {
1913         WineWindow* child;
1915         if (fakingClose) return;
1916         if (latentParentWindow)
1917         {
1918             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
1919             self.latentParentWindow = nil;
1920         }
1922         for (child in latentChildWindows)
1923         {
1924             if (child.latentParentWindow == self)
1925                 child.latentParentWindow = nil;
1926         }
1927         [latentChildWindows removeAllObjects];
1928     }
1930     - (void) windowWillEnterFullScreen:(NSNotification*)notification
1931     {
1932         enteringFullScreen = TRUE;
1933         nonFullscreenFrame = [self frame];
1934     }
1936     - (void) windowWillExitFullScreen:(NSNotification*)notification
1937     {
1938         exitingFullScreen = TRUE;
1939     }
1941     - (void)windowWillMiniaturize:(NSNotification *)notification
1942     {
1943         [self becameIneligibleParentOrChild];
1944     }
1946     - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
1947     {
1948         if ([self inLiveResize])
1949         {
1950             NSRect rect;
1951             macdrv_query* query;
1953             rect = [self frame];
1954             if (resizingFromLeft)
1955                 rect.origin.x = NSMaxX(rect) - frameSize.width;
1956             if (!resizingFromTop)
1957                 rect.origin.y = NSMaxY(rect) - frameSize.height;
1958             rect.size = frameSize;
1959             rect = [self contentRectForFrameRect:rect];
1960             [[WineApplicationController sharedController] flipRect:&rect];
1962             query = macdrv_create_query();
1963             query->type = QUERY_RESIZE_SIZE;
1964             query->window = (macdrv_window)[self retain];
1965             query->resize_size.rect = NSRectToCGRect(rect);
1966             query->resize_size.from_left = resizingFromLeft;
1967             query->resize_size.from_top = resizingFromTop;
1969             if ([self.queue query:query timeout:0.1])
1970             {
1971                 rect = NSRectFromCGRect(query->resize_size.rect);
1972                 rect = [self frameRectForContentRect:rect];
1973                 frameSize = rect.size;
1974             }
1976             macdrv_release_query(query);
1977         }
1979         return frameSize;
1980     }
1982     - (void) windowWillStartLiveResize:(NSNotification *)notification
1983     {
1984         macdrv_query* query = macdrv_create_query();
1985         query->type = QUERY_RESIZE_START;
1986         query->window = (macdrv_window)[self retain];
1988         [self.queue query:query timeout:0.3];
1989         macdrv_release_query(query);
1991         frameAtResizeStart = [self frame];
1992         resizingFromLeft = resizingFromTop = FALSE;
1994         // There's a strange restriction in window redrawing during Cocoa-
1995         // managed window resizing.  Only calls to -[NSView setNeedsDisplay...]
1996         // that happen synchronously when Cocoa tells us that our window size
1997         // has changed or asynchronously in a short interval thereafter provoke
1998         // the window to redraw.  Calls to those methods that happen asynchronously
1999         // a half second or more after the last change of the window size aren't
2000         // heeded until the next resize-related user event (e.g. mouse movement).
2001         //
2002         // Wine often has a significant delay between when it's been told that
2003         // the window has changed size and when it can flush completed drawing.
2004         // So, our windows would get stuck with incomplete drawing for as long
2005         // as the user holds the mouse button down and doesn't move it.
2006         //
2007         // We address this by "manually" asking our windows to check if they need
2008         // redrawing every so often (during live resize only).
2009         self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
2010                                                                        target:self
2011                                                                      selector:@selector(displayIfNeeded)
2012                                                                      userInfo:nil
2013                                                                       repeats:YES];
2014         [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
2015                                      forMode:NSRunLoopCommonModes];
2016     }
2018     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2019     {
2020         macdrv_query* query;
2021         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2022         NSSize maxSize;
2024         query = macdrv_create_query();
2025         query->type = QUERY_MIN_MAX_INFO;
2026         query->window = (macdrv_window)[self retain];
2027         [self.queue query:query timeout:0.5];
2028         macdrv_release_query(query);
2030         currentContentRect = [self contentRectForFrameRect:[self frame]];
2031         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2033         maxSize = [self contentMaxSize];
2034         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2035         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2037         // Try to keep the top-left corner where it is.
2038         newContentRect.origin.x = NSMinX(currentContentRect);
2039         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2041         // If that pushes the bottom or right off the screen, pull it up and to the left.
2042         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2043         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2044             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2045         if (NSMinY(newContentRect) < NSMinY(screenRect))
2046             newContentRect.origin.y = NSMinY(screenRect);
2048         // If that pushes the top or left off the screen, push it down and the right
2049         // again.  Do this last because the top-left corner is more important than the
2050         // bottom-right.
2051         if (NSMinX(newContentRect) < NSMinX(screenRect))
2052             newContentRect.origin.x = NSMinX(screenRect);
2053         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2054             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2056         return [self frameRectForContentRect:newContentRect];
2057     }
2060     /*
2061      * ---------- NSPasteboardOwner methods ----------
2062      */
2063     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2064     {
2065         macdrv_query* query = macdrv_create_query();
2066         query->type = QUERY_PASTEBOARD_DATA;
2067         query->window = (macdrv_window)[self retain];
2068         query->pasteboard_data.type = (CFStringRef)[type copy];
2070         [self.queue query:query timeout:3];
2071         macdrv_release_query(query);
2072     }
2075     /*
2076      * ---------- NSDraggingDestination methods ----------
2077      */
2078     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2079     {
2080         return [self draggingUpdated:sender];
2081     }
2083     - (void) draggingExited:(id <NSDraggingInfo>)sender
2084     {
2085         // This isn't really a query.  We don't need any response.  However, it
2086         // has to be processed in a similar manner as the other drag-and-drop
2087         // queries in order to maintain the proper order of operations.
2088         macdrv_query* query = macdrv_create_query();
2089         query->type = QUERY_DRAG_EXITED;
2090         query->window = (macdrv_window)[self retain];
2092         [self.queue query:query timeout:0.1];
2093         macdrv_release_query(query);
2094     }
2096     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2097     {
2098         NSDragOperation ret;
2099         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2100         NSPasteboard* pb = [sender draggingPasteboard];
2102         macdrv_query* query = macdrv_create_query();
2103         query->type = QUERY_DRAG_OPERATION;
2104         query->window = (macdrv_window)[self retain];
2105         query->drag_operation.x = pt.x;
2106         query->drag_operation.y = pt.y;
2107         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2108         query->drag_operation.accepted_op = NSDragOperationNone;
2109         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2111         [self.queue query:query timeout:3];
2112         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2113         macdrv_release_query(query);
2115         return ret;
2116     }
2118     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2119     {
2120         BOOL ret;
2121         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2122         NSPasteboard* pb = [sender draggingPasteboard];
2124         macdrv_query* query = macdrv_create_query();
2125         query->type = QUERY_DRAG_DROP;
2126         query->window = (macdrv_window)[self retain];
2127         query->drag_drop.x = pt.x;
2128         query->drag_drop.y = pt.y;
2129         query->drag_drop.op = [sender draggingSourceOperationMask];
2130         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2132         [self.queue query:query timeout:3 * 60 processEvents:YES];
2133         ret = query->status;
2134         macdrv_release_query(query);
2136         return ret;
2137     }
2139     - (BOOL) wantsPeriodicDraggingUpdates
2140     {
2141         return NO;
2142     }
2144 @end
2147 /***********************************************************************
2148  *              macdrv_create_cocoa_window
2150  * Create a Cocoa window with the given content frame and features (e.g.
2151  * title bar, close box, etc.).
2152  */
2153 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2154         CGRect frame, void* hwnd, macdrv_event_queue queue)
2156     __block WineWindow* window;
2158     OnMainThread(^{
2159         window = [[WineWindow createWindowWithFeatures:wf
2160                                            windowFrame:NSRectFromCGRect(frame)
2161                                                   hwnd:hwnd
2162                                                  queue:(WineEventQueue*)queue] retain];
2163     });
2165     return (macdrv_window)window;
2168 /***********************************************************************
2169  *              macdrv_destroy_cocoa_window
2171  * Destroy a Cocoa window.
2172  */
2173 void macdrv_destroy_cocoa_window(macdrv_window w)
2175     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2176     WineWindow* window = (WineWindow*)w;
2178     OnMainThread(^{
2179         [window doOrderOut];
2180         [window close];
2181     });
2182     [window.queue discardEventsMatchingMask:-1 forWindow:window];
2183     [window release];
2185     [pool release];
2188 /***********************************************************************
2189  *              macdrv_get_window_hwnd
2191  * Get the hwnd that was set for the window at creation.
2192  */
2193 void* macdrv_get_window_hwnd(macdrv_window w)
2195     WineWindow* window = (WineWindow*)w;
2196     return window.hwnd;
2199 /***********************************************************************
2200  *              macdrv_set_cocoa_window_features
2202  * Update a Cocoa window's features.
2203  */
2204 void macdrv_set_cocoa_window_features(macdrv_window w,
2205         const struct macdrv_window_features* wf)
2207     WineWindow* window = (WineWindow*)w;
2209     OnMainThread(^{
2210         [window setWindowFeatures:wf];
2211     });
2214 /***********************************************************************
2215  *              macdrv_set_cocoa_window_state
2217  * Update a Cocoa window's state.
2218  */
2219 void macdrv_set_cocoa_window_state(macdrv_window w,
2220         const struct macdrv_window_state* state)
2222     WineWindow* window = (WineWindow*)w;
2224     OnMainThread(^{
2225         [window setMacDrvState:state];
2226     });
2229 /***********************************************************************
2230  *              macdrv_set_cocoa_window_title
2232  * Set a Cocoa window's title.
2233  */
2234 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2235         size_t length)
2237     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2238     WineWindow* window = (WineWindow*)w;
2239     NSString* titleString;
2241     if (title)
2242         titleString = [NSString stringWithCharacters:title length:length];
2243     else
2244         titleString = @"";
2245     OnMainThreadAsync(^{
2246         [window setTitle:titleString];
2247         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2248             [NSApp changeWindowsItem:window title:titleString filename:NO];
2249     });
2251     [pool release];
2254 /***********************************************************************
2255  *              macdrv_order_cocoa_window
2257  * Reorder a Cocoa window relative to other windows.  If prev is
2258  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
2259  * it is ordered above that window.  Otherwise, it is ordered to the
2260  * front.
2261  */
2262 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2263         macdrv_window n, int activate)
2265     WineWindow* window = (WineWindow*)w;
2266     WineWindow* prev = (WineWindow*)p;
2267     WineWindow* next = (WineWindow*)n;
2269     OnMainThreadAsync(^{
2270         [window orderBelow:prev
2271                    orAbove:next
2272                   activate:activate];
2273     });
2274     [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2275                                   forWindow:window];
2276     [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2277                                 forWindow:next];
2280 /***********************************************************************
2281  *              macdrv_hide_cocoa_window
2283  * Hides a Cocoa window.
2284  */
2285 void macdrv_hide_cocoa_window(macdrv_window w)
2287     WineWindow* window = (WineWindow*)w;
2289     OnMainThread(^{
2290         [window doOrderOut];
2291     });
2294 /***********************************************************************
2295  *              macdrv_set_cocoa_window_frame
2297  * Move a Cocoa window.
2298  */
2299 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2301     WineWindow* window = (WineWindow*)w;
2303     OnMainThread(^{
2304         [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2305     });
2308 /***********************************************************************
2309  *              macdrv_get_cocoa_window_frame
2311  * Gets the frame of a Cocoa window.
2312  */
2313 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2315     WineWindow* window = (WineWindow*)w;
2317     OnMainThread(^{
2318         NSRect frame;
2320         frame = [window contentRectForFrameRect:[window frame]];
2321         [[WineApplicationController sharedController] flipRect:&frame];
2322         *out_frame = NSRectToCGRect(frame);
2323     });
2326 /***********************************************************************
2327  *              macdrv_set_cocoa_parent_window
2329  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
2330  * the parent window.
2331  */
2332 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2334     WineWindow* window = (WineWindow*)w;
2336     OnMainThread(^{
2337         [window setMacDrvParentWindow:(WineWindow*)parent];
2338     });
2341 /***********************************************************************
2342  *              macdrv_set_window_surface
2343  */
2344 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2346     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2347     WineWindow* window = (WineWindow*)w;
2349     OnMainThread(^{
2350         window.surface = surface;
2351         window.surface_mutex = mutex;
2352     });
2354     [pool release];
2357 /***********************************************************************
2358  *              macdrv_window_needs_display
2360  * Mark a window as needing display in a specified rect (in non-client
2361  * area coordinates).
2362  */
2363 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2365     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2366     WineWindow* window = (WineWindow*)w;
2368     OnMainThreadAsync(^{
2369         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2370     });
2372     [pool release];
2375 /***********************************************************************
2376  *              macdrv_set_window_shape
2378  * Sets the shape of a Cocoa window from an array of rectangles.  If
2379  * rects is NULL, resets the window's shape to its frame.
2380  */
2381 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2383     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2384     WineWindow* window = (WineWindow*)w;
2386     OnMainThread(^{
2387         if (!rects || !count)
2388         {
2389             window.shape = nil;
2390             window.shapeData = nil;
2391         }
2392         else
2393         {
2394             size_t length = sizeof(*rects) * count;
2395             if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
2396             {
2397                 NSBezierPath* path;
2398                 unsigned int i;
2400                 path = [NSBezierPath bezierPath];
2401                 for (i = 0; i < count; i++)
2402                     [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2403                 window.shape = path;
2404                 window.shapeData = [NSData dataWithBytes:rects length:length];
2405             }
2406         }
2407     });
2409     [pool release];
2412 /***********************************************************************
2413  *              macdrv_set_window_alpha
2414  */
2415 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2417     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2418     WineWindow* window = (WineWindow*)w;
2420     [window setAlphaValue:alpha];
2422     [pool release];
2425 /***********************************************************************
2426  *              macdrv_set_window_color_key
2427  */
2428 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2429                                  CGFloat keyBlue)
2431     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2432     WineWindow* window = (WineWindow*)w;
2434     OnMainThread(^{
2435         window.colorKeyed       = TRUE;
2436         window.colorKeyRed      = keyRed;
2437         window.colorKeyGreen    = keyGreen;
2438         window.colorKeyBlue     = keyBlue;
2439         [window checkTransparency];
2440     });
2442     [pool release];
2445 /***********************************************************************
2446  *              macdrv_clear_window_color_key
2447  */
2448 void macdrv_clear_window_color_key(macdrv_window w)
2450     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2451     WineWindow* window = (WineWindow*)w;
2453     OnMainThread(^{
2454         window.colorKeyed = FALSE;
2455         [window checkTransparency];
2456     });
2458     [pool release];
2461 /***********************************************************************
2462  *              macdrv_window_use_per_pixel_alpha
2463  */
2464 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2466     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2467     WineWindow* window = (WineWindow*)w;
2469     OnMainThread(^{
2470         window.usePerPixelAlpha = use_per_pixel_alpha;
2471         [window checkTransparency];
2472     });
2474     [pool release];
2477 /***********************************************************************
2478  *              macdrv_give_cocoa_window_focus
2480  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
2481  * orders it front and, if its frame was not within the desktop bounds,
2482  * Cocoa will typically move it on-screen.
2483  */
2484 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2486     WineWindow* window = (WineWindow*)w;
2488     OnMainThread(^{
2489         [window makeFocused:activate];
2490     });
2493 /***********************************************************************
2494  *              macdrv_set_window_min_max_sizes
2496  * Sets the window's minimum and maximum content sizes.
2497  */
2498 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2500     WineWindow* window = (WineWindow*)w;
2502     OnMainThread(^{
2503         [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2504     });
2507 /***********************************************************************
2508  *              macdrv_create_view
2510  * Creates and returns a view in the specified rect of the window.  The
2511  * caller is responsible for calling macdrv_dispose_view() on the view
2512  * when it is done with it.
2513  */
2514 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2516     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2517     WineWindow* window = (WineWindow*)w;
2518     __block WineContentView* view;
2520     if (CGRectIsNull(rect)) rect = CGRectZero;
2522     OnMainThread(^{
2523         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2525         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2526         [view setAutoresizesSubviews:NO];
2527         [nc addObserver:view
2528                selector:@selector(updateGLContexts)
2529                    name:NSViewGlobalFrameDidChangeNotification
2530                  object:view];
2531         [nc addObserver:view
2532                selector:@selector(updateGLContexts)
2533                    name:NSApplicationDidChangeScreenParametersNotification
2534                  object:NSApp];
2535         [[window contentView] addSubview:view];
2536         [window updateColorSpace];
2537     });
2539     [pool release];
2540     return (macdrv_view)view;
2543 /***********************************************************************
2544  *              macdrv_dispose_view
2546  * Destroys a view previously returned by macdrv_create_view.
2547  */
2548 void macdrv_dispose_view(macdrv_view v)
2550     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2551     WineContentView* view = (WineContentView*)v;
2553     OnMainThread(^{
2554         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2555         WineWindow* window = (WineWindow*)[view window];
2557         [nc removeObserver:view
2558                       name:NSViewGlobalFrameDidChangeNotification
2559                     object:view];
2560         [nc removeObserver:view
2561                       name:NSApplicationDidChangeScreenParametersNotification
2562                     object:NSApp];
2563         [view removeFromSuperview];
2564         [view release];
2565         [window updateColorSpace];
2566     });
2568     [pool release];
2571 /***********************************************************************
2572  *              macdrv_set_view_window_and_frame
2574  * Move a view to a new window and/or position within its window.  If w
2575  * is NULL, leave the view in its current window and just change its
2576  * frame.
2577  */
2578 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2580     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2581     WineContentView* view = (WineContentView*)v;
2582     WineWindow* window = (WineWindow*)w;
2584     if (CGRectIsNull(rect)) rect = CGRectZero;
2586     OnMainThread(^{
2587         BOOL changedWindow = (window && window != [view window]);
2588         NSRect newFrame = NSRectFromCGRect(rect);
2589         NSRect oldFrame = [view frame];
2590         BOOL needUpdateWindowColorSpace = FALSE;
2592         if (changedWindow)
2593         {
2594             WineWindow* oldWindow = (WineWindow*)[view window];
2595             [view removeFromSuperview];
2596             [oldWindow updateColorSpace];
2597             [[window contentView] addSubview:view];
2598             needUpdateWindowColorSpace = TRUE;
2599         }
2601         if (!NSEqualRects(oldFrame, newFrame))
2602         {
2603             if (!changedWindow)
2604                 [[view superview] setNeedsDisplayInRect:oldFrame];
2605             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2606                 [view setFrameSize:newFrame.size];
2607             else if (NSEqualSizes(oldFrame.size, newFrame.size))
2608                 [view setFrameOrigin:newFrame.origin];
2609             else
2610                 [view setFrame:newFrame];
2611             [view setNeedsDisplay:YES];
2612             needUpdateWindowColorSpace = TRUE;
2613         }
2615         if (needUpdateWindowColorSpace)
2616             [(WineWindow*)[view window] updateColorSpace];
2617     });
2619     [pool release];
2622 /***********************************************************************
2623  *              macdrv_add_view_opengl_context
2625  * Add an OpenGL context to the list being tracked for each view.
2626  */
2627 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2629     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2630     WineContentView* view = (WineContentView*)v;
2631     WineOpenGLContext *context = (WineOpenGLContext*)c;
2633     OnMainThread(^{
2634         [view addGLContext:context];
2635     });
2637     [pool release];
2640 /***********************************************************************
2641  *              macdrv_remove_view_opengl_context
2643  * Add an OpenGL context to the list being tracked for each view.
2644  */
2645 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2647     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2648     WineContentView* view = (WineContentView*)v;
2649     WineOpenGLContext *context = (WineOpenGLContext*)c;
2651     OnMainThreadAsync(^{
2652         [view removeGLContext:context];
2653     });
2655     [pool release];
2658 /***********************************************************************
2659  *              macdrv_window_background_color
2661  * Returns the standard Mac window background color as a 32-bit value of
2662  * the form 0x00rrggbb.
2663  */
2664 uint32_t macdrv_window_background_color(void)
2666     static uint32_t result;
2667     static dispatch_once_t once;
2669     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2670     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
2671     // of it is to draw with it.
2672     dispatch_once(&once, ^{
2673         OnMainThread(^{
2674             unsigned char rgbx[4];
2675             unsigned char *planes = rgbx;
2676             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2677                                                                                pixelsWide:1
2678                                                                                pixelsHigh:1
2679                                                                             bitsPerSample:8
2680                                                                           samplesPerPixel:3
2681                                                                                  hasAlpha:NO
2682                                                                                  isPlanar:NO
2683                                                                            colorSpaceName:NSCalibratedRGBColorSpace
2684                                                                              bitmapFormat:0
2685                                                                               bytesPerRow:4
2686                                                                              bitsPerPixel:32];
2687             [NSGraphicsContext saveGraphicsState];
2688             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2689             [[NSColor windowBackgroundColor] set];
2690             NSRectFill(NSMakeRect(0, 0, 1, 1));
2691             [NSGraphicsContext restoreGraphicsState];
2692             [bitmap release];
2693             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2694         });
2695     });
2697     return result;
2700 /***********************************************************************
2701  *              macdrv_send_text_input_event
2702  */
2703 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2705     __block BOOL ret;
2707     OnMainThread(^{
2708         WineWindow* window = (WineWindow*)[NSApp keyWindow];
2709         if (![window isKindOfClass:[WineWindow class]])
2710         {
2711             window = (WineWindow*)[NSApp mainWindow];
2712             if (![window isKindOfClass:[WineWindow class]])
2713                 window = [[WineApplicationController sharedController] frontWineWindow];
2714         }
2716         if (window)
2717         {
2718             NSUInteger localFlags = flags;
2719             CGEventRef c;
2720             NSEvent* event;
2722             window.imeData = data;
2723             fix_device_modifiers_by_generic(&localFlags);
2725             // An NSEvent created with +keyEventWithType:... is internally marked
2726             // as synthetic and doesn't get sent through input methods.  But one
2727             // created from a CGEvent doesn't have that problem.
2728             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2729             CGEventSetFlags(c, localFlags);
2730             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2731             event = [NSEvent eventWithCGEvent:c];
2732             CFRelease(c);
2734             window.commandDone = FALSE;
2735             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
2736         }
2737         else
2738             ret = FALSE;
2739     });
2741     return ret;