winemac: Implement WINDOW_FRAME_CHANGED event to tell Wine when window is moved or...
[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, 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 @end
162
163
164 @implementation WineWindow
165
166     @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
167     @synthesize surface, surface_mutex;
168     @synthesize shape, shapeChangedSinceLastDraw;
169     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
170     @synthesize usePerPixelAlpha;
171
172     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
173                                  windowFrame:(NSRect)window_frame
174                                         hwnd:(void*)hwnd
175                                        queue:(WineEventQueue*)queue
176     {
177         WineWindow* window;
178         WineContentView* contentView;
179
180         [self flipRect:&window_frame];
181
182         window = [[[self alloc] initWithContentRect:window_frame
183                                           styleMask:style_mask_for_features(wf)
184                                             backing:NSBackingStoreBuffered
185                                               defer:YES] autorelease];
186
187         if (!window) return nil;
188         window->normalStyleMask = [window styleMask];
189
190         /* Standardize windows to eliminate differences between titled and
191            borderless windows and between NSWindow and NSPanel. */
192         [window setHidesOnDeactivate:NO];
193         [window setReleasedWhenClosed:NO];
194
195         [window disableCursorRects];
196         [window setShowsResizeIndicator:NO];
197         [window setHasShadow:wf->shadow];
198         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
199         [window setDelegate:window];
200         window.hwnd = hwnd;
201         window.queue = queue;
202
203         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
204         if (!contentView)
205             return nil;
206         [contentView setAutoresizesSubviews:NO];
207
208         [window setContentView:contentView];
209
210         /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
211            event.  The back end will ignore it if nothing actually changed. */
212         [window windowDidResize:nil];
213
214         return window;
215     }
216
217     - (void) dealloc
218     {
219         [queue release];
220         [latentParentWindow release];
221         [shape release];
222         [super dealloc];
223     }
224
225     + (void) flipRect:(NSRect*)rect
226     {
227         rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
228     }
229
230     - (void) adjustFeaturesForState
231     {
232         NSUInteger style = normalStyleMask;
233
234         if (self.disabled)
235             style &= ~NSResizableWindowMask;
236         if (style != [self styleMask])
237             [self setStyleMask:style];
238
239         if (style & NSClosableWindowMask)
240             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
241         if (style & NSMiniaturizableWindowMask)
242             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
243     }
244
245     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
246     {
247         normalStyleMask = style_mask_for_features(wf);
248         [self adjustFeaturesForState];
249         [self setHasShadow:wf->shadow];
250     }
251
252     - (void) setMacDrvState:(const struct macdrv_window_state*)state
253     {
254         NSInteger level;
255         NSWindowCollectionBehavior behavior;
256
257         self.disabled = state->disabled;
258         self.noActivate = state->no_activate;
259
260         self.floating = state->floating;
261         level = state->floating ? NSFloatingWindowLevel : NSNormalWindowLevel;
262         if (level != [self level])
263             [self setLevel:level];
264
265         behavior = NSWindowCollectionBehaviorDefault;
266         if (state->excluded_by_expose)
267             behavior |= NSWindowCollectionBehaviorTransient;
268         else
269             behavior |= NSWindowCollectionBehaviorManaged;
270         if (state->excluded_by_cycle)
271         {
272             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
273             if ([self isVisible])
274                 [NSApp removeWindowsItem:self];
275         }
276         else
277         {
278             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
279             if ([self isVisible])
280                 [NSApp addWindowsItem:self title:[self title] filename:NO];
281         }
282         [self setCollectionBehavior:behavior];
283     }
284
285     /* Returns whether or not the window was ordered in, which depends on if
286        its frame intersects any screen. */
287     - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
288     {
289         BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
290         if (on_screen)
291         {
292             [NSApp transformProcessToForeground];
293
294             if (prev)
295                 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
296             else
297                 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
298             if (latentParentWindow)
299             {
300                 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
301                 self.latentParentWindow = nil;
302             }
303
304             /* Cocoa may adjust the frame when the window is ordered onto the screen.
305                Generate a frame-changed event just in case.  The back end will ignore
306                it if nothing actually changed. */
307             [self windowDidResize:nil];
308
309             if (![self isExcludedFromWindowsMenu])
310                 [NSApp addWindowsItem:self title:[self title] filename:NO];
311         }
312
313         return on_screen;
314     }
315
316     - (void) doOrderOut
317     {
318         self.latentParentWindow = [self parentWindow];
319         [latentParentWindow removeChildWindow:self];
320         [self orderOut:nil];
321         [NSApp removeWindowsItem:self];
322     }
323
324     - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
325     {
326         NSArray* screens = [NSScreen screens];
327         BOOL on_screen = [self isVisible];
328         NSRect frame, oldFrame;
329
330         if (![screens count]) return on_screen;
331
332         /* Origin is (left, top) in a top-down space.  Need to convert it to
333            (left, bottom) in a bottom-up space. */
334         [[self class] flipRect:&contentRect];
335
336         if (on_screen)
337         {
338             on_screen = frame_intersects_screens(contentRect, screens);
339             if (!on_screen)
340                 [self doOrderOut];
341         }
342
343         oldFrame = [self frame];
344         frame = [self frameRectForContentRect:contentRect];
345         if (!NSEqualRects(frame, oldFrame))
346         {
347             if (NSEqualSizes(frame.size, oldFrame.size))
348                 [self setFrameOrigin:frame.origin];
349             else
350                 [self setFrame:frame display:YES];
351         }
352
353         /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
354            event.  The back end will ignore it if nothing actually changed. */
355         [self windowDidResize:nil];
356
357         return on_screen;
358     }
359
360     - (void) setMacDrvParentWindow:(WineWindow*)parent
361     {
362         if ([self parentWindow] != parent)
363         {
364             [[self parentWindow] removeChildWindow:self];
365             self.latentParentWindow = nil;
366             if ([self isVisible] && parent)
367                 [parent addChildWindow:self ordered:NSWindowAbove];
368             else
369                 self.latentParentWindow = parent;
370         }
371     }
372
373     - (void) setDisabled:(BOOL)newValue
374     {
375         if (disabled != newValue)
376         {
377             disabled = newValue;
378             [self adjustFeaturesForState];
379         }
380     }
381
382     - (BOOL) needsTransparency
383     {
384         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
385     }
386
387     - (void) checkTransparency
388     {
389         if (![self isOpaque] && !self.needsTransparency)
390         {
391             [self setBackgroundColor:[NSColor windowBackgroundColor]];
392             [self setOpaque:YES];
393         }
394         else if ([self isOpaque] && self.needsTransparency)
395         {
396             [self setBackgroundColor:[NSColor clearColor]];
397             [self setOpaque:NO];
398         }
399     }
400
401     - (void) setShape:(NSBezierPath*)newShape
402     {
403         if (shape == newShape) return;
404         if (shape && newShape && [shape isEqual:newShape]) return;
405
406         if (shape)
407         {
408             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
409             [shape release];
410         }
411         if (newShape)
412             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
413
414         shape = [newShape copy];
415         self.shapeChangedSinceLastDraw = TRUE;
416
417         [self checkTransparency];
418     }
419
420
421     /*
422      * ---------- NSWindow method overrides ----------
423      */
424     - (BOOL) canBecomeKeyWindow
425     {
426         if (self.disabled || self.noActivate) return NO;
427         return YES;
428     }
429
430     - (BOOL) canBecomeMainWindow
431     {
432         return [self canBecomeKeyWindow];
433     }
434
435     - (BOOL) isExcludedFromWindowsMenu
436     {
437         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
438     }
439
440     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
441     {
442         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
443             return [self isKeyWindow] || (!self.disabled && !self.noActivate);
444         return [super validateMenuItem:menuItem];
445     }
446
447
448     /*
449      * ---------- NSWindowDelegate methods ----------
450      */
451     - (void)windowDidMove:(NSNotification *)notification
452     {
453         [self windowDidResize:notification];
454     }
455
456     - (void)windowDidResize:(NSNotification *)notification
457     {
458         macdrv_event event;
459         NSRect frame = [self contentRectForFrameRect:[self frame]];
460
461         [[self class] flipRect:&frame];
462
463         /* Coalesce events by discarding any previous ones still in the queue. */
464         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
465                                forWindow:self];
466
467         event.type = WINDOW_FRAME_CHANGED;
468         event.window = (macdrv_window)[self retain];
469         event.window_frame_changed.frame = NSRectToCGRect(frame);
470         [queue postEvent:&event];
471     }
472
473     - (BOOL)windowShouldClose:(id)sender
474     {
475         macdrv_event event;
476         event.type = WINDOW_CLOSE_REQUESTED;
477         event.window = (macdrv_window)[self retain];
478         [queue postEvent:&event];
479         return NO;
480     }
481
482 @end
483
484
485 /***********************************************************************
486  *              macdrv_create_cocoa_window
487  *
488  * Create a Cocoa window with the given content frame and features (e.g.
489  * title bar, close box, etc.).
490  */
491 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
492         CGRect frame, void* hwnd, macdrv_event_queue queue)
493 {
494     __block WineWindow* window;
495
496     OnMainThread(^{
497         window = [[WineWindow createWindowWithFeatures:wf
498                                            windowFrame:NSRectFromCGRect(frame)
499                                                   hwnd:hwnd
500                                                  queue:(WineEventQueue*)queue] retain];
501     });
502
503     return (macdrv_window)window;
504 }
505
506 /***********************************************************************
507  *              macdrv_destroy_cocoa_window
508  *
509  * Destroy a Cocoa window.
510  */
511 void macdrv_destroy_cocoa_window(macdrv_window w)
512 {
513     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
514     WineWindow* window = (WineWindow*)w;
515
516     [window.queue discardEventsMatchingMask:-1 forWindow:window];
517     [window close];
518     [window release];
519
520     [pool release];
521 }
522
523 /***********************************************************************
524  *              macdrv_get_window_hwnd
525  *
526  * Get the hwnd that was set for the window at creation.
527  */
528 void* macdrv_get_window_hwnd(macdrv_window w)
529 {
530     WineWindow* window = (WineWindow*)w;
531     return window.hwnd;
532 }
533
534 /***********************************************************************
535  *              macdrv_set_cocoa_window_features
536  *
537  * Update a Cocoa window's features.
538  */
539 void macdrv_set_cocoa_window_features(macdrv_window w,
540         const struct macdrv_window_features* wf)
541 {
542     WineWindow* window = (WineWindow*)w;
543
544     OnMainThread(^{
545         [window setWindowFeatures:wf];
546     });
547 }
548
549 /***********************************************************************
550  *              macdrv_set_cocoa_window_state
551  *
552  * Update a Cocoa window's state.
553  */
554 void macdrv_set_cocoa_window_state(macdrv_window w,
555         const struct macdrv_window_state* state)
556 {
557     WineWindow* window = (WineWindow*)w;
558
559     OnMainThread(^{
560         [window setMacDrvState:state];
561     });
562 }
563
564 /***********************************************************************
565  *              macdrv_set_cocoa_window_title
566  *
567  * Set a Cocoa window's title.
568  */
569 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
570         size_t length)
571 {
572     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
573     WineWindow* window = (WineWindow*)w;
574     NSString* titleString;
575
576     if (title)
577         titleString = [NSString stringWithCharacters:title length:length];
578     else
579         titleString = @"";
580     OnMainThreadAsync(^{
581         [window setTitle:titleString];
582         if ([window isVisible] && ![window isExcludedFromWindowsMenu])
583             [NSApp changeWindowsItem:window title:titleString filename:NO];
584     });
585
586     [pool release];
587 }
588
589 /***********************************************************************
590  *              macdrv_order_cocoa_window
591  *
592  * Reorder a Cocoa window relative to other windows.  If prev is
593  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
594  * it is ordered above that window.  Otherwise, it is ordered to the
595  * front.
596  *
597  * Returns true if the window has actually been ordered onto the screen
598  * (i.e. if its frame intersects with a screen).  Otherwise, false.
599  */
600 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
601         macdrv_window next)
602 {
603     WineWindow* window = (WineWindow*)w;
604     __block BOOL on_screen;
605
606     OnMainThread(^{
607         on_screen = [window orderBelow:(WineWindow*)prev
608                                orAbove:(WineWindow*)next];
609     });
610
611     return on_screen;
612 }
613
614 /***********************************************************************
615  *              macdrv_hide_cocoa_window
616  *
617  * Hides a Cocoa window.
618  */
619 void macdrv_hide_cocoa_window(macdrv_window w)
620 {
621     WineWindow* window = (WineWindow*)w;
622
623     OnMainThread(^{
624         [window doOrderOut];
625     });
626 }
627
628 /***********************************************************************
629  *              macdrv_set_cocoa_window_frame
630  *
631  * Move a Cocoa window.  If the window has been moved out of the bounds
632  * of the desktop, it is ordered out.  (This routine won't ever order a
633  * window in, though.)
634  *
635  * Returns true if the window is on screen; false otherwise.
636  */
637 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
638 {
639     WineWindow* window = (WineWindow*)w;
640     __block BOOL on_screen;
641
642     OnMainThread(^{
643         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
644     });
645
646     return on_screen;
647 }
648
649 /***********************************************************************
650  *              macdrv_get_cocoa_window_frame
651  *
652  * Gets the frame of a Cocoa window.
653  */
654 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
655 {
656     WineWindow* window = (WineWindow*)w;
657
658     OnMainThread(^{
659         NSRect frame;
660
661         frame = [window contentRectForFrameRect:[window frame]];
662         [[window class] flipRect:&frame];
663         *out_frame = NSRectToCGRect(frame);
664     });
665 }
666
667 /***********************************************************************
668  *              macdrv_set_cocoa_parent_window
669  *
670  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
671  * the parent window.
672  */
673 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
674 {
675     WineWindow* window = (WineWindow*)w;
676
677     OnMainThread(^{
678         [window setMacDrvParentWindow:(WineWindow*)parent];
679     });
680 }
681
682 /***********************************************************************
683  *              macdrv_set_window_surface
684  */
685 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
686 {
687     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
688     WineWindow* window = (WineWindow*)w;
689
690     OnMainThread(^{
691         window.surface = surface;
692         window.surface_mutex = mutex;
693     });
694
695     [pool release];
696 }
697
698 /***********************************************************************
699  *              macdrv_window_needs_display
700  *
701  * Mark a window as needing display in a specified rect (in non-client
702  * area coordinates).
703  */
704 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
705 {
706     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
707     WineWindow* window = (WineWindow*)w;
708
709     OnMainThreadAsync(^{
710         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
711     });
712
713     [pool release];
714 }
715
716 /***********************************************************************
717  *              macdrv_set_window_shape
718  *
719  * Sets the shape of a Cocoa window from an array of rectangles.  If
720  * rects is NULL, resets the window's shape to its frame.
721  */
722 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
723 {
724     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
725     WineWindow* window = (WineWindow*)w;
726
727     OnMainThread(^{
728         if (!rects || !count)
729             window.shape = nil;
730         else
731         {
732             NSBezierPath* path;
733             unsigned int i;
734
735             path = [NSBezierPath bezierPath];
736             for (i = 0; i < count; i++)
737                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
738             window.shape = path;
739         }
740     });
741
742     [pool release];
743 }
744
745 /***********************************************************************
746  *              macdrv_set_window_alpha
747  */
748 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
749 {
750     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
751     WineWindow* window = (WineWindow*)w;
752
753     [window setAlphaValue:alpha];
754
755     [pool release];
756 }
757
758 /***********************************************************************
759  *              macdrv_set_window_color_key
760  */
761 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
762                                  CGFloat keyBlue)
763 {
764     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
765     WineWindow* window = (WineWindow*)w;
766
767     OnMainThread(^{
768         window.colorKeyed       = TRUE;
769         window.colorKeyRed      = keyRed;
770         window.colorKeyGreen    = keyGreen;
771         window.colorKeyBlue     = keyBlue;
772         [window checkTransparency];
773     });
774
775     [pool release];
776 }
777
778 /***********************************************************************
779  *              macdrv_clear_window_color_key
780  */
781 void macdrv_clear_window_color_key(macdrv_window w)
782 {
783     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
784     WineWindow* window = (WineWindow*)w;
785
786     OnMainThread(^{
787         window.colorKeyed = FALSE;
788         [window checkTransparency];
789     });
790
791     [pool release];
792 }
793
794 /***********************************************************************
795  *              macdrv_window_use_per_pixel_alpha
796  */
797 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
798 {
799     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
800     WineWindow* window = (WineWindow*)w;
801
802     OnMainThread(^{
803         window.usePerPixelAlpha = use_per_pixel_alpha;
804         [window checkTransparency];
805     });
806
807     [pool release];
808 }