winemac: Implement support for full-screen windows.
[wine] / dlls / winemac.drv / cocoa_window.m
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  */
20
21 #import <Carbon/Carbon.h>
22
23 #import "cocoa_window.h"
24
25 #include "macdrv_cocoa.h"
26 #import "cocoa_app.h"
27 #import "cocoa_event.h"
28
29
30 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
31 enum {
32     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
33 };
34
35
36 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
37 {
38     NSUInteger style_mask;
39
40     if (wf->title_bar)
41     {
42         style_mask = NSTitledWindowMask;
43         if (wf->close_button) style_mask |= NSClosableWindowMask;
44         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
45         if (wf->resizable) style_mask |= NSResizableWindowMask;
46         if (wf->utility) style_mask |= NSUtilityWindowMask;
47     }
48     else style_mask = NSBorderlessWindowMask;
49
50     return style_mask;
51 }
52
53
54 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
55 {
56     NSScreen* screen;
57     for (screen in screens)
58     {
59         if (NSIntersectsRect(frame, [screen frame]))
60             return TRUE;
61     }
62     return FALSE;
63 }
64
65
66 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
67 {
68     for (NSScreen* screen in screens)
69     {
70         if (NSContainsRect(rect, [screen frame]))
71             return screen;
72     }
73     return nil;
74 }
75
76
77 /* We rely on the supposedly device-dependent modifier flags to distinguish the
78    keys on the left side of the keyboard from those on the right.  Some event
79    sources don't set those device-depdendent flags.  If we see a device-independent
80    flag for a modifier without either corresponding device-dependent flag, assume
81    the left one. */
82 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
83 {
84     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
85         *modifiers |= NX_DEVICELCMDKEYMASK;
86     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
87         *modifiers |= NX_DEVICELSHIFTKEYMASK;
88     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
89         *modifiers |= NX_DEVICELCTLKEYMASK;
90     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
91         *modifiers |= NX_DEVICELALTKEYMASK;
92 }
93
94 /* As we manipulate individual bits of a modifier mask, we can end up with
95    inconsistent sets of flags.  In particular, we might set or clear one of the
96    left/right-specific bits, but not the corresponding non-side-specific bit.
97    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
98    otherwise clear it. */
99 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
100 {
101     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
102         *modifiers |= NX_COMMANDMASK;
103     else
104         *modifiers &= ~NX_COMMANDMASK;
105     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
106         *modifiers |= NX_SHIFTMASK;
107     else
108         *modifiers &= ~NX_SHIFTMASK;
109     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
110         *modifiers |= NX_CONTROLMASK;
111     else
112         *modifiers &= ~NX_CONTROLMASK;
113     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
114         *modifiers |= NX_ALTERNATEMASK;
115     else
116         *modifiers &= ~NX_ALTERNATEMASK;
117 }
118
119
120 @interface WineContentView : NSView
121 @end
122
123
124 @interface WineWindow ()
125
126 @property (nonatomic) BOOL disabled;
127 @property (nonatomic) BOOL noActivate;
128 @property (readwrite, nonatomic) BOOL floating;
129 @property (retain, nonatomic) NSWindow* latentParentWindow;
130
131 @property (nonatomic) void* hwnd;
132 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
133
134 @property (nonatomic) void* surface;
135 @property (nonatomic) pthread_mutex_t* surface_mutex;
136
137 @property (copy, nonatomic) NSBezierPath* shape;
138 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
139 @property (readonly, nonatomic) BOOL needsTransparency;
140
141 @property (nonatomic) BOOL colorKeyed;
142 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
143 @property (nonatomic) BOOL usePerPixelAlpha;
144
145 @property (readwrite, nonatomic) NSInteger levelWhenActive;
146
147     + (void) flipRect:(NSRect*)rect;
148
149 @end
150
151
152 @implementation WineContentView
153
154     - (BOOL) isFlipped
155     {
156         return YES;
157     }
158
159     - (void) drawRect:(NSRect)rect
160     {
161         WineWindow* window = (WineWindow*)[self window];
162
163         if (window.surface && window.surface_mutex &&
164             !pthread_mutex_lock(window.surface_mutex))
165         {
166             const CGRect* rects;
167             int count;
168
169             if (!get_surface_region_rects(window.surface, &rects, &count) || count)
170             {
171                 CGRect imageRect;
172                 CGImageRef image;
173
174                 imageRect = NSRectToCGRect(rect);
175                 image = create_surface_image(window.surface, &imageRect, FALSE);
176
177                 if (image)
178                 {
179                     CGContextRef context;
180
181                     if (rects && count)
182                     {
183                         NSBezierPath* surfaceClip = [NSBezierPath bezierPath];
184                         int i;
185                         for (i = 0; i < count; i++)
186                             [surfaceClip appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
187                         [surfaceClip addClip];
188                     }
189
190                     [window.shape addClip];
191
192                     if (window.colorKeyed)
193                     {
194                         CGImageRef maskedImage;
195                         CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
196                                                  window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
197                                                  window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
198                         maskedImage = CGImageCreateWithMaskingColors(image, components);
199                         if (maskedImage)
200                         {
201                             CGImageRelease(image);
202                             image = maskedImage;
203                         }
204                     }
205
206                     context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
207                     CGContextSetBlendMode(context, kCGBlendModeCopy);
208                     CGContextDrawImage(context, imageRect, image);
209
210                     CGImageRelease(image);
211
212                     if (window.shapeChangedSinceLastDraw || window.colorKeyed ||
213                         window.usePerPixelAlpha)
214                     {
215                         window.shapeChangedSinceLastDraw = FALSE;
216                         [window invalidateShadow];
217                     }
218                 }
219             }
220
221             pthread_mutex_unlock(window.surface_mutex);
222         }
223     }
224
225     /* By default, NSView will swallow right-clicks in an attempt to support contextual
226        menus.  We need to bypass that and allow the event to make it to the window. */
227     - (void) rightMouseDown:(NSEvent*)theEvent
228     {
229         [[self window] rightMouseDown:theEvent];
230     }
231
232     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
233     {
234         return YES;
235     }
236
237 @end
238
239
240 @implementation WineWindow
241
242     @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
243     @synthesize surface, surface_mutex;
244     @synthesize shape, shapeChangedSinceLastDraw;
245     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
246     @synthesize usePerPixelAlpha;
247     @synthesize levelWhenActive;
248
249     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
250                                  windowFrame:(NSRect)window_frame
251                                         hwnd:(void*)hwnd
252                                        queue:(WineEventQueue*)queue
253     {
254         WineWindow* window;
255         WineContentView* contentView;
256         NSTrackingArea* trackingArea;
257
258         [self flipRect:&window_frame];
259
260         window = [[[self alloc] initWithContentRect:window_frame
261                                           styleMask:style_mask_for_features(wf)
262                                             backing:NSBackingStoreBuffered
263                                               defer:YES] autorelease];
264
265         if (!window) return nil;
266         window->normalStyleMask = [window styleMask];
267         window->forceNextMouseMoveAbsolute = TRUE;
268
269         /* Standardize windows to eliminate differences between titled and
270            borderless windows and between NSWindow and NSPanel. */
271         [window setHidesOnDeactivate:NO];
272         [window setReleasedWhenClosed:NO];
273
274         [window disableCursorRects];
275         [window setShowsResizeIndicator:NO];
276         [window setHasShadow:wf->shadow];
277         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
278         [window setDelegate:window];
279         window.hwnd = hwnd;
280         window.queue = queue;
281
282         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
283         if (!contentView)
284             return nil;
285         [contentView setAutoresizesSubviews:NO];
286
287         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
288                                                      options:(NSTrackingMouseEnteredAndExited |
289                                                               NSTrackingMouseMoved |
290                                                               NSTrackingActiveAlways |
291                                                               NSTrackingInVisibleRect)
292                                                        owner:window
293                                                     userInfo:nil] autorelease];
294         if (!trackingArea)
295             return nil;
296         [contentView addTrackingArea:trackingArea];
297
298         [window setContentView:contentView];
299
300         return window;
301     }
302
303     - (void) dealloc
304     {
305         [queue release];
306         [latentParentWindow release];
307         [shape release];
308         [super dealloc];
309     }
310
311     + (void) flipRect:(NSRect*)rect
312     {
313         rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
314     }
315
316     - (void) adjustFeaturesForState
317     {
318         NSUInteger style = normalStyleMask;
319
320         if (self.disabled)
321             style &= ~NSResizableWindowMask;
322         if (style != [self styleMask])
323             [self setStyleMask:style];
324
325         if (style & NSClosableWindowMask)
326             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
327         if (style & NSMiniaturizableWindowMask)
328             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
329     }
330
331     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
332     {
333         normalStyleMask = style_mask_for_features(wf);
334         [self adjustFeaturesForState];
335         [self setHasShadow:wf->shadow];
336     }
337
338     - (void) adjustWindowLevel
339     {
340         NSInteger level;
341         BOOL fullscreen;
342         NSScreen* screen;
343         NSUInteger index;
344         WineWindow* other = nil;
345
346         screen = screen_covered_by_rect([self frame], [NSScreen screens]);
347         fullscreen = (screen != nil);
348
349         if (fullscreen)
350         {
351             level = NSMainMenuWindowLevel + 1;
352             if (self.floating)
353                 level++;
354         }
355         else if (self.floating)
356             level = NSFloatingWindowLevel;
357         else
358             level = NSNormalWindowLevel;
359
360         index = [[NSApp orderedWineWindows] indexOfObjectIdenticalTo:self];
361         if (index != NSNotFound && index + 1 < [[NSApp orderedWineWindows] count])
362         {
363             other = [[NSApp orderedWineWindows] objectAtIndex:index + 1];
364             if (level < [other level])
365                 level = [other level];
366         }
367
368         if (level != [self level])
369         {
370             [self setLevelWhenActive:level];
371
372             /* Setting the window level above has moved this window to the front
373                of all other windows at the same level.  We need to move it
374                back into its proper place among other windows of that level.
375                Also, any windows which are supposed to be in front of it had
376                better have the same or higher window level.  If not, bump them
377                up. */
378             if (index != NSNotFound)
379             {
380                 for (; index > 0; index--)
381                 {
382                     other = [[NSApp orderedWineWindows] objectAtIndex:index - 1];
383                     if ([other level] < level)
384                         [other setLevelWhenActive:level];
385                     else
386                     {
387                         [self orderWindow:NSWindowBelow relativeTo:[other windowNumber]];
388                         break;
389                     }
390                 }
391             }
392         }
393     }
394
395     - (void) setMacDrvState:(const struct macdrv_window_state*)state
396     {
397         NSWindowCollectionBehavior behavior;
398
399         self.disabled = state->disabled;
400         self.noActivate = state->no_activate;
401
402         self.floating = state->floating;
403         [self adjustWindowLevel];
404
405         behavior = NSWindowCollectionBehaviorDefault;
406         if (state->excluded_by_expose)
407             behavior |= NSWindowCollectionBehaviorTransient;
408         else
409             behavior |= NSWindowCollectionBehaviorManaged;
410         if (state->excluded_by_cycle)
411         {
412             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
413             if ([self isVisible])
414                 [NSApp removeWindowsItem:self];
415         }
416         else
417         {
418             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
419             if ([self isVisible])
420                 [NSApp addWindowsItem:self title:[self title] filename:NO];
421         }
422         [self setCollectionBehavior:behavior];
423
424         if (state->minimized && ![self isMiniaturized])
425         {
426             ignore_windowMiniaturize = TRUE;
427             [self miniaturize:nil];
428         }
429         else if (!state->minimized && [self isMiniaturized])
430         {
431             ignore_windowDeminiaturize = TRUE;
432             [self deminiaturize:nil];
433         }
434
435         /* Whatever events regarding minimization might have been in the queue are now stale. */
436         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
437                                          event_mask_for_type(WINDOW_DID_UNMINIMIZE)
438                                forWindow:self];
439     }
440
441     /* Returns whether or not the window was ordered in, which depends on if
442        its frame intersects any screen. */
443     - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
444     {
445         BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
446         if (on_screen)
447         {
448             [NSApp transformProcessToForeground];
449
450             if (prev)
451             {
452                 /* Make sure that windows that should be above this one really are.
453                    This is necessary since a full-screen window gets a boost to its
454                    window level to be in front of the menu bar and Dock and that moves
455                    it out of the z-order that Win32 would otherwise establish. */
456                 if ([prev level] < [self level])
457                 {
458                     NSUInteger index = [[NSApp orderedWineWindows] indexOfObjectIdenticalTo:prev];
459                     if (index != NSNotFound)
460                     {
461                         [prev setLevelWhenActive:[self level]];
462                         for (; index > 0; index--)
463                         {
464                             WineWindow* other = [[NSApp orderedWineWindows] objectAtIndex:index - 1];
465                             if ([other level] < [self level])
466                                 [other setLevelWhenActive:[self level]];
467                         }
468                     }
469                 }
470                 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
471                 [NSApp wineWindow:self ordered:NSWindowBelow relativeTo:prev];
472             }
473             else
474             {
475                 /* Similarly, make sure this window is really above what it should be. */
476                 if (next && [next level] > [self level])
477                     [self setLevelWhenActive:[next level]];
478                 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
479                 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:next];
480             }
481             if (latentParentWindow)
482             {
483                 if ([latentParentWindow level] > [self level])
484                     [self setLevelWhenActive:[latentParentWindow level]];
485                 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
486                 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
487                 self.latentParentWindow = nil;
488             }
489
490             /* Cocoa may adjust the frame when the window is ordered onto the screen.
491                Generate a frame-changed event just in case.  The back end will ignore
492                it if nothing actually changed. */
493             [self windowDidResize:nil];
494
495             if (![self isExcludedFromWindowsMenu])
496                 [NSApp addWindowsItem:self title:[self title] filename:NO];
497         }
498
499         return on_screen;
500     }
501
502     - (void) doOrderOut
503     {
504         self.latentParentWindow = [self parentWindow];
505         [latentParentWindow removeChildWindow:self];
506         forceNextMouseMoveAbsolute = TRUE;
507         [self orderOut:nil];
508         [NSApp wineWindow:self ordered:NSWindowOut relativeTo:nil];
509         [NSApp removeWindowsItem:self];
510     }
511
512     - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
513     {
514         NSArray* screens = [NSScreen screens];
515         BOOL on_screen = [self isVisible];
516         NSRect frame, oldFrame;
517
518         if (![screens count]) return on_screen;
519
520         /* Origin is (left, top) in a top-down space.  Need to convert it to
521            (left, bottom) in a bottom-up space. */
522         [[self class] flipRect:&contentRect];
523
524         if (on_screen)
525         {
526             on_screen = frame_intersects_screens(contentRect, screens);
527             if (!on_screen)
528                 [self doOrderOut];
529         }
530
531         if (!NSIsEmptyRect(contentRect))
532         {
533             oldFrame = [self frame];
534             frame = [self frameRectForContentRect:contentRect];
535             if (!NSEqualRects(frame, oldFrame))
536             {
537                 if (NSEqualSizes(frame.size, oldFrame.size))
538                     [self setFrameOrigin:frame.origin];
539                 else
540                     [self setFrame:frame display:YES];
541             }
542         }
543
544         if (on_screen)
545         {
546             [self adjustWindowLevel];
547
548             /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
549                event.  The back end will ignore it if nothing actually changed. */
550             [self windowDidResize:nil];
551         }
552         else
553         {
554             /* The back end is establishing a new window size and position.  It's
555                not interested in any stale events regarding those that may be sitting
556                in the queue. */
557             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
558                                    forWindow:self];
559         }
560
561         return on_screen;
562     }
563
564     - (void) setMacDrvParentWindow:(WineWindow*)parent
565     {
566         if ([self parentWindow] != parent)
567         {
568             [[self parentWindow] removeChildWindow:self];
569             self.latentParentWindow = nil;
570             if ([self isVisible] && parent)
571             {
572                 if ([parent level] > [self level])
573                     [self setLevelWhenActive:[parent level]];
574                 [parent addChildWindow:self ordered:NSWindowAbove];
575                 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:parent];
576             }
577             else
578                 self.latentParentWindow = parent;
579         }
580     }
581
582     - (void) setDisabled:(BOOL)newValue
583     {
584         if (disabled != newValue)
585         {
586             disabled = newValue;
587             [self adjustFeaturesForState];
588         }
589     }
590
591     - (BOOL) needsTransparency
592     {
593         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
594     }
595
596     - (void) checkTransparency
597     {
598         if (![self isOpaque] && !self.needsTransparency)
599         {
600             [self setBackgroundColor:[NSColor windowBackgroundColor]];
601             [self setOpaque:YES];
602         }
603         else if ([self isOpaque] && self.needsTransparency)
604         {
605             [self setBackgroundColor:[NSColor clearColor]];
606             [self setOpaque:NO];
607         }
608     }
609
610     - (void) setShape:(NSBezierPath*)newShape
611     {
612         if (shape == newShape) return;
613         if (shape && newShape && [shape isEqual:newShape]) return;
614
615         if (shape)
616         {
617             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
618             [shape release];
619         }
620         if (newShape)
621             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
622
623         shape = [newShape copy];
624         self.shapeChangedSinceLastDraw = TRUE;
625
626         [self checkTransparency];
627     }
628
629     - (void) postMouseButtonEvent:(NSEvent *)theEvent pressed:(int)pressed
630     {
631         CGPoint pt = CGEventGetLocation([theEvent CGEvent]);
632         macdrv_event event;
633
634         event.type = MOUSE_BUTTON;
635         event.window = (macdrv_window)[self retain];
636         event.mouse_button.button = [theEvent buttonNumber];
637         event.mouse_button.pressed = pressed;
638         event.mouse_button.x = pt.x;
639         event.mouse_button.y = pt.y;
640         event.mouse_button.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
641
642         [queue postEvent:&event];
643     }
644
645     - (void) makeFocused
646     {
647         NSArray* screens;
648
649         [NSApp transformProcessToForeground];
650
651         /* If a borderless window is offscreen, orderFront: won't move
652            it onscreen like it would for a titled window.  Do that ourselves. */
653         screens = [NSScreen screens];
654         if (!([self styleMask] & NSTitledWindowMask) && ![self isVisible] &&
655             !frame_intersects_screens([self frame], screens))
656         {
657             NSScreen* primaryScreen = [screens objectAtIndex:0];
658             NSRect frame = [primaryScreen frame];
659             [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
660             frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
661             [self setFrame:frame display:YES];
662         }
663
664         if ([[NSApp orderedWineWindows] count])
665         {
666             WineWindow* front;
667             if (self.floating)
668                 front = [[NSApp orderedWineWindows] objectAtIndex:0];
669             else
670             {
671                 for (front in [NSApp orderedWineWindows])
672                     if (!front.floating) break;
673             }
674             if (front && [front levelWhenActive] > [self levelWhenActive])
675                 [self setLevelWhenActive:[front levelWhenActive]];
676         }
677         [self orderFront:nil];
678         [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
679         causing_becomeKeyWindow = TRUE;
680         [self makeKeyWindow];
681         causing_becomeKeyWindow = FALSE;
682         if (latentParentWindow)
683         {
684             if ([latentParentWindow level] > [self level])
685                 [self setLevelWhenActive:[latentParentWindow level]];
686             [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
687             [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
688             self.latentParentWindow = nil;
689         }
690         if (![self isExcludedFromWindowsMenu])
691             [NSApp addWindowsItem:self title:[self title] filename:NO];
692
693         /* Cocoa may adjust the frame when the window is ordered onto the screen.
694            Generate a frame-changed event just in case.  The back end will ignore
695            it if nothing actually changed. */
696         [self windowDidResize:nil];
697     }
698
699     - (void) postKey:(uint16_t)keyCode
700              pressed:(BOOL)pressed
701            modifiers:(NSUInteger)modifiers
702                event:(NSEvent*)theEvent
703     {
704         macdrv_event event;
705         CGEventRef cgevent;
706         WineApplication* app = (WineApplication*)NSApp;
707
708         event.type          = pressed ? KEY_PRESS : KEY_RELEASE;
709         event.window        = (macdrv_window)[self retain];
710         event.key.keycode   = keyCode;
711         event.key.modifiers = modifiers;
712         event.key.time_ms   = [app ticksForEventTime:[theEvent timestamp]];
713
714         if ((cgevent = [theEvent CGEvent]))
715         {
716             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
717                                                         kCGKeyboardEventKeyboardType);
718             if (keyboardType != app.keyboardType)
719             {
720                 app.keyboardType = keyboardType;
721                 [app keyboardSelectionDidChange];
722             }
723         }
724
725         [queue postEvent:&event];
726     }
727
728     - (void) postKeyEvent:(NSEvent *)theEvent
729     {
730         [self flagsChanged:theEvent];
731         [self postKey:[theEvent keyCode]
732               pressed:[theEvent type] == NSKeyDown
733             modifiers:[theEvent modifierFlags]
734                 event:theEvent];
735     }
736
737     - (void) postMouseMovedEvent:(NSEvent *)theEvent
738     {
739         macdrv_event event;
740
741         if (forceNextMouseMoveAbsolute)
742         {
743             CGPoint point = CGEventGetLocation([theEvent CGEvent]);
744
745             event.type = MOUSE_MOVED_ABSOLUTE;
746             event.mouse_moved.x = point.x;
747             event.mouse_moved.y = point.y;
748
749             mouseMoveDeltaX = 0;
750             mouseMoveDeltaY = 0;
751
752             forceNextMouseMoveAbsolute = FALSE;
753         }
754         else
755         {
756             /* Add event delta to accumulated delta error */
757             /* deltaY is already flipped */
758             mouseMoveDeltaX += [theEvent deltaX];
759             mouseMoveDeltaY += [theEvent deltaY];
760
761             event.type = MOUSE_MOVED;
762             event.mouse_moved.x = mouseMoveDeltaX;
763             event.mouse_moved.y = mouseMoveDeltaY;
764
765             /* Keep the remainder after integer truncation. */
766             mouseMoveDeltaX -= event.mouse_moved.x;
767             mouseMoveDeltaY -= event.mouse_moved.y;
768         }
769
770         if (event.type == MOUSE_MOVED_ABSOLUTE || event.mouse_moved.x || event.mouse_moved.y)
771         {
772             event.window = (macdrv_window)[self retain];
773             event.mouse_moved.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
774
775             [queue postEvent:&event];
776         }
777     }
778
779     - (void) setLevelWhenActive:(NSInteger)level
780     {
781         levelWhenActive = level;
782         if (([NSApp isActive] || level <= NSFloatingWindowLevel) &&
783             level != [self level])
784             [self setLevel:level];
785     }
786
787
788     /*
789      * ---------- NSWindow method overrides ----------
790      */
791     - (BOOL) canBecomeKeyWindow
792     {
793         if (causing_becomeKeyWindow) return YES;
794         if (self.disabled || self.noActivate) return NO;
795         return [self isKeyWindow];
796     }
797
798     - (BOOL) canBecomeMainWindow
799     {
800         return [self canBecomeKeyWindow];
801     }
802
803     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
804     {
805         // If a window is sized to completely cover a screen, then it's in
806         // full-screen mode.  In that case, we don't allow NSWindow to constrain
807         // it.
808         NSRect contentRect = [self contentRectForFrameRect:frameRect];
809         if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
810             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
811         return frameRect;
812     }
813
814     - (BOOL) isExcludedFromWindowsMenu
815     {
816         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
817     }
818
819     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
820     {
821         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
822             return [self isKeyWindow] || (!self.disabled && !self.noActivate);
823         return [super validateMenuItem:menuItem];
824     }
825
826     /* We don't call this.  It's the action method of the items in the Window menu. */
827     - (void) makeKeyAndOrderFront:(id)sender
828     {
829         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
830             [NSApp windowGotFocus:self];
831     }
832
833     - (void) sendEvent:(NSEvent*)event
834     {
835         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
836            interface control.  For example, Control-Tab switches focus among
837            views.  We want to bypass that feature, so directly route key-down
838            events to -keyDown:. */
839         if ([event type] == NSKeyDown)
840             [[self firstResponder] keyDown:event];
841         else
842         {
843             if ([event type] == NSLeftMouseDown)
844             {
845                 NSWindowButton windowButton;
846                 BOOL broughtWindowForward = TRUE;
847
848                 /* Since our windows generally claim they can't be made key, clicks
849                    in their title bars are swallowed by the theme frame stuff.  So,
850                    we hook directly into the event stream and assume that any click
851                    in the window will activate it, if Wine and the Win32 program
852                    accept. */
853                 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
854                     [NSApp windowGotFocus:self];
855
856                 /* Any left-click on our window anyplace other than the close or
857                    minimize buttons will bring it forward. */
858                 for (windowButton = NSWindowCloseButton;
859                      windowButton <= NSWindowMiniaturizeButton;
860                      windowButton++)
861                 {
862                     NSButton* button = [[event window] standardWindowButton:windowButton];
863                     if (button)
864                     {
865                         NSPoint point = [button convertPoint:[event locationInWindow] fromView:nil];
866                         if ([button mouse:point inRect:[button bounds]])
867                         {
868                             broughtWindowForward = FALSE;
869                             break;
870                         }
871                     }
872                 }
873
874                 if (broughtWindowForward)
875                     [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
876             }
877
878             [super sendEvent:event];
879         }
880     }
881
882
883     /*
884      * ---------- NSResponder method overrides ----------
885      */
886     - (void) mouseDown:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:1]; }
887     - (void) rightMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
888     - (void) otherMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
889
890     - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:0]; }
891     - (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
892     - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
893
894     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
895     - (void) keyUp:(NSEvent *)theEvent   { [self postKeyEvent:theEvent]; }
896
897     - (void) flagsChanged:(NSEvent *)theEvent
898     {
899         static const struct {
900             NSUInteger  mask;
901             uint16_t    keycode;
902         } modifiers[] = {
903             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
904             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
905             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
906             { NX_DEVICELCTLKEYMASK,     kVK_Control },
907             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
908             { NX_DEVICELALTKEYMASK,     kVK_Option },
909             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
910             { NX_DEVICELCMDKEYMASK,     kVK_Command },
911             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
912         };
913
914         NSUInteger modifierFlags = [theEvent modifierFlags];
915         NSUInteger changed;
916         int i, last_changed;
917
918         fix_device_modifiers_by_generic(&modifierFlags);
919         changed = modifierFlags ^ lastModifierFlags;
920
921         last_changed = -1;
922         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
923             if (changed & modifiers[i].mask)
924                 last_changed = i;
925
926         for (i = 0; i <= last_changed; i++)
927         {
928             if (changed & modifiers[i].mask)
929             {
930                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
931
932                 if (i == last_changed)
933                     lastModifierFlags = modifierFlags;
934                 else
935                 {
936                     lastModifierFlags ^= modifiers[i].mask;
937                     fix_generic_modifiers_by_device(&lastModifierFlags);
938                 }
939
940                 // Caps lock generates one event for each press-release action.
941                 // We need to simulate a pair of events for each actual event.
942                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
943                 {
944                     [self postKey:modifiers[i].keycode
945                           pressed:TRUE
946                         modifiers:lastModifierFlags
947                             event:(NSEvent*)theEvent];
948                     pressed = FALSE;
949                 }
950
951                 [self postKey:modifiers[i].keycode
952                       pressed:pressed
953                     modifiers:lastModifierFlags
954                         event:(NSEvent*)theEvent];
955             }
956         }
957     }
958
959     - (void) mouseEntered:(NSEvent *)theEvent { forceNextMouseMoveAbsolute = TRUE; }
960     - (void) mouseExited:(NSEvent *)theEvent  { forceNextMouseMoveAbsolute = TRUE; }
961
962     - (void) mouseMoved:(NSEvent *)theEvent         { [self postMouseMovedEvent:theEvent]; }
963     - (void) mouseDragged:(NSEvent *)theEvent       { [self postMouseMovedEvent:theEvent]; }
964     - (void) rightMouseDragged:(NSEvent *)theEvent  { [self postMouseMovedEvent:theEvent]; }
965     - (void) otherMouseDragged:(NSEvent *)theEvent  { [self postMouseMovedEvent:theEvent]; }
966
967     - (void) scrollWheel:(NSEvent *)theEvent
968     {
969         CGPoint pt;
970         macdrv_event event;
971         CGEventRef cgevent;
972         CGFloat x, y;
973         BOOL continuous = FALSE;
974
975         cgevent = [theEvent CGEvent];
976         pt = CGEventGetLocation(cgevent);
977
978         event.type = MOUSE_SCROLL;
979         event.window = (macdrv_window)[self retain];
980         event.mouse_scroll.x = pt.x;
981         event.mouse_scroll.y = pt.y;
982         event.mouse_scroll.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
983
984         if (CGEventGetIntegerValueField(cgevent, kCGScrollWheelEventIsContinuous))
985         {
986             continuous = TRUE;
987
988             /* Continuous scroll wheel events come from high-precision scrolling
989                hardware like Apple's Magic Mouse, Mighty Mouse, and trackpads.
990                For these, we can get more precise data from the CGEvent API. */
991             /* Axis 1 is vertical, axis 2 is horizontal. */
992             x = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis2);
993             y = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis1);
994         }
995         else
996         {
997             double pixelsPerLine = 10;
998             CGEventSourceRef source;
999
1000             /* The non-continuous values are in units of "lines", not pixels. */
1001             if ((source = CGEventCreateSourceFromEvent(cgevent)))
1002             {
1003                 pixelsPerLine = CGEventSourceGetPixelsPerLine(source);
1004                 CFRelease(source);
1005             }
1006
1007             x = pixelsPerLine * [theEvent deltaX];
1008             y = pixelsPerLine * [theEvent deltaY];
1009         }
1010
1011         /* Mac: negative is right or down, positive is left or up.
1012            Win32: negative is left or down, positive is right or up.
1013            So, negate the X scroll value to translate. */
1014         x = -x;
1015
1016         /* The x,y values so far are in pixels.  Win32 expects to receive some
1017            fraction of WHEEL_DELTA == 120.  By my estimation, that's roughly
1018            6 times the pixel value. */
1019         event.mouse_scroll.x_scroll = 6 * x;
1020         event.mouse_scroll.y_scroll = 6 * y;
1021
1022         if (!continuous)
1023         {
1024             /* For non-continuous "clicky" wheels, if there was any motion, make
1025                sure there was at least WHEEL_DELTA motion.  This is so, at slow
1026                speeds where the system's acceleration curve is actually reducing the
1027                scroll distance, the user is sure to get some action out of each click.
1028                For example, this is important for rotating though weapons in a
1029                first-person shooter. */
1030             if (0 < event.mouse_scroll.x_scroll && event.mouse_scroll.x_scroll < 120)
1031                 event.mouse_scroll.x_scroll = 120;
1032             else if (-120 < event.mouse_scroll.x_scroll && event.mouse_scroll.x_scroll < 0)
1033                 event.mouse_scroll.x_scroll = -120;
1034
1035             if (0 < event.mouse_scroll.y_scroll && event.mouse_scroll.y_scroll < 120)
1036                 event.mouse_scroll.y_scroll = 120;
1037             else if (-120 < event.mouse_scroll.y_scroll && event.mouse_scroll.y_scroll < 0)
1038                 event.mouse_scroll.y_scroll = -120;
1039         }
1040
1041         if (event.mouse_scroll.x_scroll || event.mouse_scroll.y_scroll)
1042             [queue postEvent:&event];
1043     }
1044
1045
1046     /*
1047      * ---------- NSWindowDelegate methods ----------
1048      */
1049     - (void)windowDidBecomeKey:(NSNotification *)notification
1050     {
1051         NSEvent* event = [NSApp lastFlagsChanged];
1052         if (event)
1053             [self flagsChanged:event];
1054
1055         if (causing_becomeKeyWindow) return;
1056
1057         [NSApp windowGotFocus:self];
1058     }
1059
1060     - (void)windowDidDeminiaturize:(NSNotification *)notification
1061     {
1062         if (!ignore_windowDeminiaturize)
1063         {
1064             macdrv_event event;
1065
1066             /* Coalesce events by discarding any previous ones still in the queue. */
1067             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1068                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1069                                    forWindow:self];
1070
1071             event.type = WINDOW_DID_UNMINIMIZE;
1072             event.window = (macdrv_window)[self retain];
1073             [queue postEvent:&event];
1074         }
1075
1076         ignore_windowDeminiaturize = FALSE;
1077
1078         [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1079     }
1080
1081     - (void)windowDidMove:(NSNotification *)notification
1082     {
1083         [self windowDidResize:notification];
1084     }
1085
1086     - (void)windowDidResignKey:(NSNotification *)notification
1087     {
1088         macdrv_event event;
1089
1090         if (causing_becomeKeyWindow) return;
1091
1092         event.type = WINDOW_LOST_FOCUS;
1093         event.window = (macdrv_window)[self retain];
1094         [queue postEvent:&event];
1095     }
1096
1097     - (void)windowDidResize:(NSNotification *)notification
1098     {
1099         macdrv_event event;
1100         NSRect frame = [self contentRectForFrameRect:[self frame]];
1101
1102         [[self class] flipRect:&frame];
1103
1104         /* Coalesce events by discarding any previous ones still in the queue. */
1105         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1106                                forWindow:self];
1107
1108         event.type = WINDOW_FRAME_CHANGED;
1109         event.window = (macdrv_window)[self retain];
1110         event.window_frame_changed.frame = NSRectToCGRect(frame);
1111         [queue postEvent:&event];
1112     }
1113
1114     - (BOOL)windowShouldClose:(id)sender
1115     {
1116         macdrv_event event;
1117         event.type = WINDOW_CLOSE_REQUESTED;
1118         event.window = (macdrv_window)[self retain];
1119         [queue postEvent:&event];
1120         return NO;
1121     }
1122
1123     - (void)windowWillMiniaturize:(NSNotification *)notification
1124     {
1125         if (!ignore_windowMiniaturize)
1126         {
1127             macdrv_event event;
1128
1129             /* Coalesce events by discarding any previous ones still in the queue. */
1130             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1131                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1132                                    forWindow:self];
1133
1134             event.type = WINDOW_DID_MINIMIZE;
1135             event.window = (macdrv_window)[self retain];
1136             [queue postEvent:&event];
1137         }
1138
1139         ignore_windowMiniaturize = FALSE;
1140     }
1141
1142 @end
1143
1144
1145 /***********************************************************************
1146  *              macdrv_create_cocoa_window
1147  *
1148  * Create a Cocoa window with the given content frame and features (e.g.
1149  * title bar, close box, etc.).
1150  */
1151 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1152         CGRect frame, void* hwnd, macdrv_event_queue queue)
1153 {
1154     __block WineWindow* window;
1155
1156     OnMainThread(^{
1157         window = [[WineWindow createWindowWithFeatures:wf
1158                                            windowFrame:NSRectFromCGRect(frame)
1159                                                   hwnd:hwnd
1160                                                  queue:(WineEventQueue*)queue] retain];
1161     });
1162
1163     return (macdrv_window)window;
1164 }
1165
1166 /***********************************************************************
1167  *              macdrv_destroy_cocoa_window
1168  *
1169  * Destroy a Cocoa window.
1170  */
1171 void macdrv_destroy_cocoa_window(macdrv_window w)
1172 {
1173     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1174     WineWindow* window = (WineWindow*)w;
1175
1176     [window.queue discardEventsMatchingMask:-1 forWindow:window];
1177     [window close];
1178     [window release];
1179
1180     [pool release];
1181 }
1182
1183 /***********************************************************************
1184  *              macdrv_get_window_hwnd
1185  *
1186  * Get the hwnd that was set for the window at creation.
1187  */
1188 void* macdrv_get_window_hwnd(macdrv_window w)
1189 {
1190     WineWindow* window = (WineWindow*)w;
1191     return window.hwnd;
1192 }
1193
1194 /***********************************************************************
1195  *              macdrv_set_cocoa_window_features
1196  *
1197  * Update a Cocoa window's features.
1198  */
1199 void macdrv_set_cocoa_window_features(macdrv_window w,
1200         const struct macdrv_window_features* wf)
1201 {
1202     WineWindow* window = (WineWindow*)w;
1203
1204     OnMainThread(^{
1205         [window setWindowFeatures:wf];
1206     });
1207 }
1208
1209 /***********************************************************************
1210  *              macdrv_set_cocoa_window_state
1211  *
1212  * Update a Cocoa window's state.
1213  */
1214 void macdrv_set_cocoa_window_state(macdrv_window w,
1215         const struct macdrv_window_state* state)
1216 {
1217     WineWindow* window = (WineWindow*)w;
1218
1219     OnMainThread(^{
1220         [window setMacDrvState:state];
1221     });
1222 }
1223
1224 /***********************************************************************
1225  *              macdrv_set_cocoa_window_title
1226  *
1227  * Set a Cocoa window's title.
1228  */
1229 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1230         size_t length)
1231 {
1232     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1233     WineWindow* window = (WineWindow*)w;
1234     NSString* titleString;
1235
1236     if (title)
1237         titleString = [NSString stringWithCharacters:title length:length];
1238     else
1239         titleString = @"";
1240     OnMainThreadAsync(^{
1241         [window setTitle:titleString];
1242         if ([window isVisible] && ![window isExcludedFromWindowsMenu])
1243             [NSApp changeWindowsItem:window title:titleString filename:NO];
1244     });
1245
1246     [pool release];
1247 }
1248
1249 /***********************************************************************
1250  *              macdrv_order_cocoa_window
1251  *
1252  * Reorder a Cocoa window relative to other windows.  If prev is
1253  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
1254  * it is ordered above that window.  Otherwise, it is ordered to the
1255  * front.
1256  *
1257  * Returns true if the window has actually been ordered onto the screen
1258  * (i.e. if its frame intersects with a screen).  Otherwise, false.
1259  */
1260 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1261         macdrv_window next)
1262 {
1263     WineWindow* window = (WineWindow*)w;
1264     __block BOOL on_screen;
1265
1266     OnMainThread(^{
1267         on_screen = [window orderBelow:(WineWindow*)prev
1268                                orAbove:(WineWindow*)next];
1269     });
1270
1271     return on_screen;
1272 }
1273
1274 /***********************************************************************
1275  *              macdrv_hide_cocoa_window
1276  *
1277  * Hides a Cocoa window.
1278  */
1279 void macdrv_hide_cocoa_window(macdrv_window w)
1280 {
1281     WineWindow* window = (WineWindow*)w;
1282
1283     OnMainThread(^{
1284         [window doOrderOut];
1285     });
1286 }
1287
1288 /***********************************************************************
1289  *              macdrv_set_cocoa_window_frame
1290  *
1291  * Move a Cocoa window.  If the window has been moved out of the bounds
1292  * of the desktop, it is ordered out.  (This routine won't ever order a
1293  * window in, though.)
1294  *
1295  * Returns true if the window is on screen; false otherwise.
1296  */
1297 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1298 {
1299     WineWindow* window = (WineWindow*)w;
1300     __block BOOL on_screen;
1301
1302     OnMainThread(^{
1303         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1304     });
1305
1306     return on_screen;
1307 }
1308
1309 /***********************************************************************
1310  *              macdrv_get_cocoa_window_frame
1311  *
1312  * Gets the frame of a Cocoa window.
1313  */
1314 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1315 {
1316     WineWindow* window = (WineWindow*)w;
1317
1318     OnMainThread(^{
1319         NSRect frame;
1320
1321         frame = [window contentRectForFrameRect:[window frame]];
1322         [[window class] flipRect:&frame];
1323         *out_frame = NSRectToCGRect(frame);
1324     });
1325 }
1326
1327 /***********************************************************************
1328  *              macdrv_set_cocoa_parent_window
1329  *
1330  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
1331  * the parent window.
1332  */
1333 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1334 {
1335     WineWindow* window = (WineWindow*)w;
1336
1337     OnMainThread(^{
1338         [window setMacDrvParentWindow:(WineWindow*)parent];
1339     });
1340 }
1341
1342 /***********************************************************************
1343  *              macdrv_set_window_surface
1344  */
1345 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1346 {
1347     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1348     WineWindow* window = (WineWindow*)w;
1349
1350     OnMainThread(^{
1351         window.surface = surface;
1352         window.surface_mutex = mutex;
1353     });
1354
1355     [pool release];
1356 }
1357
1358 /***********************************************************************
1359  *              macdrv_window_needs_display
1360  *
1361  * Mark a window as needing display in a specified rect (in non-client
1362  * area coordinates).
1363  */
1364 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1365 {
1366     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1367     WineWindow* window = (WineWindow*)w;
1368
1369     OnMainThreadAsync(^{
1370         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1371     });
1372
1373     [pool release];
1374 }
1375
1376 /***********************************************************************
1377  *              macdrv_set_window_shape
1378  *
1379  * Sets the shape of a Cocoa window from an array of rectangles.  If
1380  * rects is NULL, resets the window's shape to its frame.
1381  */
1382 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1383 {
1384     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1385     WineWindow* window = (WineWindow*)w;
1386
1387     OnMainThread(^{
1388         if (!rects || !count)
1389             window.shape = nil;
1390         else
1391         {
1392             NSBezierPath* path;
1393             unsigned int i;
1394
1395             path = [NSBezierPath bezierPath];
1396             for (i = 0; i < count; i++)
1397                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1398             window.shape = path;
1399         }
1400     });
1401
1402     [pool release];
1403 }
1404
1405 /***********************************************************************
1406  *              macdrv_set_window_alpha
1407  */
1408 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1409 {
1410     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1411     WineWindow* window = (WineWindow*)w;
1412
1413     [window setAlphaValue:alpha];
1414
1415     [pool release];
1416 }
1417
1418 /***********************************************************************
1419  *              macdrv_set_window_color_key
1420  */
1421 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1422                                  CGFloat keyBlue)
1423 {
1424     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1425     WineWindow* window = (WineWindow*)w;
1426
1427     OnMainThread(^{
1428         window.colorKeyed       = TRUE;
1429         window.colorKeyRed      = keyRed;
1430         window.colorKeyGreen    = keyGreen;
1431         window.colorKeyBlue     = keyBlue;
1432         [window checkTransparency];
1433     });
1434
1435     [pool release];
1436 }
1437
1438 /***********************************************************************
1439  *              macdrv_clear_window_color_key
1440  */
1441 void macdrv_clear_window_color_key(macdrv_window w)
1442 {
1443     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1444     WineWindow* window = (WineWindow*)w;
1445
1446     OnMainThread(^{
1447         window.colorKeyed = FALSE;
1448         [window checkTransparency];
1449     });
1450
1451     [pool release];
1452 }
1453
1454 /***********************************************************************
1455  *              macdrv_window_use_per_pixel_alpha
1456  */
1457 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1458 {
1459     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1460     WineWindow* window = (WineWindow*)w;
1461
1462     OnMainThread(^{
1463         window.usePerPixelAlpha = use_per_pixel_alpha;
1464         [window checkTransparency];
1465     });
1466
1467     [pool release];
1468 }
1469
1470 /***********************************************************************
1471  *              macdrv_give_cocoa_window_focus
1472  *
1473  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
1474  * orders it front and, if its frame was not within the desktop bounds,
1475  * Cocoa will typically move it on-screen.
1476  */
1477 void macdrv_give_cocoa_window_focus(macdrv_window w)
1478 {
1479     WineWindow* window = (WineWindow*)w;
1480
1481     OnMainThread(^{
1482         [window makeFocused];
1483     });
1484 }