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