winemac: Implement a WINDOW_GOT_FOCUS event for when Cocoa tries to focus a window.
[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 "cocoa_window.h"
22
23 #include "macdrv_cocoa.h"
24 #import "cocoa_app.h"
25 #import "cocoa_event.h"
26
27
28 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
29 {
30     NSUInteger style_mask;
31
32     if (wf->title_bar)
33     {
34         style_mask = NSTitledWindowMask;
35         if (wf->close_button) style_mask |= NSClosableWindowMask;
36         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
37         if (wf->resizable) style_mask |= NSResizableWindowMask;
38         if (wf->utility) style_mask |= NSUtilityWindowMask;
39     }
40     else style_mask = NSBorderlessWindowMask;
41
42     return style_mask;
43 }
44
45
46 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
47 {
48     NSScreen* screen;
49     for (screen in screens)
50     {
51         if (NSIntersectsRect(frame, [screen frame]))
52             return TRUE;
53     }
54     return FALSE;
55 }
56
57
58 @interface WineContentView : NSView
59 @end
60
61
62 @interface WineWindow ()
63
64 @property (nonatomic) BOOL disabled;
65 @property (nonatomic) BOOL noActivate;
66 @property (nonatomic) BOOL floating;
67 @property (retain, nonatomic) NSWindow* latentParentWindow;
68
69 @property (nonatomic) void* hwnd;
70 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
71
72 @property (nonatomic) void* surface;
73 @property (nonatomic) pthread_mutex_t* surface_mutex;
74
75 @property (copy, nonatomic) NSBezierPath* shape;
76 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
77 @property (readonly, nonatomic) BOOL needsTransparency;
78
79 @property (nonatomic) BOOL colorKeyed;
80 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
81 @property (nonatomic) BOOL usePerPixelAlpha;
82
83     + (void) flipRect:(NSRect*)rect;
84
85 @end
86
87
88 @implementation WineContentView
89
90     - (BOOL) isFlipped
91     {
92         return YES;
93     }
94
95     - (void) drawRect:(NSRect)rect
96     {
97         WineWindow* window = (WineWindow*)[self window];
98
99         if (window.surface && window.surface_mutex &&
100             !pthread_mutex_lock(window.surface_mutex))
101         {
102             const CGRect* rects;
103             int count;
104
105             if (!get_surface_region_rects(window.surface, &rects, &count) || count)
106             {
107                 CGRect imageRect;
108                 CGImageRef image;
109
110                 imageRect = NSRectToCGRect(rect);
111                 image = create_surface_image(window.surface, &imageRect, FALSE);
112
113                 if (image)
114                 {
115                     CGContextRef context;
116
117                     if (rects && count)
118                     {
119                         NSBezierPath* surfaceClip = [NSBezierPath bezierPath];
120                         int i;
121                         for (i = 0; i < count; i++)
122                             [surfaceClip appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
123                         [surfaceClip addClip];
124                     }
125
126                     [window.shape addClip];
127
128                     if (window.colorKeyed)
129                     {
130                         CGImageRef maskedImage;
131                         CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
132                                                  window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
133                                                  window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
134                         maskedImage = CGImageCreateWithMaskingColors(image, components);
135                         if (maskedImage)
136                         {
137                             CGImageRelease(image);
138                             image = maskedImage;
139                         }
140                     }
141
142                     context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
143                     CGContextSetBlendMode(context, kCGBlendModeCopy);
144                     CGContextDrawImage(context, imageRect, image);
145
146                     CGImageRelease(image);
147
148                     if (window.shapeChangedSinceLastDraw || window.colorKeyed ||
149                         window.usePerPixelAlpha)
150                     {
151                         window.shapeChangedSinceLastDraw = FALSE;
152                         [window invalidateShadow];
153                     }
154                 }
155             }
156
157             pthread_mutex_unlock(window.surface_mutex);
158         }
159     }
160
161     /* By default, NSView will swallow right-clicks in an attempt to support contextual
162        menus.  We need to bypass that and allow the event to make it to the window. */
163     - (void) rightMouseDown:(NSEvent*)theEvent
164     {
165         [[self window] rightMouseDown:theEvent];
166     }
167
168 @end
169
170
171 @implementation WineWindow
172
173     @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
174     @synthesize surface, surface_mutex;
175     @synthesize shape, shapeChangedSinceLastDraw;
176     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
177     @synthesize usePerPixelAlpha;
178
179     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
180                                  windowFrame:(NSRect)window_frame
181                                         hwnd:(void*)hwnd
182                                        queue:(WineEventQueue*)queue
183     {
184         WineWindow* window;
185         WineContentView* contentView;
186
187         [self flipRect:&window_frame];
188
189         window = [[[self alloc] initWithContentRect:window_frame
190                                           styleMask:style_mask_for_features(wf)
191                                             backing:NSBackingStoreBuffered
192                                               defer:YES] autorelease];
193
194         if (!window) return nil;
195         window->normalStyleMask = [window styleMask];
196
197         /* Standardize windows to eliminate differences between titled and
198            borderless windows and between NSWindow and NSPanel. */
199         [window setHidesOnDeactivate:NO];
200         [window setReleasedWhenClosed:NO];
201
202         [window disableCursorRects];
203         [window setShowsResizeIndicator:NO];
204         [window setHasShadow:wf->shadow];
205         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
206         [window setDelegate:window];
207         window.hwnd = hwnd;
208         window.queue = queue;
209
210         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
211         if (!contentView)
212             return nil;
213         [contentView setAutoresizesSubviews:NO];
214
215         [window setContentView:contentView];
216
217         /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
218            event.  The back end will ignore it if nothing actually changed. */
219         [window windowDidResize:nil];
220
221         return window;
222     }
223
224     - (void) dealloc
225     {
226         [queue release];
227         [latentParentWindow release];
228         [shape release];
229         [super dealloc];
230     }
231
232     + (void) flipRect:(NSRect*)rect
233     {
234         rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
235     }
236
237     - (void) adjustFeaturesForState
238     {
239         NSUInteger style = normalStyleMask;
240
241         if (self.disabled)
242             style &= ~NSResizableWindowMask;
243         if (style != [self styleMask])
244             [self setStyleMask:style];
245
246         if (style & NSClosableWindowMask)
247             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
248         if (style & NSMiniaturizableWindowMask)
249             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
250     }
251
252     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
253     {
254         normalStyleMask = style_mask_for_features(wf);
255         [self adjustFeaturesForState];
256         [self setHasShadow:wf->shadow];
257     }
258
259     - (void) setMacDrvState:(const struct macdrv_window_state*)state
260     {
261         NSInteger level;
262         NSWindowCollectionBehavior behavior;
263
264         self.disabled = state->disabled;
265         self.noActivate = state->no_activate;
266
267         self.floating = state->floating;
268         level = state->floating ? NSFloatingWindowLevel : NSNormalWindowLevel;
269         if (level != [self level])
270             [self setLevel:level];
271
272         behavior = NSWindowCollectionBehaviorDefault;
273         if (state->excluded_by_expose)
274             behavior |= NSWindowCollectionBehaviorTransient;
275         else
276             behavior |= NSWindowCollectionBehaviorManaged;
277         if (state->excluded_by_cycle)
278         {
279             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
280             if ([self isVisible])
281                 [NSApp removeWindowsItem:self];
282         }
283         else
284         {
285             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
286             if ([self isVisible])
287                 [NSApp addWindowsItem:self title:[self title] filename:NO];
288         }
289         [self setCollectionBehavior:behavior];
290     }
291
292     /* Returns whether or not the window was ordered in, which depends on if
293        its frame intersects any screen. */
294     - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
295     {
296         BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
297         if (on_screen)
298         {
299             [NSApp transformProcessToForeground];
300
301             if (prev)
302                 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
303             else
304                 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
305             if (latentParentWindow)
306             {
307                 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
308                 self.latentParentWindow = nil;
309             }
310
311             /* Cocoa may adjust the frame when the window is ordered onto the screen.
312                Generate a frame-changed event just in case.  The back end will ignore
313                it if nothing actually changed. */
314             [self windowDidResize:nil];
315
316             if (![self isExcludedFromWindowsMenu])
317                 [NSApp addWindowsItem:self title:[self title] filename:NO];
318         }
319
320         return on_screen;
321     }
322
323     - (void) doOrderOut
324     {
325         self.latentParentWindow = [self parentWindow];
326         [latentParentWindow removeChildWindow:self];
327         [self orderOut:nil];
328         [NSApp removeWindowsItem:self];
329     }
330
331     - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
332     {
333         NSArray* screens = [NSScreen screens];
334         BOOL on_screen = [self isVisible];
335         NSRect frame, oldFrame;
336
337         if (![screens count]) return on_screen;
338
339         /* Origin is (left, top) in a top-down space.  Need to convert it to
340            (left, bottom) in a bottom-up space. */
341         [[self class] flipRect:&contentRect];
342
343         if (on_screen)
344         {
345             on_screen = frame_intersects_screens(contentRect, screens);
346             if (!on_screen)
347                 [self doOrderOut];
348         }
349
350         oldFrame = [self frame];
351         frame = [self frameRectForContentRect:contentRect];
352         if (!NSEqualRects(frame, oldFrame))
353         {
354             if (NSEqualSizes(frame.size, oldFrame.size))
355                 [self setFrameOrigin:frame.origin];
356             else
357                 [self setFrame:frame display:YES];
358         }
359
360         /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
361            event.  The back end will ignore it if nothing actually changed. */
362         [self windowDidResize:nil];
363
364         return on_screen;
365     }
366
367     - (void) setMacDrvParentWindow:(WineWindow*)parent
368     {
369         if ([self parentWindow] != parent)
370         {
371             [[self parentWindow] removeChildWindow:self];
372             self.latentParentWindow = nil;
373             if ([self isVisible] && parent)
374                 [parent addChildWindow:self ordered:NSWindowAbove];
375             else
376                 self.latentParentWindow = parent;
377         }
378     }
379
380     - (void) setDisabled:(BOOL)newValue
381     {
382         if (disabled != newValue)
383         {
384             disabled = newValue;
385             [self adjustFeaturesForState];
386         }
387     }
388
389     - (BOOL) needsTransparency
390     {
391         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
392     }
393
394     - (void) checkTransparency
395     {
396         if (![self isOpaque] && !self.needsTransparency)
397         {
398             [self setBackgroundColor:[NSColor windowBackgroundColor]];
399             [self setOpaque:YES];
400         }
401         else if ([self isOpaque] && self.needsTransparency)
402         {
403             [self setBackgroundColor:[NSColor clearColor]];
404             [self setOpaque:NO];
405         }
406     }
407
408     - (void) setShape:(NSBezierPath*)newShape
409     {
410         if (shape == newShape) return;
411         if (shape && newShape && [shape isEqual:newShape]) return;
412
413         if (shape)
414         {
415             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
416             [shape release];
417         }
418         if (newShape)
419             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
420
421         shape = [newShape copy];
422         self.shapeChangedSinceLastDraw = TRUE;
423
424         [self checkTransparency];
425     }
426
427     - (void) postMouseButtonEvent:(NSEvent *)theEvent pressed:(int)pressed
428     {
429         CGPoint pt = CGEventGetLocation([theEvent CGEvent]);
430         macdrv_event event;
431
432         event.type = MOUSE_BUTTON;
433         event.window = (macdrv_window)[self retain];
434         event.mouse_button.button = [theEvent buttonNumber];
435         event.mouse_button.pressed = pressed;
436         event.mouse_button.x = pt.x;
437         event.mouse_button.y = pt.y;
438         event.mouse_button.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
439
440         [queue postEvent:&event];
441     }
442
443     - (void) makeFocused
444     {
445         NSArray* screens;
446
447         [NSApp transformProcessToForeground];
448
449         /* If a borderless window is offscreen, orderFront: won't move
450            it onscreen like it would for a titled window.  Do that ourselves. */
451         screens = [NSScreen screens];
452         if (!([self styleMask] & NSTitledWindowMask) && ![self isVisible] &&
453             !frame_intersects_screens([self frame], screens))
454         {
455             NSScreen* primaryScreen = [screens objectAtIndex:0];
456             NSRect frame = [primaryScreen frame];
457             [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
458             frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
459             [self setFrame:frame display:YES];
460         }
461
462         [self orderFront:nil];
463         causing_becomeKeyWindow = TRUE;
464         [self makeKeyWindow];
465         causing_becomeKeyWindow = FALSE;
466         if (latentParentWindow)
467         {
468             [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
469             self.latentParentWindow = nil;
470         }
471         if (![self isExcludedFromWindowsMenu])
472             [NSApp addWindowsItem:self title:[self title] filename:NO];
473
474         /* Cocoa may adjust the frame when the window is ordered onto the screen.
475            Generate a frame-changed event just in case.  The back end will ignore
476            it if nothing actually changed. */
477         [self windowDidResize:nil];
478     }
479
480
481     /*
482      * ---------- NSWindow method overrides ----------
483      */
484     - (BOOL) canBecomeKeyWindow
485     {
486         if (causing_becomeKeyWindow) return YES;
487         if (self.disabled || self.noActivate) return NO;
488         return [self isKeyWindow];
489     }
490
491     - (BOOL) canBecomeMainWindow
492     {
493         return [self canBecomeKeyWindow];
494     }
495
496     - (BOOL) isExcludedFromWindowsMenu
497     {
498         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
499     }
500
501     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
502     {
503         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
504             return [self isKeyWindow] || (!self.disabled && !self.noActivate);
505         return [super validateMenuItem:menuItem];
506     }
507
508     /* We don't call this.  It's the action method of the items in the Window menu. */
509     - (void) makeKeyAndOrderFront:(id)sender
510     {
511         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
512             [NSApp windowGotFocus:self];
513     }
514
515     - (void) sendEvent:(NSEvent*)event
516     {
517         if ([event type] == NSLeftMouseDown)
518         {
519             /* Since our windows generally claim they can't be made key, clicks
520                in their title bars are swallowed by the theme frame stuff.  So,
521                we hook directly into the event stream and assume that any click
522                in the window will activate it, if Wine and the Win32 program
523                accept. */
524             if (![self isKeyWindow] && !self.disabled && !self.noActivate)
525                 [NSApp windowGotFocus:self];
526         }
527
528         [super sendEvent:event];
529     }
530
531
532     /*
533      * ---------- NSResponder method overrides ----------
534      */
535     - (void) mouseDown:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:1]; }
536     - (void) rightMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
537     - (void) otherMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
538
539     - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:0]; }
540     - (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
541     - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
542
543
544     /*
545      * ---------- NSWindowDelegate methods ----------
546      */
547     - (void)windowDidBecomeKey:(NSNotification *)notification
548     {
549         if (causing_becomeKeyWindow) return;
550
551         [NSApp windowGotFocus:self];
552     }
553
554     - (void)windowDidMove:(NSNotification *)notification
555     {
556         [self windowDidResize:notification];
557     }
558
559     - (void)windowDidResize:(NSNotification *)notification
560     {
561         macdrv_event event;
562         NSRect frame = [self contentRectForFrameRect:[self frame]];
563
564         [[self class] flipRect:&frame];
565
566         /* Coalesce events by discarding any previous ones still in the queue. */
567         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
568                                forWindow:self];
569
570         event.type = WINDOW_FRAME_CHANGED;
571         event.window = (macdrv_window)[self retain];
572         event.window_frame_changed.frame = NSRectToCGRect(frame);
573         [queue postEvent:&event];
574     }
575
576     - (BOOL)windowShouldClose:(id)sender
577     {
578         macdrv_event event;
579         event.type = WINDOW_CLOSE_REQUESTED;
580         event.window = (macdrv_window)[self retain];
581         [queue postEvent:&event];
582         return NO;
583     }
584
585 @end
586
587
588 /***********************************************************************
589  *              macdrv_create_cocoa_window
590  *
591  * Create a Cocoa window with the given content frame and features (e.g.
592  * title bar, close box, etc.).
593  */
594 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
595         CGRect frame, void* hwnd, macdrv_event_queue queue)
596 {
597     __block WineWindow* window;
598
599     OnMainThread(^{
600         window = [[WineWindow createWindowWithFeatures:wf
601                                            windowFrame:NSRectFromCGRect(frame)
602                                                   hwnd:hwnd
603                                                  queue:(WineEventQueue*)queue] retain];
604     });
605
606     return (macdrv_window)window;
607 }
608
609 /***********************************************************************
610  *              macdrv_destroy_cocoa_window
611  *
612  * Destroy a Cocoa window.
613  */
614 void macdrv_destroy_cocoa_window(macdrv_window w)
615 {
616     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
617     WineWindow* window = (WineWindow*)w;
618
619     [window.queue discardEventsMatchingMask:-1 forWindow:window];
620     [window close];
621     [window release];
622
623     [pool release];
624 }
625
626 /***********************************************************************
627  *              macdrv_get_window_hwnd
628  *
629  * Get the hwnd that was set for the window at creation.
630  */
631 void* macdrv_get_window_hwnd(macdrv_window w)
632 {
633     WineWindow* window = (WineWindow*)w;
634     return window.hwnd;
635 }
636
637 /***********************************************************************
638  *              macdrv_set_cocoa_window_features
639  *
640  * Update a Cocoa window's features.
641  */
642 void macdrv_set_cocoa_window_features(macdrv_window w,
643         const struct macdrv_window_features* wf)
644 {
645     WineWindow* window = (WineWindow*)w;
646
647     OnMainThread(^{
648         [window setWindowFeatures:wf];
649     });
650 }
651
652 /***********************************************************************
653  *              macdrv_set_cocoa_window_state
654  *
655  * Update a Cocoa window's state.
656  */
657 void macdrv_set_cocoa_window_state(macdrv_window w,
658         const struct macdrv_window_state* state)
659 {
660     WineWindow* window = (WineWindow*)w;
661
662     OnMainThread(^{
663         [window setMacDrvState:state];
664     });
665 }
666
667 /***********************************************************************
668  *              macdrv_set_cocoa_window_title
669  *
670  * Set a Cocoa window's title.
671  */
672 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
673         size_t length)
674 {
675     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
676     WineWindow* window = (WineWindow*)w;
677     NSString* titleString;
678
679     if (title)
680         titleString = [NSString stringWithCharacters:title length:length];
681     else
682         titleString = @"";
683     OnMainThreadAsync(^{
684         [window setTitle:titleString];
685         if ([window isVisible] && ![window isExcludedFromWindowsMenu])
686             [NSApp changeWindowsItem:window title:titleString filename:NO];
687     });
688
689     [pool release];
690 }
691
692 /***********************************************************************
693  *              macdrv_order_cocoa_window
694  *
695  * Reorder a Cocoa window relative to other windows.  If prev is
696  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
697  * it is ordered above that window.  Otherwise, it is ordered to the
698  * front.
699  *
700  * Returns true if the window has actually been ordered onto the screen
701  * (i.e. if its frame intersects with a screen).  Otherwise, false.
702  */
703 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
704         macdrv_window next)
705 {
706     WineWindow* window = (WineWindow*)w;
707     __block BOOL on_screen;
708
709     OnMainThread(^{
710         on_screen = [window orderBelow:(WineWindow*)prev
711                                orAbove:(WineWindow*)next];
712     });
713
714     return on_screen;
715 }
716
717 /***********************************************************************
718  *              macdrv_hide_cocoa_window
719  *
720  * Hides a Cocoa window.
721  */
722 void macdrv_hide_cocoa_window(macdrv_window w)
723 {
724     WineWindow* window = (WineWindow*)w;
725
726     OnMainThread(^{
727         [window doOrderOut];
728     });
729 }
730
731 /***********************************************************************
732  *              macdrv_set_cocoa_window_frame
733  *
734  * Move a Cocoa window.  If the window has been moved out of the bounds
735  * of the desktop, it is ordered out.  (This routine won't ever order a
736  * window in, though.)
737  *
738  * Returns true if the window is on screen; false otherwise.
739  */
740 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
741 {
742     WineWindow* window = (WineWindow*)w;
743     __block BOOL on_screen;
744
745     OnMainThread(^{
746         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
747     });
748
749     return on_screen;
750 }
751
752 /***********************************************************************
753  *              macdrv_get_cocoa_window_frame
754  *
755  * Gets the frame of a Cocoa window.
756  */
757 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
758 {
759     WineWindow* window = (WineWindow*)w;
760
761     OnMainThread(^{
762         NSRect frame;
763
764         frame = [window contentRectForFrameRect:[window frame]];
765         [[window class] flipRect:&frame];
766         *out_frame = NSRectToCGRect(frame);
767     });
768 }
769
770 /***********************************************************************
771  *              macdrv_set_cocoa_parent_window
772  *
773  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
774  * the parent window.
775  */
776 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
777 {
778     WineWindow* window = (WineWindow*)w;
779
780     OnMainThread(^{
781         [window setMacDrvParentWindow:(WineWindow*)parent];
782     });
783 }
784
785 /***********************************************************************
786  *              macdrv_set_window_surface
787  */
788 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
789 {
790     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
791     WineWindow* window = (WineWindow*)w;
792
793     OnMainThread(^{
794         window.surface = surface;
795         window.surface_mutex = mutex;
796     });
797
798     [pool release];
799 }
800
801 /***********************************************************************
802  *              macdrv_window_needs_display
803  *
804  * Mark a window as needing display in a specified rect (in non-client
805  * area coordinates).
806  */
807 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
808 {
809     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
810     WineWindow* window = (WineWindow*)w;
811
812     OnMainThreadAsync(^{
813         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
814     });
815
816     [pool release];
817 }
818
819 /***********************************************************************
820  *              macdrv_set_window_shape
821  *
822  * Sets the shape of a Cocoa window from an array of rectangles.  If
823  * rects is NULL, resets the window's shape to its frame.
824  */
825 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
826 {
827     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
828     WineWindow* window = (WineWindow*)w;
829
830     OnMainThread(^{
831         if (!rects || !count)
832             window.shape = nil;
833         else
834         {
835             NSBezierPath* path;
836             unsigned int i;
837
838             path = [NSBezierPath bezierPath];
839             for (i = 0; i < count; i++)
840                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
841             window.shape = path;
842         }
843     });
844
845     [pool release];
846 }
847
848 /***********************************************************************
849  *              macdrv_set_window_alpha
850  */
851 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
852 {
853     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
854     WineWindow* window = (WineWindow*)w;
855
856     [window setAlphaValue:alpha];
857
858     [pool release];
859 }
860
861 /***********************************************************************
862  *              macdrv_set_window_color_key
863  */
864 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
865                                  CGFloat keyBlue)
866 {
867     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
868     WineWindow* window = (WineWindow*)w;
869
870     OnMainThread(^{
871         window.colorKeyed       = TRUE;
872         window.colorKeyRed      = keyRed;
873         window.colorKeyGreen    = keyGreen;
874         window.colorKeyBlue     = keyBlue;
875         [window checkTransparency];
876     });
877
878     [pool release];
879 }
880
881 /***********************************************************************
882  *              macdrv_clear_window_color_key
883  */
884 void macdrv_clear_window_color_key(macdrv_window w)
885 {
886     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
887     WineWindow* window = (WineWindow*)w;
888
889     OnMainThread(^{
890         window.colorKeyed = FALSE;
891         [window checkTransparency];
892     });
893
894     [pool release];
895 }
896
897 /***********************************************************************
898  *              macdrv_window_use_per_pixel_alpha
899  */
900 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
901 {
902     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
903     WineWindow* window = (WineWindow*)w;
904
905     OnMainThread(^{
906         window.usePerPixelAlpha = use_per_pixel_alpha;
907         [window checkTransparency];
908     });
909
910     [pool release];
911 }
912
913 /***********************************************************************
914  *              macdrv_give_cocoa_window_focus
915  *
916  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
917  * orders it front and, if its frame was not within the desktop bounds,
918  * Cocoa will typically move it on-screen.
919  */
920 void macdrv_give_cocoa_window_focus(macdrv_window w)
921 {
922     WineWindow* window = (WineWindow*)w;
923
924     OnMainThread(^{
925         [window makeFocused];
926     });
927 }