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