wbemprox: Return an empty object if the path is NULL or empty.
[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)windowDidResignKey:(NSNotification *)notification
560     {
561         macdrv_event event;
562
563         if (causing_becomeKeyWindow) return;
564
565         event.type = WINDOW_LOST_FOCUS;
566         event.window = (macdrv_window)[self retain];
567         [queue postEvent:&event];
568     }
569
570     - (void)windowDidResize:(NSNotification *)notification
571     {
572         macdrv_event event;
573         NSRect frame = [self contentRectForFrameRect:[self frame]];
574
575         [[self class] flipRect:&frame];
576
577         /* Coalesce events by discarding any previous ones still in the queue. */
578         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
579                                forWindow:self];
580
581         event.type = WINDOW_FRAME_CHANGED;
582         event.window = (macdrv_window)[self retain];
583         event.window_frame_changed.frame = NSRectToCGRect(frame);
584         [queue postEvent:&event];
585     }
586
587     - (BOOL)windowShouldClose:(id)sender
588     {
589         macdrv_event event;
590         event.type = WINDOW_CLOSE_REQUESTED;
591         event.window = (macdrv_window)[self retain];
592         [queue postEvent:&event];
593         return NO;
594     }
595
596 @end
597
598
599 /***********************************************************************
600  *              macdrv_create_cocoa_window
601  *
602  * Create a Cocoa window with the given content frame and features (e.g.
603  * title bar, close box, etc.).
604  */
605 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
606         CGRect frame, void* hwnd, macdrv_event_queue queue)
607 {
608     __block WineWindow* window;
609
610     OnMainThread(^{
611         window = [[WineWindow createWindowWithFeatures:wf
612                                            windowFrame:NSRectFromCGRect(frame)
613                                                   hwnd:hwnd
614                                                  queue:(WineEventQueue*)queue] retain];
615     });
616
617     return (macdrv_window)window;
618 }
619
620 /***********************************************************************
621  *              macdrv_destroy_cocoa_window
622  *
623  * Destroy a Cocoa window.
624  */
625 void macdrv_destroy_cocoa_window(macdrv_window w)
626 {
627     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
628     WineWindow* window = (WineWindow*)w;
629
630     [window.queue discardEventsMatchingMask:-1 forWindow:window];
631     [window close];
632     [window release];
633
634     [pool release];
635 }
636
637 /***********************************************************************
638  *              macdrv_get_window_hwnd
639  *
640  * Get the hwnd that was set for the window at creation.
641  */
642 void* macdrv_get_window_hwnd(macdrv_window w)
643 {
644     WineWindow* window = (WineWindow*)w;
645     return window.hwnd;
646 }
647
648 /***********************************************************************
649  *              macdrv_set_cocoa_window_features
650  *
651  * Update a Cocoa window's features.
652  */
653 void macdrv_set_cocoa_window_features(macdrv_window w,
654         const struct macdrv_window_features* wf)
655 {
656     WineWindow* window = (WineWindow*)w;
657
658     OnMainThread(^{
659         [window setWindowFeatures:wf];
660     });
661 }
662
663 /***********************************************************************
664  *              macdrv_set_cocoa_window_state
665  *
666  * Update a Cocoa window's state.
667  */
668 void macdrv_set_cocoa_window_state(macdrv_window w,
669         const struct macdrv_window_state* state)
670 {
671     WineWindow* window = (WineWindow*)w;
672
673     OnMainThread(^{
674         [window setMacDrvState:state];
675     });
676 }
677
678 /***********************************************************************
679  *              macdrv_set_cocoa_window_title
680  *
681  * Set a Cocoa window's title.
682  */
683 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
684         size_t length)
685 {
686     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
687     WineWindow* window = (WineWindow*)w;
688     NSString* titleString;
689
690     if (title)
691         titleString = [NSString stringWithCharacters:title length:length];
692     else
693         titleString = @"";
694     OnMainThreadAsync(^{
695         [window setTitle:titleString];
696         if ([window isVisible] && ![window isExcludedFromWindowsMenu])
697             [NSApp changeWindowsItem:window title:titleString filename:NO];
698     });
699
700     [pool release];
701 }
702
703 /***********************************************************************
704  *              macdrv_order_cocoa_window
705  *
706  * Reorder a Cocoa window relative to other windows.  If prev is
707  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
708  * it is ordered above that window.  Otherwise, it is ordered to the
709  * front.
710  *
711  * Returns true if the window has actually been ordered onto the screen
712  * (i.e. if its frame intersects with a screen).  Otherwise, false.
713  */
714 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
715         macdrv_window next)
716 {
717     WineWindow* window = (WineWindow*)w;
718     __block BOOL on_screen;
719
720     OnMainThread(^{
721         on_screen = [window orderBelow:(WineWindow*)prev
722                                orAbove:(WineWindow*)next];
723     });
724
725     return on_screen;
726 }
727
728 /***********************************************************************
729  *              macdrv_hide_cocoa_window
730  *
731  * Hides a Cocoa window.
732  */
733 void macdrv_hide_cocoa_window(macdrv_window w)
734 {
735     WineWindow* window = (WineWindow*)w;
736
737     OnMainThread(^{
738         [window doOrderOut];
739     });
740 }
741
742 /***********************************************************************
743  *              macdrv_set_cocoa_window_frame
744  *
745  * Move a Cocoa window.  If the window has been moved out of the bounds
746  * of the desktop, it is ordered out.  (This routine won't ever order a
747  * window in, though.)
748  *
749  * Returns true if the window is on screen; false otherwise.
750  */
751 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
752 {
753     WineWindow* window = (WineWindow*)w;
754     __block BOOL on_screen;
755
756     OnMainThread(^{
757         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
758     });
759
760     return on_screen;
761 }
762
763 /***********************************************************************
764  *              macdrv_get_cocoa_window_frame
765  *
766  * Gets the frame of a Cocoa window.
767  */
768 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
769 {
770     WineWindow* window = (WineWindow*)w;
771
772     OnMainThread(^{
773         NSRect frame;
774
775         frame = [window contentRectForFrameRect:[window frame]];
776         [[window class] flipRect:&frame];
777         *out_frame = NSRectToCGRect(frame);
778     });
779 }
780
781 /***********************************************************************
782  *              macdrv_set_cocoa_parent_window
783  *
784  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
785  * the parent window.
786  */
787 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
788 {
789     WineWindow* window = (WineWindow*)w;
790
791     OnMainThread(^{
792         [window setMacDrvParentWindow:(WineWindow*)parent];
793     });
794 }
795
796 /***********************************************************************
797  *              macdrv_set_window_surface
798  */
799 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
800 {
801     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
802     WineWindow* window = (WineWindow*)w;
803
804     OnMainThread(^{
805         window.surface = surface;
806         window.surface_mutex = mutex;
807     });
808
809     [pool release];
810 }
811
812 /***********************************************************************
813  *              macdrv_window_needs_display
814  *
815  * Mark a window as needing display in a specified rect (in non-client
816  * area coordinates).
817  */
818 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
819 {
820     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
821     WineWindow* window = (WineWindow*)w;
822
823     OnMainThreadAsync(^{
824         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
825     });
826
827     [pool release];
828 }
829
830 /***********************************************************************
831  *              macdrv_set_window_shape
832  *
833  * Sets the shape of a Cocoa window from an array of rectangles.  If
834  * rects is NULL, resets the window's shape to its frame.
835  */
836 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
837 {
838     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
839     WineWindow* window = (WineWindow*)w;
840
841     OnMainThread(^{
842         if (!rects || !count)
843             window.shape = nil;
844         else
845         {
846             NSBezierPath* path;
847             unsigned int i;
848
849             path = [NSBezierPath bezierPath];
850             for (i = 0; i < count; i++)
851                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
852             window.shape = path;
853         }
854     });
855
856     [pool release];
857 }
858
859 /***********************************************************************
860  *              macdrv_set_window_alpha
861  */
862 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
863 {
864     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
865     WineWindow* window = (WineWindow*)w;
866
867     [window setAlphaValue:alpha];
868
869     [pool release];
870 }
871
872 /***********************************************************************
873  *              macdrv_set_window_color_key
874  */
875 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
876                                  CGFloat keyBlue)
877 {
878     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
879     WineWindow* window = (WineWindow*)w;
880
881     OnMainThread(^{
882         window.colorKeyed       = TRUE;
883         window.colorKeyRed      = keyRed;
884         window.colorKeyGreen    = keyGreen;
885         window.colorKeyBlue     = keyBlue;
886         [window checkTransparency];
887     });
888
889     [pool release];
890 }
891
892 /***********************************************************************
893  *              macdrv_clear_window_color_key
894  */
895 void macdrv_clear_window_color_key(macdrv_window w)
896 {
897     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
898     WineWindow* window = (WineWindow*)w;
899
900     OnMainThread(^{
901         window.colorKeyed = FALSE;
902         [window checkTransparency];
903     });
904
905     [pool release];
906 }
907
908 /***********************************************************************
909  *              macdrv_window_use_per_pixel_alpha
910  */
911 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
912 {
913     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
914     WineWindow* window = (WineWindow*)w;
915
916     OnMainThread(^{
917         window.usePerPixelAlpha = use_per_pixel_alpha;
918         [window checkTransparency];
919     });
920
921     [pool release];
922 }
923
924 /***********************************************************************
925  *              macdrv_give_cocoa_window_focus
926  *
927  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
928  * orders it front and, if its frame was not within the desktop bounds,
929  * Cocoa will typically move it on-screen.
930  */
931 void macdrv_give_cocoa_window_focus(macdrv_window w)
932 {
933     WineWindow* window = (WineWindow*)w;
934
935     OnMainThread(^{
936         [window makeFocused];
937     });
938 }