makefiles: Generate the common rules for programs from configure.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blob2bb9aef37f12ecfd9acb8d7c6d75e6e81f04d466
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 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
32 enum {
33     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
37 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
39     NSUInteger style_mask;
41     if (wf->title_bar)
42     {
43         style_mask = NSTitledWindowMask;
44         if (wf->close_button) style_mask |= NSClosableWindowMask;
45         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
46         if (wf->resizable) style_mask |= NSResizableWindowMask;
47         if (wf->utility) style_mask |= NSUtilityWindowMask;
48     }
49     else style_mask = NSBorderlessWindowMask;
51     return style_mask;
55 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
57     NSScreen* screen;
58     for (screen in screens)
59     {
60         if (NSIntersectsRect(frame, [screen frame]))
61             return TRUE;
62     }
63     return FALSE;
67 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
69     for (NSScreen* screen in screens)
70     {
71         if (NSContainsRect(rect, [screen frame]))
72             return screen;
73     }
74     return nil;
78 /* We rely on the supposedly device-dependent modifier flags to distinguish the
79    keys on the left side of the keyboard from those on the right.  Some event
80    sources don't set those device-depdendent flags.  If we see a device-independent
81    flag for a modifier without either corresponding device-dependent flag, assume
82    the left one. */
83 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
85     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
86         *modifiers |= NX_DEVICELCMDKEYMASK;
87     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
88         *modifiers |= NX_DEVICELSHIFTKEYMASK;
89     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
90         *modifiers |= NX_DEVICELCTLKEYMASK;
91     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
92         *modifiers |= NX_DEVICELALTKEYMASK;
95 /* As we manipulate individual bits of a modifier mask, we can end up with
96    inconsistent sets of flags.  In particular, we might set or clear one of the
97    left/right-specific bits, but not the corresponding non-side-specific bit.
98    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
99    otherwise clear it. */
100 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
102     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
103         *modifiers |= NX_COMMANDMASK;
104     else
105         *modifiers &= ~NX_COMMANDMASK;
106     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
107         *modifiers |= NX_SHIFTMASK;
108     else
109         *modifiers &= ~NX_SHIFTMASK;
110     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
111         *modifiers |= NX_CONTROLMASK;
112     else
113         *modifiers &= ~NX_CONTROLMASK;
114     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
115         *modifiers |= NX_ALTERNATEMASK;
116     else
117         *modifiers &= ~NX_ALTERNATEMASK;
120 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
122     fix_device_modifiers_by_generic(&modifiers);
123     if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
124     {
125         modifiers |= NX_DEVICELCMDKEYMASK;
126         modifiers &= ~NX_DEVICELALTKEYMASK;
127     }
128     if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
129     {
130         modifiers |= NX_DEVICERCMDKEYMASK;
131         modifiers &= ~NX_DEVICERALTKEYMASK;
132     }
133     fix_generic_modifiers_by_device(&modifiers);
135     return modifiers;
139 @interface WineContentView : NSView <NSTextInputClient>
141     NSMutableArray* glContexts;
142     NSMutableArray* pendingGlContexts;
144     NSMutableAttributedString* markedText;
145     NSRange markedTextSelection;
148     - (void) addGLContext:(WineOpenGLContext*)context;
149     - (void) removeGLContext:(WineOpenGLContext*)context;
150     - (void) updateGLContexts;
152 @end
155 @interface WineWindow ()
157 @property (readwrite, nonatomic) BOOL disabled;
158 @property (readwrite, nonatomic) BOOL noActivate;
159 @property (readwrite, nonatomic) BOOL floating;
160 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
161 @property (retain, nonatomic) NSWindow* latentParentWindow;
163 @property (nonatomic) void* hwnd;
164 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
166 @property (nonatomic) void* surface;
167 @property (nonatomic) pthread_mutex_t* surface_mutex;
169 @property (copy, nonatomic) NSBezierPath* shape;
170 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
171 @property (readonly, nonatomic) BOOL needsTransparency;
173 @property (nonatomic) BOOL colorKeyed;
174 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
175 @property (nonatomic) BOOL usePerPixelAlpha;
177 @property (assign, nonatomic) void* imeData;
178 @property (nonatomic) BOOL commandDone;
180 @property (retain, nonatomic) NSTimer* liveResizeDisplayTimer;
182     - (void) updateColorSpace;
184     - (BOOL) becameEligibleParentOrChild;
185     - (void) becameIneligibleChild;
187 @end
190 @implementation WineContentView
192     - (void) dealloc
193     {
194         [markedText release];
195         [glContexts release];
196         [pendingGlContexts release];
197         [super dealloc];
198     }
200     - (BOOL) isFlipped
201     {
202         return YES;
203     }
205     - (void) drawRect:(NSRect)rect
206     {
207         WineWindow* window = (WineWindow*)[self window];
209         for (WineOpenGLContext* context in pendingGlContexts)
210             context.needsUpdate = TRUE;
211         [glContexts addObjectsFromArray:pendingGlContexts];
212         [pendingGlContexts removeAllObjects];
214         if ([window contentView] != self)
215             return;
217         if (window.surface && window.surface_mutex &&
218             !pthread_mutex_lock(window.surface_mutex))
219         {
220             const CGRect* rects;
221             int count;
223             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
224             {
225                 CGContextRef context;
226                 int i;
228                 [window.shape addClip];
230                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
231                 CGContextSetBlendMode(context, kCGBlendModeCopy);
233                 for (i = 0; i < count; i++)
234                 {
235                     CGRect imageRect;
236                     CGImageRef image;
238                     imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
239                     image = create_surface_image(window.surface, &imageRect, FALSE);
241                     if (image)
242                     {
243                         if (window.colorKeyed)
244                         {
245                             CGImageRef maskedImage;
246                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
247                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
248                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
249                             maskedImage = CGImageCreateWithMaskingColors(image, components);
250                             if (maskedImage)
251                             {
252                                 CGImageRelease(image);
253                                 image = maskedImage;
254                             }
255                         }
257                         CGContextDrawImage(context, imageRect, image);
259                         CGImageRelease(image);
260                     }
261                 }
262             }
264             pthread_mutex_unlock(window.surface_mutex);
265         }
267         // If the window may be transparent, then we have to invalidate the
268         // shadow every time we draw.  Also, if this is the first time we've
269         // drawn since changing from transparent to opaque.
270         if (![window isOpaque] || window.shapeChangedSinceLastDraw)
271         {
272             window.shapeChangedSinceLastDraw = FALSE;
273             [window invalidateShadow];
274         }
275     }
277     - (void) addGLContext:(WineOpenGLContext*)context
278     {
279         if (!glContexts)
280             glContexts = [[NSMutableArray alloc] init];
281         if (!pendingGlContexts)
282             pendingGlContexts = [[NSMutableArray alloc] init];
283         [pendingGlContexts addObject:context];
284         [self setNeedsDisplay:YES];
285         [(WineWindow*)[self window] updateColorSpace];
286     }
288     - (void) removeGLContext:(WineOpenGLContext*)context
289     {
290         [glContexts removeObjectIdenticalTo:context];
291         [pendingGlContexts removeObjectIdenticalTo:context];
292         [(WineWindow*)[self window] updateColorSpace];
293     }
295     - (void) updateGLContexts
296     {
297         for (WineOpenGLContext* context in glContexts)
298             context.needsUpdate = TRUE;
299     }
301     - (BOOL) hasGLContext
302     {
303         return [glContexts count] || [pendingGlContexts count];
304     }
306     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
307     {
308         return YES;
309     }
311     - (BOOL) preservesContentDuringLiveResize
312     {
313         // Returning YES from this tells Cocoa to keep our view's content during
314         // a Cocoa-driven resize.  In theory, we're also supposed to override
315         // -setFrameSize: to mark exposed sections as needing redisplay, but
316         // user32 will take care of that in a roundabout way.  This way, we don't
317         // redraw until the window surface is flushed.
318         //
319         // This doesn't do anything when we resize the window ourselves.
320         return YES;
321     }
323     - (BOOL)acceptsFirstResponder
324     {
325         return [[self window] contentView] == self;
326     }
328     - (void) completeText:(NSString*)text
329     {
330         macdrv_event* event;
331         WineWindow* window = (WineWindow*)[self window];
333         event = macdrv_create_event(IM_SET_TEXT, window);
334         event->im_set_text.data = [window imeData];
335         event->im_set_text.text = (CFStringRef)[text copy];
336         event->im_set_text.complete = TRUE;
338         [[window queue] postEvent:event];
340         macdrv_release_event(event);
342         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
343         markedTextSelection = NSMakeRange(0, 0);
344         [[self inputContext] discardMarkedText];
345     }
347     /*
348      * ---------- NSTextInputClient methods ----------
349      */
350     - (NSTextInputContext*) inputContext
351     {
352         if (!markedText)
353             markedText = [[NSMutableAttributedString alloc] init];
354         return [super inputContext];
355     }
357     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
358     {
359         if ([string isKindOfClass:[NSAttributedString class]])
360             string = [string string];
362         if ([string isKindOfClass:[NSString class]])
363             [self completeText:string];
364     }
366     - (void) doCommandBySelector:(SEL)aSelector
367     {
368         [(WineWindow*)[self window] setCommandDone:TRUE];
369     }
371     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
372     {
373         if ([string isKindOfClass:[NSAttributedString class]])
374             string = [string string];
376         if ([string isKindOfClass:[NSString class]])
377         {
378             macdrv_event* event;
379             WineWindow* window = (WineWindow*)[self window];
381             if (replacementRange.location == NSNotFound)
382                 replacementRange = NSMakeRange(0, [markedText length]);
384             [markedText replaceCharactersInRange:replacementRange withString:string];
385             markedTextSelection = selectedRange;
386             markedTextSelection.location += replacementRange.location;
388             event = macdrv_create_event(IM_SET_TEXT, window);
389             event->im_set_text.data = [window imeData];
390             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
391             event->im_set_text.complete = FALSE;
392             event->im_set_text.cursor_pos = markedTextSelection.location;
394             [[window queue] postEvent:event];
396             macdrv_release_event(event);
398             [[self inputContext] invalidateCharacterCoordinates];
399         }
400     }
402     - (void) unmarkText
403     {
404         [self completeText:nil];
405     }
407     - (NSRange) selectedRange
408     {
409         return markedTextSelection;
410     }
412     - (NSRange) markedRange
413     {
414         NSRange range = NSMakeRange(0, [markedText length]);
415         if (!range.length)
416             range.location = NSNotFound;
417         return range;
418     }
420     - (BOOL) hasMarkedText
421     {
422         return [markedText length] > 0;
423     }
425     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
426     {
427         if (aRange.location >= [markedText length])
428             return nil;
430         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
431         if (actualRange)
432             *actualRange = aRange;
433         return [markedText attributedSubstringFromRange:aRange];
434     }
436     - (NSArray*) validAttributesForMarkedText
437     {
438         return [NSArray array];
439     }
441     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
442     {
443         macdrv_query* query;
444         WineWindow* window = (WineWindow*)[self window];
445         NSRect ret;
447         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
449         query = macdrv_create_query();
450         query->type = QUERY_IME_CHAR_RECT;
451         query->window = (macdrv_window)[window retain];
452         query->ime_char_rect.data = [window imeData];
453         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
455         if ([window.queue query:query timeout:1])
456         {
457             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
458             ret = NSRectFromCGRect(query->ime_char_rect.rect);
459             [[WineApplicationController sharedController] flipRect:&ret];
460         }
461         else
462             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
464         macdrv_release_query(query);
466         if (actualRange)
467             *actualRange = aRange;
468         return ret;
469     }
471     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
472     {
473         return NSNotFound;
474     }
476     - (NSInteger) windowLevel
477     {
478         return [[self window] level];
479     }
481 @end
484 @implementation WineWindow
486     static WineWindow* causing_becomeKeyWindow;
488     @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
489     @synthesize surface, surface_mutex;
490     @synthesize shape, shapeChangedSinceLastDraw;
491     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
492     @synthesize usePerPixelAlpha;
493     @synthesize imeData, commandDone;
494     @synthesize liveResizeDisplayTimer;
496     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
497                                  windowFrame:(NSRect)window_frame
498                                         hwnd:(void*)hwnd
499                                        queue:(WineEventQueue*)queue
500     {
501         WineWindow* window;
502         WineContentView* contentView;
503         NSTrackingArea* trackingArea;
505         [[WineApplicationController sharedController] flipRect:&window_frame];
507         window = [[[self alloc] initWithContentRect:window_frame
508                                           styleMask:style_mask_for_features(wf)
509                                             backing:NSBackingStoreBuffered
510                                               defer:YES] autorelease];
512         if (!window) return nil;
514         /* Standardize windows to eliminate differences between titled and
515            borderless windows and between NSWindow and NSPanel. */
516         [window setHidesOnDeactivate:NO];
517         [window setReleasedWhenClosed:NO];
519         [window setOneShot:YES];
520         [window disableCursorRects];
521         [window setShowsResizeIndicator:NO];
522         [window setHasShadow:wf->shadow];
523         [window setAcceptsMouseMovedEvents:YES];
524         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
525         [window setDelegate:window];
526         window.hwnd = hwnd;
527         window.queue = queue;
528         window->savedContentMinSize = NSZeroSize;
529         window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
531         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
532                                                                   (NSString*)kUTTypeContent,
533                                                                   nil]];
535         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
536         if (!contentView)
537             return nil;
538         [contentView setAutoresizesSubviews:NO];
540         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
541            because they give us mouse moves in the background. */
542         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
543                                                      options:(NSTrackingMouseMoved |
544                                                               NSTrackingActiveAlways |
545                                                               NSTrackingInVisibleRect)
546                                                        owner:window
547                                                     userInfo:nil] autorelease];
548         if (!trackingArea)
549             return nil;
550         [contentView addTrackingArea:trackingArea];
552         [window setContentView:contentView];
553         [window setInitialFirstResponder:contentView];
555         [[NSNotificationCenter defaultCenter] addObserver:window
556                                                  selector:@selector(updateFullscreen)
557                                                      name:NSApplicationDidChangeScreenParametersNotification
558                                                    object:NSApp];
559         [window updateFullscreen];
561         return window;
562     }
564     - (void) dealloc
565     {
566         [[NSNotificationCenter defaultCenter] removeObserver:self];
567         [liveResizeDisplayTimer invalidate];
568         [liveResizeDisplayTimer release];
569         [queue release];
570         [latentChildWindows release];
571         [latentParentWindow release];
572         [shape release];
573         [super dealloc];
574     }
576     - (void) adjustFeaturesForState
577     {
578         NSUInteger style = [self styleMask];
580         if (style & NSClosableWindowMask)
581             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
582         if (style & NSMiniaturizableWindowMask)
583             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
584         if (style & NSResizableWindowMask)
585             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
586     }
588     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
589     {
590         static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
591                                              NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
592         NSUInteger currentStyle = [self styleMask];
593         NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
595         if (newStyle != currentStyle)
596         {
597             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
598             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
599             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
600             {
601                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
602                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
603                 // just NSTitledWindowMask, the window buttons should disappear rather
604                 // than just being disabled.  But they don't.  Similarly in reverse.
605                 // The workaround is to also toggle NSClosableWindowMask at the same time.
606                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
607             }
608             [self setStyleMask:newStyle];
609         }
611         [self adjustFeaturesForState];
612         [self setHasShadow:wf->shadow];
613     }
615     - (BOOL) isOrderedIn
616     {
617         return [self isVisible] || [self isMiniaturized];
618     }
620     - (NSInteger) minimumLevelForActive:(BOOL)active
621     {
622         NSInteger level;
624         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
625                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
626             level = NSFloatingWindowLevel;
627         else
628             level = NSNormalWindowLevel;
630         if (active)
631         {
632             BOOL captured;
634             captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
636             if (captured || fullscreen)
637             {
638                 if (captured)
639                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
640                 else
641                     level = NSMainMenuWindowLevel + 1;
643                 if (self.floating)
644                     level++;
645             }
646         }
648         return level;
649     }
651     - (void) setMacDrvState:(const struct macdrv_window_state*)state
652     {
653         NSWindowCollectionBehavior behavior;
655         self.disabled = state->disabled;
656         self.noActivate = state->no_activate;
658         if (self.floating != state->floating)
659         {
660             self.floating = state->floating;
661             if (state->floating)
662             {
663                 // Became floating.  If child of non-floating window, make that
664                 // relationship latent.
665                 WineWindow* parent = (WineWindow*)[self parentWindow];
666                 if (parent && !parent.floating)
667                     [self becameIneligibleChild];
668             }
669             else
670             {
671                 // Became non-floating.  If parent of floating children, make that
672                 // relationship latent.
673                 WineWindow* child;
674                 for (child in [[[self childWindows] copy] autorelease])
675                 {
676                     if (child.floating)
677                         [child becameIneligibleChild];
678                 }
679             }
681             // Check our latent relationships.  If floating status was the only
682             // reason they were latent, then make them active.
683             if ([self isVisible])
684                 [self becameEligibleParentOrChild];
686             [[WineApplicationController sharedController] adjustWindowLevels];
687         }
689         behavior = NSWindowCollectionBehaviorDefault;
690         if (state->excluded_by_expose)
691             behavior |= NSWindowCollectionBehaviorTransient;
692         else
693             behavior |= NSWindowCollectionBehaviorManaged;
694         if (state->excluded_by_cycle)
695         {
696             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
697             if ([self isOrderedIn])
698                 [NSApp removeWindowsItem:self];
699         }
700         else
701         {
702             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
703             if ([self isOrderedIn])
704                 [NSApp addWindowsItem:self title:[self title] filename:NO];
705         }
706         [self setCollectionBehavior:behavior];
708         if (state->minimized_valid)
709         {
710             pendingMinimize = FALSE;
711             if (state->minimized && ![self isMiniaturized])
712             {
713                 if ([self isVisible])
714                     [super miniaturize:nil];
715                 else
716                     pendingMinimize = TRUE;
717             }
718             else if (!state->minimized && [self isMiniaturized])
719             {
720                 ignore_windowDeminiaturize = TRUE;
721                 [self deminiaturize:nil];
722             }
724             /* Whatever events regarding minimization might have been in the queue are now stale. */
725             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
726                                    forWindow:self];
727         }
728     }
730     - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
731     {
732         BOOL reordered = FALSE;
734         if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
735         {
736             if ([self level] > [child level])
737                 [child setLevel:[self level]];
738             [self addChildWindow:child ordered:NSWindowAbove];
739             [latentChildWindows removeObjectIdenticalTo:child];
740             child.latentParentWindow = nil;
741             reordered = TRUE;
742         }
743         else
744         {
745             if (!latentChildWindows)
746                 latentChildWindows = [[NSMutableArray alloc] init];
747             if (![latentChildWindows containsObject:child])
748                 [latentChildWindows addObject:child];
749             child.latentParentWindow = self;
750         }
752         return reordered;
753     }
755     - (BOOL) addChildWineWindow:(WineWindow*)child
756     {
757         return [self addChildWineWindow:child assumeVisible:FALSE];
758     }
760     - (void) removeChildWineWindow:(WineWindow*)child
761     {
762         [self removeChildWindow:child];
763         if (child.latentParentWindow == self)
764             child.latentParentWindow = nil;
765         [latentChildWindows removeObjectIdenticalTo:child];
766     }
768     - (BOOL) becameEligibleParentOrChild
769     {
770         BOOL reordered = FALSE;
771         NSUInteger count;
773         if (latentParentWindow.floating || !self.floating)
774         {
775             // If we aren't visible currently, we assume that we should be and soon
776             // will be.  So, if the latent parent is visible that's enough to assume
777             // we can establish the parent-child relationship in Cocoa.  That will
778             // actually make us visible, which is fine.
779             if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
780                 reordered = TRUE;
781         }
783         // Here, though, we may not actually be visible yet and adding a child
784         // won't make us visible.  The caller will have to call this method
785         // again after actually making us visible.
786         if ([self isVisible] && (count = [latentChildWindows count]))
787         {
788             NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
789             NSUInteger i;
791             for (i = 0; i < count; i++)
792             {
793                 WineWindow* child = [latentChildWindows objectAtIndex:i];
794                 if ([child isVisible] && (self.floating || !child.floating))
795                 {
796                     if (child.latentParentWindow == self)
797                     {
798                         if ([self level] > [child level])
799                             [child setLevel:[self level]];
800                         [self addChildWindow:child ordered:NSWindowAbove];
801                         child.latentParentWindow = nil;
802                         reordered = TRUE;
803                     }
804                     else
805                         ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
806                     [indexesToRemove addIndex:i];
807                 }
808             }
810             [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
811         }
813         return reordered;
814     }
816     - (void) becameIneligibleChild
817     {
818         WineWindow* parent = (WineWindow*)[self parentWindow];
819         if (parent)
820         {
821             if (!parent->latentChildWindows)
822                 parent->latentChildWindows = [[NSMutableArray alloc] init];
823             [parent->latentChildWindows insertObject:self atIndex:0];
824             self.latentParentWindow = parent;
825             [parent removeChildWindow:self];
826         }
827     }
829     - (void) becameIneligibleParentOrChild
830     {
831         NSArray* childWindows = [self childWindows];
833         [self becameIneligibleChild];
835         if ([childWindows count])
836         {
837             WineWindow* child;
839             childWindows = [[childWindows copy] autorelease];
840             for (child in childWindows)
841             {
842                 child.latentParentWindow = self;
843                 [self removeChildWindow:child];
844             }
846             if (latentChildWindows)
847                 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
848             else
849                 latentChildWindows = [childWindows mutableCopy];
850         }
851     }
853     // Determine if, among Wine windows, this window is directly above or below
854     // a given other Wine window with no other Wine window intervening.
855     // Intervening non-Wine windows are ignored.
856     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
857     {
858         NSNumber* windowNumber;
859         NSNumber* otherWindowNumber;
860         NSArray* windowNumbers;
861         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
863         if (![self isVisible] || ![otherWindow isVisible])
864             return FALSE;
866         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
867         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
868         windowNumbers = [[self class] windowNumbersWithOptions:0];
869         windowIndex = [windowNumbers indexOfObject:windowNumber];
870         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
872         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
873             return FALSE;
875         if (orderingMode == NSWindowAbove)
876         {
877             lowIndex = windowIndex;
878             highIndex = otherWindowIndex;
879         }
880         else if (orderingMode == NSWindowBelow)
881         {
882             lowIndex = otherWindowIndex;
883             highIndex = windowIndex;
884         }
885         else
886             return FALSE;
888         if (highIndex <= lowIndex)
889             return FALSE;
891         for (i = lowIndex + 1; i < highIndex; i++)
892         {
893             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
894             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
895             if ([interveningWindow isKindOfClass:[WineWindow class]])
896                 return FALSE;
897         }
899         return TRUE;
900     }
902     - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
903     {
904         NSMutableArray* windowNumbers;
905         NSNumber* childWindowNumber;
906         NSUInteger otherIndex, limit;
907         NSArray* origChildren;
908         NSMutableArray* children;
910         // Get the z-order from the window server and modify it to reflect the
911         // requested window ordering.
912         windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
913         childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
914         [windowNumbers removeObject:childWindowNumber];
915         otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
916         [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
918         // Get our child windows and sort them in the reverse of the desired
919         // z-order (back-to-front).
920         origChildren = [self childWindows];
921         children = [[origChildren mutableCopy] autorelease];
922         [children sortWithOptions:NSSortStable
923                   usingComparator:^NSComparisonResult(id obj1, id obj2){
924             NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
925             NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
926             NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
927             NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
928             if (index1 == NSNotFound)
929             {
930                 if (index2 == NSNotFound)
931                     return NSOrderedSame;
932                 else
933                     return NSOrderedAscending;
934             }
935             else if (index2 == NSNotFound)
936                 return NSOrderedDescending;
937             else if (index1 < index2)
938                 return NSOrderedDescending;
939             else if (index2 < index1)
940                 return NSOrderedAscending;
942             return NSOrderedSame;
943         }];
945         // If the current and desired children arrays match up to a point, leave
946         // those matching children alone.
947         limit = MIN([origChildren count], [children count]);
948         for (otherIndex = 0; otherIndex < limit; otherIndex++)
949         {
950             if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
951                 break;
952         }
953         [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
955         // Remove all of the child windows and re-add them back-to-front so they
956         // are in the desired order.
957         for (other in children)
958             [self removeChildWindow:other];
959         for (other in children)
960             [self addChildWindow:other ordered:NSWindowAbove];
961     }
963     /* Returns whether or not the window was ordered in, which depends on if
964        its frame intersects any screen. */
965     - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
966     {
967         WineApplicationController* controller = [WineApplicationController sharedController];
968         BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
969         if (on_screen && ![self isMiniaturized])
970         {
971             BOOL needAdjustWindowLevels = FALSE;
972             BOOL wasVisible = [self isVisible];
974             [controller transformProcessToForeground];
976             if (activate)
977                 [NSApp activateIgnoringOtherApps:YES];
979             NSDisableScreenUpdates();
981             if ([self becameEligibleParentOrChild])
982                 needAdjustWindowLevels = TRUE;
984             if (prev || next)
985             {
986                 WineWindow* other = [prev isVisible] ? prev : next;
987                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
989                 if (![self isOrdered:orderingMode relativeTo:other])
990                 {
991                     WineWindow* parent = (WineWindow*)[self parentWindow];
992                     WineWindow* otherParent = (WineWindow*)[other parentWindow];
994                     // This window level may not be right for this window based
995                     // on floating-ness, fullscreen-ness, etc.  But we set it
996                     // temporarily to allow us to order the windows properly.
997                     // Then the levels get fixed by -adjustWindowLevels.
998                     if ([self level] != [other level])
999                         [self setLevel:[other level]];
1000                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1002                     // The above call to -[NSWindow orderWindow:relativeTo:] won't
1003                     // reorder windows which are both children of the same parent
1004                     // relative to each other, so do that separately.
1005                     if (parent && parent == otherParent)
1006                         [parent order:orderingMode childWindow:self relativeTo:other];
1008                     needAdjustWindowLevels = TRUE;
1009                 }
1010             }
1011             else
1012             {
1013                 // Again, temporarily set level to make sure we can order to
1014                 // the right place.
1015                 next = [controller frontWineWindow];
1016                 if (next && [self level] < [next level])
1017                     [self setLevel:[next level]];
1018                 [self orderFront:nil];
1019                 needAdjustWindowLevels = TRUE;
1020             }
1022             if ([self becameEligibleParentOrChild])
1023                 needAdjustWindowLevels = TRUE;
1025             if (needAdjustWindowLevels)
1026             {
1027                 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1028                     [controller updateFullscreenWindows];
1029                 [controller adjustWindowLevels];
1030             }
1032             if (pendingMinimize)
1033             {
1034                 [super miniaturize:nil];
1035                 pendingMinimize = FALSE;
1036             }
1038             NSEnableScreenUpdates();
1040             /* Cocoa may adjust the frame when the window is ordered onto the screen.
1041                Generate a frame-changed event just in case.  The back end will ignore
1042                it if nothing actually changed. */
1043             [self windowDidResize:nil];
1045             if (![self isExcludedFromWindowsMenu])
1046                 [NSApp addWindowsItem:self title:[self title] filename:NO];
1047         }
1049         return on_screen;
1050     }
1052     - (void) doOrderOut
1053     {
1054         WineApplicationController* controller = [WineApplicationController sharedController];
1055         BOOL wasVisible = [self isVisible];
1056         BOOL wasOnActiveSpace = [self isOnActiveSpace];
1058         if ([self isMiniaturized])
1059             pendingMinimize = TRUE;
1061         [self becameIneligibleParentOrChild];
1062         if ([self isMiniaturized])
1063         {
1064             fakingClose = TRUE;
1065             [self close];
1066             fakingClose = FALSE;
1067         }
1068         else
1069             [self orderOut:nil];
1070         if (wasVisible && wasOnActiveSpace && fullscreen)
1071             [controller updateFullscreenWindows];
1072         [controller adjustWindowLevels];
1073         [NSApp removeWindowsItem:self];
1074     }
1076     - (void) updateFullscreen
1077     {
1078         NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1079         BOOL nowFullscreen = (screen_covered_by_rect(contentRect, [NSScreen screens]) != nil);
1081         if (nowFullscreen != fullscreen)
1082         {
1083             WineApplicationController* controller = [WineApplicationController sharedController];
1085             fullscreen = nowFullscreen;
1086             if ([self isVisible] && [self isOnActiveSpace])
1087                 [controller updateFullscreenWindows];
1089             [controller adjustWindowLevels];
1090         }
1091     }
1093     - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
1094     {
1095         NSArray* screens = [NSScreen screens];
1096         BOOL on_screen = [self isOrderedIn];
1098         if (![screens count]) return on_screen;
1100         /* Origin is (left, top) in a top-down space.  Need to convert it to
1101            (left, bottom) in a bottom-up space. */
1102         [[WineApplicationController sharedController] flipRect:&contentRect];
1104         if (on_screen)
1105         {
1106             on_screen = frame_intersects_screens(contentRect, screens);
1107             if (!on_screen)
1108                 [self doOrderOut];
1109         }
1111         /* The back end is establishing a new window size and position.  It's
1112            not interested in any stale events regarding those that may be sitting
1113            in the queue. */
1114         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1115                                forWindow:self];
1117         if (!NSIsEmptyRect(contentRect))
1118         {
1119             NSRect frame, oldFrame;
1121             oldFrame = [self frame];
1122             frame = [self frameRectForContentRect:contentRect];
1123             if (!NSEqualRects(frame, oldFrame))
1124             {
1125                 if (NSEqualSizes(frame.size, oldFrame.size))
1126                     [self setFrameOrigin:frame.origin];
1127                 else
1128                 {
1129                     [self setFrame:frame display:YES];
1130                     [self updateColorSpace];
1131                 }
1133                 [self updateFullscreen];
1135                 if (on_screen)
1136                 {
1137                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1138                        event.  The back end will ignore it if nothing actually changed. */
1139                     [self windowDidResize:nil];
1140                 }
1141             }
1142         }
1144         return on_screen;
1145     }
1147     - (void) setMacDrvParentWindow:(WineWindow*)parent
1148     {
1149         WineWindow* oldParent = (WineWindow*)[self parentWindow];
1150         if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1151         {
1152             [oldParent removeChildWineWindow:self];
1153             [latentParentWindow removeChildWineWindow:self];
1154             if ([parent addChildWineWindow:self])
1155                 [[WineApplicationController sharedController] adjustWindowLevels];
1156         }
1157     }
1159     - (void) setDisabled:(BOOL)newValue
1160     {
1161         if (disabled != newValue)
1162         {
1163             disabled = newValue;
1164             [self adjustFeaturesForState];
1166             if (disabled)
1167             {
1168                 NSSize size = [self contentRectForFrameRect:[self frame]].size;
1169                 [self setContentMinSize:size];
1170                 [self setContentMaxSize:size];
1171             }
1172             else
1173             {
1174                 [self setContentMaxSize:savedContentMaxSize];
1175                 [self setContentMinSize:savedContentMinSize];
1176             }
1177         }
1178     }
1180     - (BOOL) needsTransparency
1181     {
1182         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1183     }
1185     - (void) checkTransparency
1186     {
1187         if (![self isOpaque] && !self.needsTransparency)
1188         {
1189             [self setBackgroundColor:[NSColor windowBackgroundColor]];
1190             [self setOpaque:YES];
1191         }
1192         else if ([self isOpaque] && self.needsTransparency)
1193         {
1194             [self setBackgroundColor:[NSColor clearColor]];
1195             [self setOpaque:NO];
1196         }
1197     }
1199     - (void) setShape:(NSBezierPath*)newShape
1200     {
1201         if (shape == newShape) return;
1202         if (shape && newShape && [shape isEqual:newShape]) return;
1204         if (shape)
1205         {
1206             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1207             [shape release];
1208         }
1209         if (newShape)
1210             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1212         shape = [newShape copy];
1213         self.shapeChangedSinceLastDraw = TRUE;
1215         [self checkTransparency];
1216     }
1218     - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1219     {
1220         if (newTimer != liveResizeDisplayTimer)
1221         {
1222             [liveResizeDisplayTimer invalidate];
1223             [liveResizeDisplayTimer release];
1224             liveResizeDisplayTimer = [newTimer retain];
1225         }
1226     }
1228     - (void) makeFocused:(BOOL)activate
1229     {
1230         [self orderBelow:nil orAbove:nil activate:activate];
1232         causing_becomeKeyWindow = self;
1233         [self makeKeyWindow];
1234         causing_becomeKeyWindow = nil;
1235     }
1237     - (void) postKey:(uint16_t)keyCode
1238              pressed:(BOOL)pressed
1239            modifiers:(NSUInteger)modifiers
1240                event:(NSEvent*)theEvent
1241     {
1242         macdrv_event* event;
1243         CGEventRef cgevent;
1244         WineApplicationController* controller = [WineApplicationController sharedController];
1246         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1247         event->key.keycode   = keyCode;
1248         event->key.modifiers = modifiers;
1249         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1251         if ((cgevent = [theEvent CGEvent]))
1252         {
1253             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1254                                                         kCGKeyboardEventKeyboardType);
1255             if (keyboardType != controller.keyboardType)
1256             {
1257                 controller.keyboardType = keyboardType;
1258                 [controller keyboardSelectionDidChange];
1259             }
1260         }
1262         [queue postEvent:event];
1264         macdrv_release_event(event);
1266         [controller noteKey:keyCode pressed:pressed];
1267     }
1269     - (void) postKeyEvent:(NSEvent *)theEvent
1270     {
1271         [self flagsChanged:theEvent];
1272         [self postKey:[theEvent keyCode]
1273               pressed:[theEvent type] == NSKeyDown
1274             modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1275                 event:theEvent];
1276     }
1278     - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1279     {
1280         savedContentMinSize = minSize;
1281         savedContentMaxSize = maxSize;
1282         if (!self.disabled)
1283         {
1284             [self setContentMinSize:minSize];
1285             [self setContentMaxSize:maxSize];
1286         }
1287     }
1289     - (WineWindow*) ancestorWineWindow
1290     {
1291         WineWindow* ancestor = self;
1292         for (;;)
1293         {
1294             WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1295             if ([parent isKindOfClass:[WineWindow class]])
1296                 ancestor = parent;
1297             else
1298                 break;
1299         }
1300         return ancestor;
1301     }
1303     - (void) postBroughtForwardEvent
1304     {
1305         macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1306         [queue postEvent:event];
1307         macdrv_release_event(event);
1308     }
1311     /*
1312      * ---------- NSWindow method overrides ----------
1313      */
1314     - (BOOL) canBecomeKeyWindow
1315     {
1316         if (causing_becomeKeyWindow == self) return YES;
1317         if (self.disabled || self.noActivate) return NO;
1318         return [self isKeyWindow];
1319     }
1321     - (BOOL) canBecomeMainWindow
1322     {
1323         return [self canBecomeKeyWindow];
1324     }
1326     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1327     {
1328         // If a window is sized to completely cover a screen, then it's in
1329         // full-screen mode.  In that case, we don't allow NSWindow to constrain
1330         // it.
1331         NSRect contentRect = [self contentRectForFrameRect:frameRect];
1332         if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
1333             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1334         return frameRect;
1335     }
1337     - (BOOL) isExcludedFromWindowsMenu
1338     {
1339         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1340     }
1342     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1343     {
1344         BOOL ret = [super validateMenuItem:menuItem];
1346         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1347             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1349         return ret;
1350     }
1352     /* We don't call this.  It's the action method of the items in the Window menu. */
1353     - (void) makeKeyAndOrderFront:(id)sender
1354     {
1355         if ([self isMiniaturized])
1356             [self deminiaturize:nil];
1357         [self orderBelow:nil orAbove:nil activate:NO];
1358         [[self ancestorWineWindow] postBroughtForwardEvent];
1360         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1361             [[WineApplicationController sharedController] windowGotFocus:self];
1362     }
1364     - (void) sendEvent:(NSEvent*)event
1365     {
1366         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1367            interface control.  For example, Control-Tab switches focus among
1368            views.  We want to bypass that feature, so directly route key-down
1369            events to -keyDown:. */
1370         if ([event type] == NSKeyDown)
1371             [[self firstResponder] keyDown:event];
1372         else
1373             [super sendEvent:event];
1374     }
1376     - (void) miniaturize:(id)sender
1377     {
1378         macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1379         [queue postEvent:event];
1380         macdrv_release_event(event);
1381     }
1383     // We normally use the generic/calibrated RGB color space for the window,
1384     // rather than the device color space, to avoid expensive color conversion
1385     // which slows down drawing.  However, for windows displaying OpenGL, having
1386     // a different color space than the screen greatly reduces frame rates, often
1387     // limiting it to the display refresh rate.
1388     //
1389     // To avoid this, we switch back to the screen color space whenever the
1390     // window is covered by a view with an attached OpenGL context.
1391     - (void) updateColorSpace
1392     {
1393         NSRect contentRect = [[self contentView] frame];
1394         BOOL coveredByGLView = FALSE;
1395         for (WineContentView* view in [[self contentView] subviews])
1396         {
1397             if ([view hasGLContext])
1398             {
1399                 NSRect frame = [view convertRect:[view bounds] toView:nil];
1400                 if (NSContainsRect(frame, contentRect))
1401                 {
1402                     coveredByGLView = TRUE;
1403                     break;
1404                 }
1405             }
1406         }
1408         if (coveredByGLView)
1409             [self setColorSpace:nil];
1410         else
1411             [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1412     }
1415     /*
1416      * ---------- NSResponder method overrides ----------
1417      */
1418     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1420     - (void) flagsChanged:(NSEvent *)theEvent
1421     {
1422         static const struct {
1423             NSUInteger  mask;
1424             uint16_t    keycode;
1425         } modifiers[] = {
1426             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
1427             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
1428             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
1429             { NX_DEVICELCTLKEYMASK,     kVK_Control },
1430             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
1431             { NX_DEVICELALTKEYMASK,     kVK_Option },
1432             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
1433             { NX_DEVICELCMDKEYMASK,     kVK_Command },
1434             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
1435         };
1437         NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1438         NSUInteger changed;
1439         int i, last_changed;
1441         fix_device_modifiers_by_generic(&modifierFlags);
1442         changed = modifierFlags ^ lastModifierFlags;
1444         last_changed = -1;
1445         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1446             if (changed & modifiers[i].mask)
1447                 last_changed = i;
1449         for (i = 0; i <= last_changed; i++)
1450         {
1451             if (changed & modifiers[i].mask)
1452             {
1453                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1455                 if (i == last_changed)
1456                     lastModifierFlags = modifierFlags;
1457                 else
1458                 {
1459                     lastModifierFlags ^= modifiers[i].mask;
1460                     fix_generic_modifiers_by_device(&lastModifierFlags);
1461                 }
1463                 // Caps lock generates one event for each press-release action.
1464                 // We need to simulate a pair of events for each actual event.
1465                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1466                 {
1467                     [self postKey:modifiers[i].keycode
1468                           pressed:TRUE
1469                         modifiers:lastModifierFlags
1470                             event:(NSEvent*)theEvent];
1471                     pressed = FALSE;
1472                 }
1474                 [self postKey:modifiers[i].keycode
1475                       pressed:pressed
1476                     modifiers:lastModifierFlags
1477                         event:(NSEvent*)theEvent];
1478             }
1479         }
1480     }
1483     /*
1484      * ---------- NSWindowDelegate methods ----------
1485      */
1486     - (void)windowDidBecomeKey:(NSNotification *)notification
1487     {
1488         WineApplicationController* controller = [WineApplicationController sharedController];
1489         NSEvent* event = [controller lastFlagsChanged];
1490         if (event)
1491             [self flagsChanged:event];
1493         if (causing_becomeKeyWindow == self) return;
1495         [controller windowGotFocus:self];
1496     }
1498     - (void)windowDidDeminiaturize:(NSNotification *)notification
1499     {
1500         WineApplicationController* controller = [WineApplicationController sharedController];
1502         if (!ignore_windowDeminiaturize)
1503         {
1504             macdrv_event* event;
1506             /* Coalesce events by discarding any previous ones still in the queue. */
1507             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1508                                    forWindow:self];
1510             event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1511             [queue postEvent:event];
1512             macdrv_release_event(event);
1513         }
1515         ignore_windowDeminiaturize = FALSE;
1517         [self becameEligibleParentOrChild];
1519         if (fullscreen && [self isOnActiveSpace])
1520             [controller updateFullscreenWindows];
1521         [controller adjustWindowLevels];
1523         if (![self parentWindow])
1524             [self postBroughtForwardEvent];
1526         if (!self.disabled && !self.noActivate)
1527         {
1528             causing_becomeKeyWindow = self;
1529             [self makeKeyWindow];
1530             causing_becomeKeyWindow = nil;
1531             [controller windowGotFocus:self];
1532         }
1534         [self windowDidResize:notification];
1535     }
1537     - (void) windowDidEndLiveResize:(NSNotification *)notification
1538     {
1539         macdrv_query* query = macdrv_create_query();
1540         query->type = QUERY_RESIZE_END;
1541         query->window = (macdrv_window)[self retain];
1543         [self.queue query:query timeout:0.3];
1544         macdrv_release_query(query);
1546         self.liveResizeDisplayTimer = nil;
1547     }
1549     - (void)windowDidMiniaturize:(NSNotification *)notification
1550     {
1551         if (fullscreen && [self isOnActiveSpace])
1552             [[WineApplicationController sharedController] updateFullscreenWindows];
1553     }
1555     - (void)windowDidMove:(NSNotification *)notification
1556     {
1557         [self windowDidResize:notification];
1558     }
1560     - (void)windowDidResignKey:(NSNotification *)notification
1561     {
1562         macdrv_event* event;
1564         if (causing_becomeKeyWindow) return;
1566         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1567         [queue postEvent:event];
1568         macdrv_release_event(event);
1569     }
1571     - (void)windowDidResize:(NSNotification *)notification
1572     {
1573         macdrv_event* event;
1574         NSRect frame = [self contentRectForFrameRect:[self frame]];
1576         if (self.disabled)
1577         {
1578             [self setContentMinSize:frame.size];
1579             [self setContentMaxSize:frame.size];
1580         }
1582         [[WineApplicationController sharedController] flipRect:&frame];
1584         /* Coalesce events by discarding any previous ones still in the queue. */
1585         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1586                                forWindow:self];
1588         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1589         event->window_frame_changed.frame = NSRectToCGRect(frame);
1590         [queue postEvent:event];
1591         macdrv_release_event(event);
1593         [[[self contentView] inputContext] invalidateCharacterCoordinates];
1594         [self updateFullscreen];
1595     }
1597     - (BOOL)windowShouldClose:(id)sender
1598     {
1599         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1600         [queue postEvent:event];
1601         macdrv_release_event(event);
1602         return NO;
1603     }
1605     - (void) windowWillClose:(NSNotification*)notification
1606     {
1607         WineWindow* child;
1609         if (fakingClose) return;
1610         if (latentParentWindow)
1611         {
1612             [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
1613             self.latentParentWindow = nil;
1614         }
1616         for (child in latentChildWindows)
1617         {
1618             if (child.latentParentWindow == self)
1619                 child.latentParentWindow = nil;
1620         }
1621         [latentChildWindows removeAllObjects];
1622     }
1624     - (void)windowWillMiniaturize:(NSNotification *)notification
1625     {
1626         [self becameIneligibleParentOrChild];
1627     }
1629     - (void) windowWillStartLiveResize:(NSNotification *)notification
1630     {
1631         macdrv_query* query = macdrv_create_query();
1632         query->type = QUERY_RESIZE_START;
1633         query->window = (macdrv_window)[self retain];
1635         [self.queue query:query timeout:0.3];
1636         macdrv_release_query(query);
1638         // There's a strange restriction in window redrawing during Cocoa-
1639         // managed window resizing.  Only calls to -[NSView setNeedsDisplay...]
1640         // that happen synchronously when Cocoa tells us that our window size
1641         // has changed or asynchronously in a short interval thereafter provoke
1642         // the window to redraw.  Calls to those methods that happen asynchronously
1643         // a half second or more after the last change of the window size aren't
1644         // heeded until the next resize-related user event (e.g. mouse movement).
1645         //
1646         // Wine often has a significant delay between when it's been told that
1647         // the window has changed size and when it can flush completed drawing.
1648         // So, our windows would get stuck with incomplete drawing for as long
1649         // as the user holds the mouse button down and doesn't move it.
1650         //
1651         // We address this by "manually" asking our windows to check if they need
1652         // redrawing every so often (during live resize only).
1653         self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1654                                                                        target:self
1655                                                                      selector:@selector(displayIfNeeded)
1656                                                                      userInfo:nil
1657                                                                       repeats:YES];
1658         [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1659                                      forMode:NSRunLoopCommonModes];
1660     }
1662     - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
1663     {
1664         macdrv_query* query;
1665         NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
1666         NSSize maxSize;
1668         query = macdrv_create_query();
1669         query->type = QUERY_MIN_MAX_INFO;
1670         query->window = (macdrv_window)[self retain];
1671         [self.queue query:query timeout:0.5];
1672         macdrv_release_query(query);
1674         currentContentRect = [self contentRectForFrameRect:[self frame]];
1675         proposedContentRect = [self contentRectForFrameRect:proposedFrame];
1677         maxSize = [self contentMaxSize];
1678         newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
1679         newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
1681         // Try to keep the top-left corner where it is.
1682         newContentRect.origin.x = NSMinX(currentContentRect);
1683         newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
1685         // If that pushes the bottom or right off the screen, pull it up and to the left.
1686         screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
1687         if (NSMaxX(newContentRect) > NSMaxX(screenRect))
1688             newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
1689         if (NSMinY(newContentRect) < NSMinY(screenRect))
1690             newContentRect.origin.y = NSMinY(screenRect);
1692         // If that pushes the top or left off the screen, push it down and the right
1693         // again.  Do this last because the top-left corner is more important than the
1694         // bottom-right.
1695         if (NSMinX(newContentRect) < NSMinX(screenRect))
1696             newContentRect.origin.x = NSMinX(screenRect);
1697         if (NSMaxY(newContentRect) > NSMaxY(screenRect))
1698             newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
1700         return [self frameRectForContentRect:newContentRect];
1701     }
1704     /*
1705      * ---------- NSPasteboardOwner methods ----------
1706      */
1707     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1708     {
1709         macdrv_query* query = macdrv_create_query();
1710         query->type = QUERY_PASTEBOARD_DATA;
1711         query->window = (macdrv_window)[self retain];
1712         query->pasteboard_data.type = (CFStringRef)[type copy];
1714         [self.queue query:query timeout:3];
1715         macdrv_release_query(query);
1716     }
1719     /*
1720      * ---------- NSDraggingDestination methods ----------
1721      */
1722     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1723     {
1724         return [self draggingUpdated:sender];
1725     }
1727     - (void) draggingExited:(id <NSDraggingInfo>)sender
1728     {
1729         // This isn't really a query.  We don't need any response.  However, it
1730         // has to be processed in a similar manner as the other drag-and-drop
1731         // queries in order to maintain the proper order of operations.
1732         macdrv_query* query = macdrv_create_query();
1733         query->type = QUERY_DRAG_EXITED;
1734         query->window = (macdrv_window)[self retain];
1736         [self.queue query:query timeout:0.1];
1737         macdrv_release_query(query);
1738     }
1740     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1741     {
1742         NSDragOperation ret;
1743         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1744         NSPasteboard* pb = [sender draggingPasteboard];
1746         macdrv_query* query = macdrv_create_query();
1747         query->type = QUERY_DRAG_OPERATION;
1748         query->window = (macdrv_window)[self retain];
1749         query->drag_operation.x = pt.x;
1750         query->drag_operation.y = pt.y;
1751         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1752         query->drag_operation.accepted_op = NSDragOperationNone;
1753         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1755         [self.queue query:query timeout:3];
1756         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1757         macdrv_release_query(query);
1759         return ret;
1760     }
1762     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1763     {
1764         BOOL ret;
1765         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1766         NSPasteboard* pb = [sender draggingPasteboard];
1768         macdrv_query* query = macdrv_create_query();
1769         query->type = QUERY_DRAG_DROP;
1770         query->window = (macdrv_window)[self retain];
1771         query->drag_drop.x = pt.x;
1772         query->drag_drop.y = pt.y;
1773         query->drag_drop.op = [sender draggingSourceOperationMask];
1774         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1776         [self.queue query:query timeout:3 * 60 processEvents:YES];
1777         ret = query->status;
1778         macdrv_release_query(query);
1780         return ret;
1781     }
1783     - (BOOL) wantsPeriodicDraggingUpdates
1784     {
1785         return NO;
1786     }
1788 @end
1791 /***********************************************************************
1792  *              macdrv_create_cocoa_window
1794  * Create a Cocoa window with the given content frame and features (e.g.
1795  * title bar, close box, etc.).
1796  */
1797 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1798         CGRect frame, void* hwnd, macdrv_event_queue queue)
1800     __block WineWindow* window;
1802     OnMainThread(^{
1803         window = [[WineWindow createWindowWithFeatures:wf
1804                                            windowFrame:NSRectFromCGRect(frame)
1805                                                   hwnd:hwnd
1806                                                  queue:(WineEventQueue*)queue] retain];
1807     });
1809     return (macdrv_window)window;
1812 /***********************************************************************
1813  *              macdrv_destroy_cocoa_window
1815  * Destroy a Cocoa window.
1816  */
1817 void macdrv_destroy_cocoa_window(macdrv_window w)
1819     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1820     WineWindow* window = (WineWindow*)w;
1822     OnMainThread(^{
1823         [window doOrderOut];
1824         [window close];
1825     });
1826     [window.queue discardEventsMatchingMask:-1 forWindow:window];
1827     [window release];
1829     [pool release];
1832 /***********************************************************************
1833  *              macdrv_get_window_hwnd
1835  * Get the hwnd that was set for the window at creation.
1836  */
1837 void* macdrv_get_window_hwnd(macdrv_window w)
1839     WineWindow* window = (WineWindow*)w;
1840     return window.hwnd;
1843 /***********************************************************************
1844  *              macdrv_set_cocoa_window_features
1846  * Update a Cocoa window's features.
1847  */
1848 void macdrv_set_cocoa_window_features(macdrv_window w,
1849         const struct macdrv_window_features* wf)
1851     WineWindow* window = (WineWindow*)w;
1853     OnMainThread(^{
1854         [window setWindowFeatures:wf];
1855     });
1858 /***********************************************************************
1859  *              macdrv_set_cocoa_window_state
1861  * Update a Cocoa window's state.
1862  */
1863 void macdrv_set_cocoa_window_state(macdrv_window w,
1864         const struct macdrv_window_state* state)
1866     WineWindow* window = (WineWindow*)w;
1868     OnMainThread(^{
1869         [window setMacDrvState:state];
1870     });
1873 /***********************************************************************
1874  *              macdrv_set_cocoa_window_title
1876  * Set a Cocoa window's title.
1877  */
1878 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1879         size_t length)
1881     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1882     WineWindow* window = (WineWindow*)w;
1883     NSString* titleString;
1885     if (title)
1886         titleString = [NSString stringWithCharacters:title length:length];
1887     else
1888         titleString = @"";
1889     OnMainThreadAsync(^{
1890         [window setTitle:titleString];
1891         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
1892             [NSApp changeWindowsItem:window title:titleString filename:NO];
1893     });
1895     [pool release];
1898 /***********************************************************************
1899  *              macdrv_order_cocoa_window
1901  * Reorder a Cocoa window relative to other windows.  If prev is
1902  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
1903  * it is ordered above that window.  Otherwise, it is ordered to the
1904  * front.
1906  * Returns true if the window has actually been ordered onto the screen
1907  * (i.e. if its frame intersects with a screen).  Otherwise, false.
1908  */
1909 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1910         macdrv_window next, int activate)
1912     WineWindow* window = (WineWindow*)w;
1913     __block BOOL on_screen;
1915     OnMainThread(^{
1916         on_screen = [window orderBelow:(WineWindow*)prev
1917                                orAbove:(WineWindow*)next
1918                               activate:activate];
1919     });
1921     return on_screen;
1924 /***********************************************************************
1925  *              macdrv_hide_cocoa_window
1927  * Hides a Cocoa window.
1928  */
1929 void macdrv_hide_cocoa_window(macdrv_window w)
1931     WineWindow* window = (WineWindow*)w;
1933     OnMainThread(^{
1934         [window doOrderOut];
1935     });
1938 /***********************************************************************
1939  *              macdrv_set_cocoa_window_frame
1941  * Move a Cocoa window.  If the window has been moved out of the bounds
1942  * of the desktop, it is ordered out.  (This routine won't ever order a
1943  * window in, though.)
1945  * Returns true if the window is on screen; false otherwise.
1946  */
1947 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1949     WineWindow* window = (WineWindow*)w;
1950     __block BOOL on_screen;
1952     OnMainThread(^{
1953         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1954     });
1956     return on_screen;
1959 /***********************************************************************
1960  *              macdrv_get_cocoa_window_frame
1962  * Gets the frame of a Cocoa window.
1963  */
1964 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1966     WineWindow* window = (WineWindow*)w;
1968     OnMainThread(^{
1969         NSRect frame;
1971         frame = [window contentRectForFrameRect:[window frame]];
1972         [[WineApplicationController sharedController] flipRect:&frame];
1973         *out_frame = NSRectToCGRect(frame);
1974     });
1977 /***********************************************************************
1978  *              macdrv_set_cocoa_parent_window
1980  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
1981  * the parent window.
1982  */
1983 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1985     WineWindow* window = (WineWindow*)w;
1987     OnMainThread(^{
1988         [window setMacDrvParentWindow:(WineWindow*)parent];
1989     });
1992 /***********************************************************************
1993  *              macdrv_set_window_surface
1994  */
1995 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1997     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1998     WineWindow* window = (WineWindow*)w;
2000     OnMainThread(^{
2001         window.surface = surface;
2002         window.surface_mutex = mutex;
2003     });
2005     [pool release];
2008 /***********************************************************************
2009  *              macdrv_window_needs_display
2011  * Mark a window as needing display in a specified rect (in non-client
2012  * area coordinates).
2013  */
2014 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2016     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2017     WineWindow* window = (WineWindow*)w;
2019     OnMainThreadAsync(^{
2020         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2021     });
2023     [pool release];
2026 /***********************************************************************
2027  *              macdrv_set_window_shape
2029  * Sets the shape of a Cocoa window from an array of rectangles.  If
2030  * rects is NULL, resets the window's shape to its frame.
2031  */
2032 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2034     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2035     WineWindow* window = (WineWindow*)w;
2037     OnMainThread(^{
2038         if (!rects || !count)
2039             window.shape = nil;
2040         else
2041         {
2042             NSBezierPath* path;
2043             unsigned int i;
2045             path = [NSBezierPath bezierPath];
2046             for (i = 0; i < count; i++)
2047                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2048             window.shape = path;
2049         }
2050     });
2052     [pool release];
2055 /***********************************************************************
2056  *              macdrv_set_window_alpha
2057  */
2058 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2060     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2061     WineWindow* window = (WineWindow*)w;
2063     [window setAlphaValue:alpha];
2065     [pool release];
2068 /***********************************************************************
2069  *              macdrv_set_window_color_key
2070  */
2071 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2072                                  CGFloat keyBlue)
2074     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2075     WineWindow* window = (WineWindow*)w;
2077     OnMainThread(^{
2078         window.colorKeyed       = TRUE;
2079         window.colorKeyRed      = keyRed;
2080         window.colorKeyGreen    = keyGreen;
2081         window.colorKeyBlue     = keyBlue;
2082         [window checkTransparency];
2083     });
2085     [pool release];
2088 /***********************************************************************
2089  *              macdrv_clear_window_color_key
2090  */
2091 void macdrv_clear_window_color_key(macdrv_window w)
2093     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2094     WineWindow* window = (WineWindow*)w;
2096     OnMainThread(^{
2097         window.colorKeyed = FALSE;
2098         [window checkTransparency];
2099     });
2101     [pool release];
2104 /***********************************************************************
2105  *              macdrv_window_use_per_pixel_alpha
2106  */
2107 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2109     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2110     WineWindow* window = (WineWindow*)w;
2112     OnMainThread(^{
2113         window.usePerPixelAlpha = use_per_pixel_alpha;
2114         [window checkTransparency];
2115     });
2117     [pool release];
2120 /***********************************************************************
2121  *              macdrv_give_cocoa_window_focus
2123  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
2124  * orders it front and, if its frame was not within the desktop bounds,
2125  * Cocoa will typically move it on-screen.
2126  */
2127 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2129     WineWindow* window = (WineWindow*)w;
2131     OnMainThread(^{
2132         [window makeFocused:activate];
2133     });
2136 /***********************************************************************
2137  *              macdrv_set_window_min_max_sizes
2139  * Sets the window's minimum and maximum content sizes.
2140  */
2141 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2143     WineWindow* window = (WineWindow*)w;
2145     OnMainThread(^{
2146         [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2147     });
2150 /***********************************************************************
2151  *              macdrv_create_view
2153  * Creates and returns a view in the specified rect of the window.  The
2154  * caller is responsible for calling macdrv_dispose_view() on the view
2155  * when it is done with it.
2156  */
2157 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2159     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2160     WineWindow* window = (WineWindow*)w;
2161     __block WineContentView* view;
2163     if (CGRectIsNull(rect)) rect = CGRectZero;
2165     OnMainThread(^{
2166         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2168         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2169         [view setAutoresizesSubviews:NO];
2170         [nc addObserver:view
2171                selector:@selector(updateGLContexts)
2172                    name:NSViewGlobalFrameDidChangeNotification
2173                  object:view];
2174         [nc addObserver:view
2175                selector:@selector(updateGLContexts)
2176                    name:NSApplicationDidChangeScreenParametersNotification
2177                  object:NSApp];
2178         [[window contentView] addSubview:view];
2179         [window updateColorSpace];
2180     });
2182     [pool release];
2183     return (macdrv_view)view;
2186 /***********************************************************************
2187  *              macdrv_dispose_view
2189  * Destroys a view previously returned by macdrv_create_view.
2190  */
2191 void macdrv_dispose_view(macdrv_view v)
2193     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2194     WineContentView* view = (WineContentView*)v;
2196     OnMainThread(^{
2197         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2198         WineWindow* window = (WineWindow*)[view window];
2200         [nc removeObserver:view
2201                       name:NSViewGlobalFrameDidChangeNotification
2202                     object:view];
2203         [nc removeObserver:view
2204                       name:NSApplicationDidChangeScreenParametersNotification
2205                     object:NSApp];
2206         [view removeFromSuperview];
2207         [view release];
2208         [window updateColorSpace];
2209     });
2211     [pool release];
2214 /***********************************************************************
2215  *              macdrv_set_view_window_and_frame
2217  * Move a view to a new window and/or position within its window.  If w
2218  * is NULL, leave the view in its current window and just change its
2219  * frame.
2220  */
2221 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2223     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2224     WineContentView* view = (WineContentView*)v;
2225     WineWindow* window = (WineWindow*)w;
2227     if (CGRectIsNull(rect)) rect = CGRectZero;
2229     OnMainThread(^{
2230         BOOL changedWindow = (window && window != [view window]);
2231         NSRect newFrame = NSRectFromCGRect(rect);
2232         NSRect oldFrame = [view frame];
2233         BOOL needUpdateWindowColorSpace = FALSE;
2235         if (changedWindow)
2236         {
2237             WineWindow* oldWindow = (WineWindow*)[view window];
2238             [view removeFromSuperview];
2239             [oldWindow updateColorSpace];
2240             [[window contentView] addSubview:view];
2241             needUpdateWindowColorSpace = TRUE;
2242         }
2244         if (!NSEqualRects(oldFrame, newFrame))
2245         {
2246             if (!changedWindow)
2247                 [[view superview] setNeedsDisplayInRect:oldFrame];
2248             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2249                 [view setFrameSize:newFrame.size];
2250             else if (NSEqualSizes(oldFrame.size, newFrame.size))
2251                 [view setFrameOrigin:newFrame.origin];
2252             else
2253                 [view setFrame:newFrame];
2254             [view setNeedsDisplay:YES];
2255             needUpdateWindowColorSpace = TRUE;
2256         }
2258         if (needUpdateWindowColorSpace)
2259             [(WineWindow*)[view window] updateColorSpace];
2260     });
2262     [pool release];
2265 /***********************************************************************
2266  *              macdrv_add_view_opengl_context
2268  * Add an OpenGL context to the list being tracked for each view.
2269  */
2270 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2272     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2273     WineContentView* view = (WineContentView*)v;
2274     WineOpenGLContext *context = (WineOpenGLContext*)c;
2276     OnMainThreadAsync(^{
2277         [view addGLContext:context];
2278     });
2280     [pool release];
2283 /***********************************************************************
2284  *              macdrv_remove_view_opengl_context
2286  * Add an OpenGL context to the list being tracked for each view.
2287  */
2288 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2290     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2291     WineContentView* view = (WineContentView*)v;
2292     WineOpenGLContext *context = (WineOpenGLContext*)c;
2294     OnMainThreadAsync(^{
2295         [view removeGLContext:context];
2296     });
2298     [pool release];
2301 /***********************************************************************
2302  *              macdrv_window_background_color
2304  * Returns the standard Mac window background color as a 32-bit value of
2305  * the form 0x00rrggbb.
2306  */
2307 uint32_t macdrv_window_background_color(void)
2309     static uint32_t result;
2310     static dispatch_once_t once;
2312     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2313     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
2314     // of it is to draw with it.
2315     dispatch_once(&once, ^{
2316         OnMainThread(^{
2317             unsigned char rgbx[4];
2318             unsigned char *planes = rgbx;
2319             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2320                                                                                pixelsWide:1
2321                                                                                pixelsHigh:1
2322                                                                             bitsPerSample:8
2323                                                                           samplesPerPixel:3
2324                                                                                  hasAlpha:NO
2325                                                                                  isPlanar:NO
2326                                                                            colorSpaceName:NSCalibratedRGBColorSpace
2327                                                                              bitmapFormat:0
2328                                                                               bytesPerRow:4
2329                                                                              bitsPerPixel:32];
2330             [NSGraphicsContext saveGraphicsState];
2331             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2332             [[NSColor windowBackgroundColor] set];
2333             NSRectFill(NSMakeRect(0, 0, 1, 1));
2334             [NSGraphicsContext restoreGraphicsState];
2335             [bitmap release];
2336             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2337         });
2338     });
2340     return result;
2343 /***********************************************************************
2344  *              macdrv_send_text_input_event
2345  */
2346 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2348     __block BOOL ret;
2350     OnMainThread(^{
2351         WineWindow* window = (WineWindow*)[NSApp keyWindow];
2352         if (![window isKindOfClass:[WineWindow class]])
2353         {
2354             window = (WineWindow*)[NSApp mainWindow];
2355             if (![window isKindOfClass:[WineWindow class]])
2356                 window = [[WineApplicationController sharedController] frontWineWindow];
2357         }
2359         if (window)
2360         {
2361             NSUInteger localFlags = flags;
2362             CGEventRef c;
2363             NSEvent* event;
2365             window.imeData = data;
2366             fix_device_modifiers_by_generic(&localFlags);
2368             // An NSEvent created with +keyEventWithType:... is internally marked
2369             // as synthetic and doesn't get sent through input methods.  But one
2370             // created from a CGEvent doesn't have that problem.
2371             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2372             CGEventSetFlags(c, localFlags);
2373             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2374             event = [NSEvent eventWithCGEvent:c];
2375             CFRelease(c);
2377             window.commandDone = FALSE;
2378             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
2379         }
2380         else
2381             ret = FALSE;
2382     });
2384     return ret;