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