winemac: Track drawn surface region to reduce black flicker for new or resized windows.
[wine.git] / dlls / winemac.drv / cocoa_window.m
blob1d5c551c9d9d2734848702b41ac4f54923deb789
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"
28 #import "cocoa_opengl.h"
31 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
32 enum {
33     kVK_RightCommand              = 0x36, /* Invented for Wine; was unused */
37 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
39     NSUInteger style_mask;
41     if (wf->title_bar)
42     {
43         style_mask = NSTitledWindowMask;
44         if (wf->close_button) style_mask |= NSClosableWindowMask;
45         if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
46         if (wf->resizable) style_mask |= NSResizableWindowMask;
47         if (wf->utility) style_mask |= NSUtilityWindowMask;
48     }
49     else style_mask = NSBorderlessWindowMask;
51     return style_mask;
55 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
57     NSScreen* screen;
58     for (screen in screens)
59     {
60         if (NSIntersectsRect(frame, [screen frame]))
61             return TRUE;
62     }
63     return FALSE;
67 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
69     for (NSScreen* screen in screens)
70     {
71         if (NSContainsRect(rect, [screen frame]))
72             return screen;
73     }
74     return nil;
78 /* We rely on the supposedly device-dependent modifier flags to distinguish the
79    keys on the left side of the keyboard from those on the right.  Some event
80    sources don't set those device-depdendent flags.  If we see a device-independent
81    flag for a modifier without either corresponding device-dependent flag, assume
82    the left one. */
83 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
85     if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
86         *modifiers |= NX_DEVICELCMDKEYMASK;
87     if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
88         *modifiers |= NX_DEVICELSHIFTKEYMASK;
89     if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
90         *modifiers |= NX_DEVICELCTLKEYMASK;
91     if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
92         *modifiers |= NX_DEVICELALTKEYMASK;
95 /* As we manipulate individual bits of a modifier mask, we can end up with
96    inconsistent sets of flags.  In particular, we might set or clear one of the
97    left/right-specific bits, but not the corresponding non-side-specific bit.
98    Fix that.  If either side-specific bit is set, set the non-side-specific bit,
99    otherwise clear it. */
100 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
102     if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
103         *modifiers |= NX_COMMANDMASK;
104     else
105         *modifiers &= ~NX_COMMANDMASK;
106     if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
107         *modifiers |= NX_SHIFTMASK;
108     else
109         *modifiers &= ~NX_SHIFTMASK;
110     if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
111         *modifiers |= NX_CONTROLMASK;
112     else
113         *modifiers &= ~NX_CONTROLMASK;
114     if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
115         *modifiers |= NX_ALTERNATEMASK;
116     else
117         *modifiers &= ~NX_ALTERNATEMASK;
121 @interface WineContentView : NSView
123     NSMutableArray* glContexts;
124     NSMutableArray* pendingGlContexts;
127     - (void) addGLContext:(WineOpenGLContext*)context;
128     - (void) removeGLContext:(WineOpenGLContext*)context;
129     - (void) updateGLContexts;
131 @end
134 @interface WineWindow ()
136 @property (nonatomic) BOOL disabled;
137 @property (nonatomic) BOOL noActivate;
138 @property (readwrite, nonatomic) BOOL floating;
139 @property (retain, nonatomic) NSWindow* latentParentWindow;
141 @property (nonatomic) void* hwnd;
142 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
144 @property (nonatomic) void* surface;
145 @property (nonatomic) pthread_mutex_t* surface_mutex;
147 @property (copy, nonatomic) NSBezierPath* shape;
148 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
149 @property (readonly, nonatomic) BOOL needsTransparency;
151 @property (nonatomic) BOOL colorKeyed;
152 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
153 @property (nonatomic) BOOL usePerPixelAlpha;
155 @property (readwrite, nonatomic) NSInteger levelWhenActive;
157 @end
160 @implementation WineContentView
162     - (void) dealloc
163     {
164         [glContexts release];
165         [pendingGlContexts release];
166         [super dealloc];
167     }
169     - (BOOL) isFlipped
170     {
171         return YES;
172     }
174     - (void) drawRect:(NSRect)rect
175     {
176         WineWindow* window = (WineWindow*)[self window];
178         for (WineOpenGLContext* context in pendingGlContexts)
179             context.needsUpdate = TRUE;
180         [glContexts addObjectsFromArray:pendingGlContexts];
181         [pendingGlContexts removeAllObjects];
183         if ([window contentView] != self)
184             return;
186         if (window.surface && window.surface_mutex &&
187             !pthread_mutex_lock(window.surface_mutex))
188         {
189             const CGRect* rects;
190             int count;
192             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
193             {
194                 CGContextRef context;
195                 int i;
197                 [window.shape addClip];
199                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
200                 CGContextSetBlendMode(context, kCGBlendModeCopy);
202                 for (i = 0; i < count; i++)
203                 {
204                     CGRect imageRect;
205                     CGImageRef image;
207                     imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
208                     image = create_surface_image(window.surface, &imageRect, FALSE);
210                     if (image)
211                     {
212                         if (window.colorKeyed)
213                         {
214                             CGImageRef maskedImage;
215                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
216                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
217                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
218                             maskedImage = CGImageCreateWithMaskingColors(image, components);
219                             if (maskedImage)
220                             {
221                                 CGImageRelease(image);
222                                 image = maskedImage;
223                             }
224                         }
226                         CGContextDrawImage(context, imageRect, image);
228                         CGImageRelease(image);
229                     }
230                 }
231             }
233             pthread_mutex_unlock(window.surface_mutex);
234         }
236         // If the window may be transparent, then we have to invalidate the
237         // shadow every time we draw.  Also, if this is the first time we've
238         // drawn since changing from transparent to opaque.
239         if (![window isOpaque] || window.shapeChangedSinceLastDraw)
240         {
241             window.shapeChangedSinceLastDraw = FALSE;
242             [window invalidateShadow];
243         }
244     }
246     /* By default, NSView will swallow right-clicks in an attempt to support contextual
247        menus.  We need to bypass that and allow the event to make it to the window. */
248     - (void) rightMouseDown:(NSEvent*)theEvent
249     {
250         [[self window] rightMouseDown:theEvent];
251     }
253     - (void) addGLContext:(WineOpenGLContext*)context
254     {
255         if (!glContexts)
256             glContexts = [[NSMutableArray alloc] init];
257         if (!pendingGlContexts)
258             pendingGlContexts = [[NSMutableArray alloc] init];
259         [pendingGlContexts addObject:context];
260         [self setNeedsDisplay:YES];
261     }
263     - (void) removeGLContext:(WineOpenGLContext*)context
264     {
265         [glContexts removeObjectIdenticalTo:context];
266         [pendingGlContexts removeObjectIdenticalTo:context];
267     }
269     - (void) updateGLContexts
270     {
271         for (WineOpenGLContext* context in glContexts)
272             context.needsUpdate = TRUE;
273     }
275     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
276     {
277         return YES;
278     }
280 @end
283 @implementation WineWindow
285     @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
286     @synthesize surface, surface_mutex;
287     @synthesize shape, shapeChangedSinceLastDraw;
288     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
289     @synthesize usePerPixelAlpha;
290     @synthesize levelWhenActive;
292     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
293                                  windowFrame:(NSRect)window_frame
294                                         hwnd:(void*)hwnd
295                                        queue:(WineEventQueue*)queue
296     {
297         WineWindow* window;
298         WineContentView* contentView;
299         NSTrackingArea* trackingArea;
301         [NSApp flipRect:&window_frame];
303         window = [[[self alloc] initWithContentRect:window_frame
304                                           styleMask:style_mask_for_features(wf)
305                                             backing:NSBackingStoreBuffered
306                                               defer:YES] autorelease];
308         if (!window) return nil;
309         window->normalStyleMask = [window styleMask];
311         /* Standardize windows to eliminate differences between titled and
312            borderless windows and between NSWindow and NSPanel. */
313         [window setHidesOnDeactivate:NO];
314         [window setReleasedWhenClosed:NO];
316         [window disableCursorRects];
317         [window setShowsResizeIndicator:NO];
318         [window setHasShadow:wf->shadow];
319         [window setAcceptsMouseMovedEvents:YES];
320         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
321         [window setDelegate:window];
322         window.hwnd = hwnd;
323         window.queue = queue;
325         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
326                                                                   (NSString*)kUTTypeContent,
327                                                                   nil]];
329         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
330         if (!contentView)
331             return nil;
332         [contentView setAutoresizesSubviews:NO];
334         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
335            because they give us mouse moves in the background. */
336         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
337                                                      options:(NSTrackingMouseMoved |
338                                                               NSTrackingActiveAlways |
339                                                               NSTrackingInVisibleRect)
340                                                        owner:window
341                                                     userInfo:nil] autorelease];
342         if (!trackingArea)
343             return nil;
344         [contentView addTrackingArea:trackingArea];
346         [window setContentView:contentView];
348         return window;
349     }
351     - (void) dealloc
352     {
353         [queue release];
354         [latentParentWindow release];
355         [shape release];
356         [super dealloc];
357     }
359     - (void) adjustFeaturesForState
360     {
361         NSUInteger style = normalStyleMask;
363         if (self.disabled)
364             style &= ~NSResizableWindowMask;
365         if (style != [self styleMask])
366             [self setStyleMask:style];
368         if (style & NSClosableWindowMask)
369             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
370         if (style & NSMiniaturizableWindowMask)
371             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
372     }
374     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
375     {
376         normalStyleMask = style_mask_for_features(wf);
377         [self adjustFeaturesForState];
378         [self setHasShadow:wf->shadow];
379     }
381     - (void) adjustWindowLevel
382     {
383         NSInteger level;
384         BOOL fullscreen, captured;
385         NSScreen* screen;
386         NSUInteger index;
387         WineWindow* other = nil;
389         screen = screen_covered_by_rect([self frame], [NSScreen screens]);
390         fullscreen = (screen != nil);
391         captured = (screen || [self screen]) && [NSApp areDisplaysCaptured];
393         if (captured || fullscreen)
394         {
395             if (captured)
396                 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
397             else
398                 level = NSMainMenuWindowLevel + 1;
400             if (self.floating)
401                 level++;
402         }
403         else if (self.floating)
404             level = NSFloatingWindowLevel;
405         else
406             level = NSNormalWindowLevel;
408         index = [[NSApp orderedWineWindows] indexOfObjectIdenticalTo:self];
409         if (index != NSNotFound && index + 1 < [[NSApp orderedWineWindows] count])
410         {
411             other = [[NSApp orderedWineWindows] objectAtIndex:index + 1];
412             if (level < [other level])
413                 level = [other level];
414         }
416         if (level != [self level])
417         {
418             [self setLevelWhenActive:level];
420             /* Setting the window level above has moved this window to the front
421                of all other windows at the same level.  We need to move it
422                back into its proper place among other windows of that level.
423                Also, any windows which are supposed to be in front of it had
424                better have the same or higher window level.  If not, bump them
425                up. */
426             if (index != NSNotFound)
427             {
428                 for (; index > 0; index--)
429                 {
430                     other = [[NSApp orderedWineWindows] objectAtIndex:index - 1];
431                     if ([other level] < level)
432                         [other setLevelWhenActive:level];
433                     else
434                     {
435                         [self orderWindow:NSWindowBelow relativeTo:[other windowNumber]];
436                         break;
437                     }
438                 }
439             }
440         }
441     }
443     - (void) setMacDrvState:(const struct macdrv_window_state*)state
444     {
445         NSWindowCollectionBehavior behavior;
447         self.disabled = state->disabled;
448         self.noActivate = state->no_activate;
450         self.floating = state->floating;
451         [self adjustWindowLevel];
453         behavior = NSWindowCollectionBehaviorDefault;
454         if (state->excluded_by_expose)
455             behavior |= NSWindowCollectionBehaviorTransient;
456         else
457             behavior |= NSWindowCollectionBehaviorManaged;
458         if (state->excluded_by_cycle)
459         {
460             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
461             if ([self isVisible])
462                 [NSApp removeWindowsItem:self];
463         }
464         else
465         {
466             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
467             if ([self isVisible])
468                 [NSApp addWindowsItem:self title:[self title] filename:NO];
469         }
470         [self setCollectionBehavior:behavior];
472         if (state->minimized && ![self isMiniaturized])
473         {
474             ignore_windowMiniaturize = TRUE;
475             [self miniaturize:nil];
476         }
477         else if (!state->minimized && [self isMiniaturized])
478         {
479             ignore_windowDeminiaturize = TRUE;
480             [self deminiaturize:nil];
481         }
483         /* Whatever events regarding minimization might have been in the queue are now stale. */
484         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
485                                          event_mask_for_type(WINDOW_DID_UNMINIMIZE)
486                                forWindow:self];
487     }
489     /* Returns whether or not the window was ordered in, which depends on if
490        its frame intersects any screen. */
491     - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
492     {
493         BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
494         if (on_screen)
495         {
496             [NSApp transformProcessToForeground];
498             if (prev)
499             {
500                 /* Make sure that windows that should be above this one really are.
501                    This is necessary since a full-screen window gets a boost to its
502                    window level to be in front of the menu bar and Dock and that moves
503                    it out of the z-order that Win32 would otherwise establish. */
504                 if ([prev level] < [self level])
505                 {
506                     NSUInteger index = [[NSApp orderedWineWindows] indexOfObjectIdenticalTo:prev];
507                     if (index != NSNotFound)
508                     {
509                         [prev setLevelWhenActive:[self level]];
510                         for (; index > 0; index--)
511                         {
512                             WineWindow* other = [[NSApp orderedWineWindows] objectAtIndex:index - 1];
513                             if ([other level] < [self level])
514                                 [other setLevelWhenActive:[self level]];
515                         }
516                     }
517                 }
518                 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
519                 [NSApp wineWindow:self ordered:NSWindowBelow relativeTo:prev];
520             }
521             else
522             {
523                 /* Similarly, make sure this window is really above what it should be. */
524                 if (next && [next level] > [self level])
525                     [self setLevelWhenActive:[next level]];
526                 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
527                 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:next];
528             }
529             if (latentParentWindow)
530             {
531                 if ([latentParentWindow level] > [self level])
532                     [self setLevelWhenActive:[latentParentWindow level]];
533                 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
534                 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
535                 self.latentParentWindow = nil;
536             }
538             /* Cocoa may adjust the frame when the window is ordered onto the screen.
539                Generate a frame-changed event just in case.  The back end will ignore
540                it if nothing actually changed. */
541             [self windowDidResize:nil];
543             if (![self isExcludedFromWindowsMenu])
544                 [NSApp addWindowsItem:self title:[self title] filename:NO];
545         }
547         return on_screen;
548     }
550     - (void) doOrderOut
551     {
552         self.latentParentWindow = [self parentWindow];
553         [latentParentWindow removeChildWindow:self];
554         [self orderOut:nil];
555         [NSApp wineWindow:self ordered:NSWindowOut relativeTo:nil];
556         [NSApp removeWindowsItem:self];
557     }
559     - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
560     {
561         NSArray* screens = [NSScreen screens];
562         BOOL on_screen = [self isVisible];
563         NSRect frame, oldFrame;
565         if (![screens count]) return on_screen;
567         /* Origin is (left, top) in a top-down space.  Need to convert it to
568            (left, bottom) in a bottom-up space. */
569         [NSApp flipRect:&contentRect];
571         if (on_screen)
572         {
573             on_screen = frame_intersects_screens(contentRect, screens);
574             if (!on_screen)
575                 [self doOrderOut];
576         }
578         if (!NSIsEmptyRect(contentRect))
579         {
580             oldFrame = [self frame];
581             frame = [self frameRectForContentRect:contentRect];
582             if (!NSEqualRects(frame, oldFrame))
583             {
584                 if (NSEqualSizes(frame.size, oldFrame.size))
585                     [self setFrameOrigin:frame.origin];
586                 else
587                     [self setFrame:frame display:YES];
588             }
589         }
591         if (on_screen)
592         {
593             [self adjustWindowLevel];
595             /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
596                event.  The back end will ignore it if nothing actually changed. */
597             [self windowDidResize:nil];
598         }
599         else
600         {
601             /* The back end is establishing a new window size and position.  It's
602                not interested in any stale events regarding those that may be sitting
603                in the queue. */
604             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
605                                    forWindow:self];
606         }
608         return on_screen;
609     }
611     - (void) setMacDrvParentWindow:(WineWindow*)parent
612     {
613         if ([self parentWindow] != parent)
614         {
615             [[self parentWindow] removeChildWindow:self];
616             self.latentParentWindow = nil;
617             if ([self isVisible] && parent)
618             {
619                 if ([parent level] > [self level])
620                     [self setLevelWhenActive:[parent level]];
621                 [parent addChildWindow:self ordered:NSWindowAbove];
622                 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:parent];
623             }
624             else
625                 self.latentParentWindow = parent;
626         }
627     }
629     - (void) setDisabled:(BOOL)newValue
630     {
631         if (disabled != newValue)
632         {
633             disabled = newValue;
634             [self adjustFeaturesForState];
635         }
636     }
638     - (BOOL) needsTransparency
639     {
640         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
641     }
643     - (void) checkTransparency
644     {
645         if (![self isOpaque] && !self.needsTransparency)
646         {
647             [self setBackgroundColor:[NSColor windowBackgroundColor]];
648             [self setOpaque:YES];
649         }
650         else if ([self isOpaque] && self.needsTransparency)
651         {
652             [self setBackgroundColor:[NSColor clearColor]];
653             [self setOpaque:NO];
654         }
655     }
657     - (void) setShape:(NSBezierPath*)newShape
658     {
659         if (shape == newShape) return;
660         if (shape && newShape && [shape isEqual:newShape]) return;
662         if (shape)
663         {
664             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
665             [shape release];
666         }
667         if (newShape)
668             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
670         shape = [newShape copy];
671         self.shapeChangedSinceLastDraw = TRUE;
673         [self checkTransparency];
674     }
676     - (void) postMouseButtonEvent:(NSEvent *)theEvent pressed:(int)pressed
677     {
678         CGPoint pt = CGEventGetLocation([theEvent CGEvent]);
679         macdrv_event event;
681         event.type = MOUSE_BUTTON;
682         event.window = (macdrv_window)[self retain];
683         event.mouse_button.button = [theEvent buttonNumber];
684         event.mouse_button.pressed = pressed;
685         event.mouse_button.x = pt.x;
686         event.mouse_button.y = pt.y;
687         event.mouse_button.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
689         [queue postEvent:&event];
690     }
692     - (void) makeFocused
693     {
694         NSArray* screens;
696         [NSApp transformProcessToForeground];
698         /* If a borderless window is offscreen, orderFront: won't move
699            it onscreen like it would for a titled window.  Do that ourselves. */
700         screens = [NSScreen screens];
701         if (!([self styleMask] & NSTitledWindowMask) && ![self isVisible] &&
702             !frame_intersects_screens([self frame], screens))
703         {
704             NSScreen* primaryScreen = [screens objectAtIndex:0];
705             NSRect frame = [primaryScreen frame];
706             [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
707             frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
708             [self setFrame:frame display:YES];
709         }
711         if ([[NSApp orderedWineWindows] count])
712         {
713             WineWindow* front;
714             if (self.floating)
715                 front = [[NSApp orderedWineWindows] objectAtIndex:0];
716             else
717             {
718                 for (front in [NSApp orderedWineWindows])
719                     if (!front.floating) break;
720             }
721             if (front && [front levelWhenActive] > [self levelWhenActive])
722                 [self setLevelWhenActive:[front levelWhenActive]];
723         }
724         [self orderFront:nil];
725         [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
726         causing_becomeKeyWindow = TRUE;
727         [self makeKeyWindow];
728         causing_becomeKeyWindow = FALSE;
729         if (latentParentWindow)
730         {
731             if ([latentParentWindow level] > [self level])
732                 [self setLevelWhenActive:[latentParentWindow level]];
733             [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
734             [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
735             self.latentParentWindow = nil;
736         }
737         if (![self isExcludedFromWindowsMenu])
738             [NSApp addWindowsItem:self title:[self title] filename:NO];
740         /* Cocoa may adjust the frame when the window is ordered onto the screen.
741            Generate a frame-changed event just in case.  The back end will ignore
742            it if nothing actually changed. */
743         [self windowDidResize:nil];
744     }
746     - (void) postKey:(uint16_t)keyCode
747              pressed:(BOOL)pressed
748            modifiers:(NSUInteger)modifiers
749                event:(NSEvent*)theEvent
750     {
751         macdrv_event event;
752         CGEventRef cgevent;
753         WineApplication* app = (WineApplication*)NSApp;
755         event.type          = pressed ? KEY_PRESS : KEY_RELEASE;
756         event.window        = (macdrv_window)[self retain];
757         event.key.keycode   = keyCode;
758         event.key.modifiers = modifiers;
759         event.key.time_ms   = [app ticksForEventTime:[theEvent timestamp]];
761         if ((cgevent = [theEvent CGEvent]))
762         {
763             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
764                                                         kCGKeyboardEventKeyboardType);
765             if (keyboardType != app.keyboardType)
766             {
767                 app.keyboardType = keyboardType;
768                 [app keyboardSelectionDidChange];
769             }
770         }
772         [queue postEvent:&event];
773     }
775     - (void) postKeyEvent:(NSEvent *)theEvent
776     {
777         [self flagsChanged:theEvent];
778         [self postKey:[theEvent keyCode]
779               pressed:[theEvent type] == NSKeyDown
780             modifiers:[theEvent modifierFlags]
781                 event:theEvent];
782     }
784     - (void) postMouseMovedEvent:(NSEvent *)theEvent absolute:(BOOL)absolute
785     {
786         macdrv_event event;
788         if (absolute)
789         {
790             CGPoint point = CGEventGetLocation([theEvent CGEvent]);
792             event.type = MOUSE_MOVED_ABSOLUTE;
793             event.mouse_moved.x = point.x;
794             event.mouse_moved.y = point.y;
796             mouseMoveDeltaX = 0;
797             mouseMoveDeltaY = 0;
798         }
799         else
800         {
801             /* Add event delta to accumulated delta error */
802             /* deltaY is already flipped */
803             mouseMoveDeltaX += [theEvent deltaX];
804             mouseMoveDeltaY += [theEvent deltaY];
806             event.type = MOUSE_MOVED;
807             event.mouse_moved.x = mouseMoveDeltaX;
808             event.mouse_moved.y = mouseMoveDeltaY;
810             /* Keep the remainder after integer truncation. */
811             mouseMoveDeltaX -= event.mouse_moved.x;
812             mouseMoveDeltaY -= event.mouse_moved.y;
813         }
815         if (event.type == MOUSE_MOVED_ABSOLUTE || event.mouse_moved.x || event.mouse_moved.y)
816         {
817             event.window = (macdrv_window)[self retain];
818             event.mouse_moved.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
820             [queue postEvent:&event];
821         }
822     }
824     - (void) setLevelWhenActive:(NSInteger)level
825     {
826         levelWhenActive = level;
827         if (([NSApp isActive] || level <= NSFloatingWindowLevel) &&
828             level != [self level])
829             [self setLevel:level];
830     }
833     /*
834      * ---------- NSWindow method overrides ----------
835      */
836     - (BOOL) canBecomeKeyWindow
837     {
838         if (causing_becomeKeyWindow) return YES;
839         if (self.disabled || self.noActivate) return NO;
840         return [self isKeyWindow];
841     }
843     - (BOOL) canBecomeMainWindow
844     {
845         return [self canBecomeKeyWindow];
846     }
848     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
849     {
850         // If a window is sized to completely cover a screen, then it's in
851         // full-screen mode.  In that case, we don't allow NSWindow to constrain
852         // it.
853         NSRect contentRect = [self contentRectForFrameRect:frameRect];
854         if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
855             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
856         return frameRect;
857     }
859     - (BOOL) isExcludedFromWindowsMenu
860     {
861         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
862     }
864     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
865     {
866         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
867             return [self isKeyWindow] || (!self.disabled && !self.noActivate);
868         return [super validateMenuItem:menuItem];
869     }
871     /* We don't call this.  It's the action method of the items in the Window menu. */
872     - (void) makeKeyAndOrderFront:(id)sender
873     {
874         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
875             [NSApp windowGotFocus:self];
876     }
878     - (void) sendEvent:(NSEvent*)event
879     {
880         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
881            interface control.  For example, Control-Tab switches focus among
882            views.  We want to bypass that feature, so directly route key-down
883            events to -keyDown:. */
884         if ([event type] == NSKeyDown)
885             [[self firstResponder] keyDown:event];
886         else
887         {
888             if ([event type] == NSLeftMouseDown)
889             {
890                 NSWindowButton windowButton;
891                 BOOL broughtWindowForward = TRUE;
893                 /* Since our windows generally claim they can't be made key, clicks
894                    in their title bars are swallowed by the theme frame stuff.  So,
895                    we hook directly into the event stream and assume that any click
896                    in the window will activate it, if Wine and the Win32 program
897                    accept. */
898                 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
899                     [NSApp windowGotFocus:self];
901                 /* Any left-click on our window anyplace other than the close or
902                    minimize buttons will bring it forward. */
903                 for (windowButton = NSWindowCloseButton;
904                      windowButton <= NSWindowMiniaturizeButton;
905                      windowButton++)
906                 {
907                     NSButton* button = [[event window] standardWindowButton:windowButton];
908                     if (button)
909                     {
910                         NSPoint point = [button convertPoint:[event locationInWindow] fromView:nil];
911                         if ([button mouse:point inRect:[button bounds]])
912                         {
913                             broughtWindowForward = FALSE;
914                             break;
915                         }
916                     }
917                 }
919                 if (broughtWindowForward)
920                     [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
921             }
923             [super sendEvent:event];
924         }
925     }
928     /*
929      * ---------- NSResponder method overrides ----------
930      */
931     - (void) mouseDown:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:1]; }
932     - (void) rightMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
933     - (void) otherMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
935     - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:0]; }
936     - (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
937     - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
939     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
940     - (void) keyUp:(NSEvent *)theEvent   { [self postKeyEvent:theEvent]; }
942     - (void) flagsChanged:(NSEvent *)theEvent
943     {
944         static const struct {
945             NSUInteger  mask;
946             uint16_t    keycode;
947         } modifiers[] = {
948             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
949             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
950             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
951             { NX_DEVICELCTLKEYMASK,     kVK_Control },
952             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
953             { NX_DEVICELALTKEYMASK,     kVK_Option },
954             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
955             { NX_DEVICELCMDKEYMASK,     kVK_Command },
956             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
957         };
959         NSUInteger modifierFlags = [theEvent modifierFlags];
960         NSUInteger changed;
961         int i, last_changed;
963         fix_device_modifiers_by_generic(&modifierFlags);
964         changed = modifierFlags ^ lastModifierFlags;
966         last_changed = -1;
967         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
968             if (changed & modifiers[i].mask)
969                 last_changed = i;
971         for (i = 0; i <= last_changed; i++)
972         {
973             if (changed & modifiers[i].mask)
974             {
975                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
977                 if (i == last_changed)
978                     lastModifierFlags = modifierFlags;
979                 else
980                 {
981                     lastModifierFlags ^= modifiers[i].mask;
982                     fix_generic_modifiers_by_device(&lastModifierFlags);
983                 }
985                 // Caps lock generates one event for each press-release action.
986                 // We need to simulate a pair of events for each actual event.
987                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
988                 {
989                     [self postKey:modifiers[i].keycode
990                           pressed:TRUE
991                         modifiers:lastModifierFlags
992                             event:(NSEvent*)theEvent];
993                     pressed = FALSE;
994                 }
996                 [self postKey:modifiers[i].keycode
997                       pressed:pressed
998                     modifiers:lastModifierFlags
999                         event:(NSEvent*)theEvent];
1000             }
1001         }
1002     }
1004     - (void) scrollWheel:(NSEvent *)theEvent
1005     {
1006         CGPoint pt;
1007         macdrv_event event;
1008         CGEventRef cgevent;
1009         CGFloat x, y;
1010         BOOL continuous = FALSE;
1012         cgevent = [theEvent CGEvent];
1013         pt = CGEventGetLocation(cgevent);
1015         event.type = MOUSE_SCROLL;
1016         event.window = (macdrv_window)[self retain];
1017         event.mouse_scroll.x = pt.x;
1018         event.mouse_scroll.y = pt.y;
1019         event.mouse_scroll.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
1021         if (CGEventGetIntegerValueField(cgevent, kCGScrollWheelEventIsContinuous))
1022         {
1023             continuous = TRUE;
1025             /* Continuous scroll wheel events come from high-precision scrolling
1026                hardware like Apple's Magic Mouse, Mighty Mouse, and trackpads.
1027                For these, we can get more precise data from the CGEvent API. */
1028             /* Axis 1 is vertical, axis 2 is horizontal. */
1029             x = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis2);
1030             y = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis1);
1031         }
1032         else
1033         {
1034             double pixelsPerLine = 10;
1035             CGEventSourceRef source;
1037             /* The non-continuous values are in units of "lines", not pixels. */
1038             if ((source = CGEventCreateSourceFromEvent(cgevent)))
1039             {
1040                 pixelsPerLine = CGEventSourceGetPixelsPerLine(source);
1041                 CFRelease(source);
1042             }
1044             x = pixelsPerLine * [theEvent deltaX];
1045             y = pixelsPerLine * [theEvent deltaY];
1046         }
1048         /* Mac: negative is right or down, positive is left or up.
1049            Win32: negative is left or down, positive is right or up.
1050            So, negate the X scroll value to translate. */
1051         x = -x;
1053         /* The x,y values so far are in pixels.  Win32 expects to receive some
1054            fraction of WHEEL_DELTA == 120.  By my estimation, that's roughly
1055            6 times the pixel value. */
1056         event.mouse_scroll.x_scroll = 6 * x;
1057         event.mouse_scroll.y_scroll = 6 * y;
1059         if (!continuous)
1060         {
1061             /* For non-continuous "clicky" wheels, if there was any motion, make
1062                sure there was at least WHEEL_DELTA motion.  This is so, at slow
1063                speeds where the system's acceleration curve is actually reducing the
1064                scroll distance, the user is sure to get some action out of each click.
1065                For example, this is important for rotating though weapons in a
1066                first-person shooter. */
1067             if (0 < event.mouse_scroll.x_scroll && event.mouse_scroll.x_scroll < 120)
1068                 event.mouse_scroll.x_scroll = 120;
1069             else if (-120 < event.mouse_scroll.x_scroll && event.mouse_scroll.x_scroll < 0)
1070                 event.mouse_scroll.x_scroll = -120;
1072             if (0 < event.mouse_scroll.y_scroll && event.mouse_scroll.y_scroll < 120)
1073                 event.mouse_scroll.y_scroll = 120;
1074             else if (-120 < event.mouse_scroll.y_scroll && event.mouse_scroll.y_scroll < 0)
1075                 event.mouse_scroll.y_scroll = -120;
1076         }
1078         if (event.mouse_scroll.x_scroll || event.mouse_scroll.y_scroll)
1079             [queue postEvent:&event];
1080     }
1083     /*
1084      * ---------- NSWindowDelegate methods ----------
1085      */
1086     - (void)windowDidBecomeKey:(NSNotification *)notification
1087     {
1088         NSEvent* event = [NSApp lastFlagsChanged];
1089         if (event)
1090             [self flagsChanged:event];
1092         if (causing_becomeKeyWindow) return;
1094         [NSApp windowGotFocus:self];
1095     }
1097     - (void)windowDidDeminiaturize:(NSNotification *)notification
1098     {
1099         if (!ignore_windowDeminiaturize)
1100         {
1101             macdrv_event event;
1103             /* Coalesce events by discarding any previous ones still in the queue. */
1104             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1105                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1106                                    forWindow:self];
1108             event.type = WINDOW_DID_UNMINIMIZE;
1109             event.window = (macdrv_window)[self retain];
1110             [queue postEvent:&event];
1111         }
1113         ignore_windowDeminiaturize = FALSE;
1115         [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1116     }
1118     - (void)windowDidMove:(NSNotification *)notification
1119     {
1120         [self windowDidResize:notification];
1121     }
1123     - (void)windowDidResignKey:(NSNotification *)notification
1124     {
1125         macdrv_event event;
1127         if (causing_becomeKeyWindow) return;
1129         event.type = WINDOW_LOST_FOCUS;
1130         event.window = (macdrv_window)[self retain];
1131         [queue postEvent:&event];
1132     }
1134     - (void)windowDidResize:(NSNotification *)notification
1135     {
1136         macdrv_event event;
1137         NSRect frame = [self contentRectForFrameRect:[self frame]];
1139         [NSApp flipRect:&frame];
1141         /* Coalesce events by discarding any previous ones still in the queue. */
1142         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1143                                forWindow:self];
1145         event.type = WINDOW_FRAME_CHANGED;
1146         event.window = (macdrv_window)[self retain];
1147         event.window_frame_changed.frame = NSRectToCGRect(frame);
1148         [queue postEvent:&event];
1149     }
1151     - (BOOL)windowShouldClose:(id)sender
1152     {
1153         macdrv_event event;
1154         event.type = WINDOW_CLOSE_REQUESTED;
1155         event.window = (macdrv_window)[self retain];
1156         [queue postEvent:&event];
1157         return NO;
1158     }
1160     - (void)windowWillMiniaturize:(NSNotification *)notification
1161     {
1162         if (!ignore_windowMiniaturize)
1163         {
1164             macdrv_event event;
1166             /* Coalesce events by discarding any previous ones still in the queue. */
1167             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1168                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1169                                    forWindow:self];
1171             event.type = WINDOW_DID_MINIMIZE;
1172             event.window = (macdrv_window)[self retain];
1173             [queue postEvent:&event];
1174         }
1176         ignore_windowMiniaturize = FALSE;
1177     }
1180     /*
1181      * ---------- NSPasteboardOwner methods ----------
1182      */
1183     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1184     {
1185         macdrv_query* query = macdrv_create_query();
1186         query->type = QUERY_PASTEBOARD_DATA;
1187         query->window = (macdrv_window)[self retain];
1188         query->pasteboard_data.type = (CFStringRef)[type copy];
1190         [self.queue query:query timeout:3];
1191         macdrv_release_query(query);
1192     }
1195     /*
1196      * ---------- NSDraggingDestination methods ----------
1197      */
1198     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1199     {
1200         return [self draggingUpdated:sender];
1201     }
1203     - (void) draggingExited:(id <NSDraggingInfo>)sender
1204     {
1205         // This isn't really a query.  We don't need any response.  However, it
1206         // has to be processed in a similar manner as the other drag-and-drop
1207         // queries in order to maintain the proper order of operations.
1208         macdrv_query* query = macdrv_create_query();
1209         query->type = QUERY_DRAG_EXITED;
1210         query->window = (macdrv_window)[self retain];
1212         [self.queue query:query timeout:0.1];
1213         macdrv_release_query(query);
1214     }
1216     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1217     {
1218         NSDragOperation ret;
1219         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1220         NSPasteboard* pb = [sender draggingPasteboard];
1222         macdrv_query* query = macdrv_create_query();
1223         query->type = QUERY_DRAG_OPERATION;
1224         query->window = (macdrv_window)[self retain];
1225         query->drag_operation.x = pt.x;
1226         query->drag_operation.y = pt.y;
1227         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1228         query->drag_operation.accepted_op = NSDragOperationNone;
1229         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1231         [self.queue query:query timeout:3];
1232         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1233         macdrv_release_query(query);
1235         return ret;
1236     }
1238     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1239     {
1240         BOOL ret;
1241         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1242         NSPasteboard* pb = [sender draggingPasteboard];
1244         macdrv_query* query = macdrv_create_query();
1245         query->type = QUERY_DRAG_DROP;
1246         query->window = (macdrv_window)[self retain];
1247         query->drag_drop.x = pt.x;
1248         query->drag_drop.y = pt.y;
1249         query->drag_drop.op = [sender draggingSourceOperationMask];
1250         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1252         [self.queue query:query timeout:3 * 60 processEvents:YES];
1253         ret = query->status;
1254         macdrv_release_query(query);
1256         return ret;
1257     }
1259     - (BOOL) wantsPeriodicDraggingUpdates
1260     {
1261         return NO;
1262     }
1264 @end
1267 /***********************************************************************
1268  *              macdrv_create_cocoa_window
1270  * Create a Cocoa window with the given content frame and features (e.g.
1271  * title bar, close box, etc.).
1272  */
1273 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1274         CGRect frame, void* hwnd, macdrv_event_queue queue)
1276     __block WineWindow* window;
1278     OnMainThread(^{
1279         window = [[WineWindow createWindowWithFeatures:wf
1280                                            windowFrame:NSRectFromCGRect(frame)
1281                                                   hwnd:hwnd
1282                                                  queue:(WineEventQueue*)queue] retain];
1283     });
1285     return (macdrv_window)window;
1288 /***********************************************************************
1289  *              macdrv_destroy_cocoa_window
1291  * Destroy a Cocoa window.
1292  */
1293 void macdrv_destroy_cocoa_window(macdrv_window w)
1295     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1296     WineWindow* window = (WineWindow*)w;
1298     [window.queue discardEventsMatchingMask:-1 forWindow:window];
1299     [window close];
1300     [window release];
1302     [pool release];
1305 /***********************************************************************
1306  *              macdrv_get_window_hwnd
1308  * Get the hwnd that was set for the window at creation.
1309  */
1310 void* macdrv_get_window_hwnd(macdrv_window w)
1312     WineWindow* window = (WineWindow*)w;
1313     return window.hwnd;
1316 /***********************************************************************
1317  *              macdrv_set_cocoa_window_features
1319  * Update a Cocoa window's features.
1320  */
1321 void macdrv_set_cocoa_window_features(macdrv_window w,
1322         const struct macdrv_window_features* wf)
1324     WineWindow* window = (WineWindow*)w;
1326     OnMainThread(^{
1327         [window setWindowFeatures:wf];
1328     });
1331 /***********************************************************************
1332  *              macdrv_set_cocoa_window_state
1334  * Update a Cocoa window's state.
1335  */
1336 void macdrv_set_cocoa_window_state(macdrv_window w,
1337         const struct macdrv_window_state* state)
1339     WineWindow* window = (WineWindow*)w;
1341     OnMainThread(^{
1342         [window setMacDrvState:state];
1343     });
1346 /***********************************************************************
1347  *              macdrv_set_cocoa_window_title
1349  * Set a Cocoa window's title.
1350  */
1351 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1352         size_t length)
1354     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1355     WineWindow* window = (WineWindow*)w;
1356     NSString* titleString;
1358     if (title)
1359         titleString = [NSString stringWithCharacters:title length:length];
1360     else
1361         titleString = @"";
1362     OnMainThreadAsync(^{
1363         [window setTitle:titleString];
1364         if ([window isVisible] && ![window isExcludedFromWindowsMenu])
1365             [NSApp changeWindowsItem:window title:titleString filename:NO];
1366     });
1368     [pool release];
1371 /***********************************************************************
1372  *              macdrv_order_cocoa_window
1374  * Reorder a Cocoa window relative to other windows.  If prev is
1375  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
1376  * it is ordered above that window.  Otherwise, it is ordered to the
1377  * front.
1379  * Returns true if the window has actually been ordered onto the screen
1380  * (i.e. if its frame intersects with a screen).  Otherwise, false.
1381  */
1382 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1383         macdrv_window next)
1385     WineWindow* window = (WineWindow*)w;
1386     __block BOOL on_screen;
1388     OnMainThread(^{
1389         on_screen = [window orderBelow:(WineWindow*)prev
1390                                orAbove:(WineWindow*)next];
1391     });
1393     return on_screen;
1396 /***********************************************************************
1397  *              macdrv_hide_cocoa_window
1399  * Hides a Cocoa window.
1400  */
1401 void macdrv_hide_cocoa_window(macdrv_window w)
1403     WineWindow* window = (WineWindow*)w;
1405     OnMainThread(^{
1406         [window doOrderOut];
1407     });
1410 /***********************************************************************
1411  *              macdrv_set_cocoa_window_frame
1413  * Move a Cocoa window.  If the window has been moved out of the bounds
1414  * of the desktop, it is ordered out.  (This routine won't ever order a
1415  * window in, though.)
1417  * Returns true if the window is on screen; false otherwise.
1418  */
1419 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1421     WineWindow* window = (WineWindow*)w;
1422     __block BOOL on_screen;
1424     OnMainThread(^{
1425         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1426     });
1428     return on_screen;
1431 /***********************************************************************
1432  *              macdrv_get_cocoa_window_frame
1434  * Gets the frame of a Cocoa window.
1435  */
1436 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1438     WineWindow* window = (WineWindow*)w;
1440     OnMainThread(^{
1441         NSRect frame;
1443         frame = [window contentRectForFrameRect:[window frame]];
1444         [NSApp flipRect:&frame];
1445         *out_frame = NSRectToCGRect(frame);
1446     });
1449 /***********************************************************************
1450  *              macdrv_set_cocoa_parent_window
1452  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
1453  * the parent window.
1454  */
1455 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1457     WineWindow* window = (WineWindow*)w;
1459     OnMainThread(^{
1460         [window setMacDrvParentWindow:(WineWindow*)parent];
1461     });
1464 /***********************************************************************
1465  *              macdrv_set_window_surface
1466  */
1467 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1469     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1470     WineWindow* window = (WineWindow*)w;
1472     OnMainThread(^{
1473         window.surface = surface;
1474         window.surface_mutex = mutex;
1475     });
1477     [pool release];
1480 /***********************************************************************
1481  *              macdrv_window_needs_display
1483  * Mark a window as needing display in a specified rect (in non-client
1484  * area coordinates).
1485  */
1486 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1488     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1489     WineWindow* window = (WineWindow*)w;
1491     OnMainThreadAsync(^{
1492         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1493     });
1495     [pool release];
1498 /***********************************************************************
1499  *              macdrv_set_window_shape
1501  * Sets the shape of a Cocoa window from an array of rectangles.  If
1502  * rects is NULL, resets the window's shape to its frame.
1503  */
1504 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1506     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1507     WineWindow* window = (WineWindow*)w;
1509     OnMainThread(^{
1510         if (!rects || !count)
1511             window.shape = nil;
1512         else
1513         {
1514             NSBezierPath* path;
1515             unsigned int i;
1517             path = [NSBezierPath bezierPath];
1518             for (i = 0; i < count; i++)
1519                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1520             window.shape = path;
1521         }
1522     });
1524     [pool release];
1527 /***********************************************************************
1528  *              macdrv_set_window_alpha
1529  */
1530 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1532     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1533     WineWindow* window = (WineWindow*)w;
1535     [window setAlphaValue:alpha];
1537     [pool release];
1540 /***********************************************************************
1541  *              macdrv_set_window_color_key
1542  */
1543 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1544                                  CGFloat keyBlue)
1546     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1547     WineWindow* window = (WineWindow*)w;
1549     OnMainThread(^{
1550         window.colorKeyed       = TRUE;
1551         window.colorKeyRed      = keyRed;
1552         window.colorKeyGreen    = keyGreen;
1553         window.colorKeyBlue     = keyBlue;
1554         [window checkTransparency];
1555     });
1557     [pool release];
1560 /***********************************************************************
1561  *              macdrv_clear_window_color_key
1562  */
1563 void macdrv_clear_window_color_key(macdrv_window w)
1565     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1566     WineWindow* window = (WineWindow*)w;
1568     OnMainThread(^{
1569         window.colorKeyed = FALSE;
1570         [window checkTransparency];
1571     });
1573     [pool release];
1576 /***********************************************************************
1577  *              macdrv_window_use_per_pixel_alpha
1578  */
1579 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1581     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1582     WineWindow* window = (WineWindow*)w;
1584     OnMainThread(^{
1585         window.usePerPixelAlpha = use_per_pixel_alpha;
1586         [window checkTransparency];
1587     });
1589     [pool release];
1592 /***********************************************************************
1593  *              macdrv_give_cocoa_window_focus
1595  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
1596  * orders it front and, if its frame was not within the desktop bounds,
1597  * Cocoa will typically move it on-screen.
1598  */
1599 void macdrv_give_cocoa_window_focus(macdrv_window w)
1601     WineWindow* window = (WineWindow*)w;
1603     OnMainThread(^{
1604         [window makeFocused];
1605     });
1608 /***********************************************************************
1609  *              macdrv_create_view
1611  * Creates and returns a view in the specified rect of the window.  The
1612  * caller is responsible for calling macdrv_dispose_view() on the view
1613  * when it is done with it.
1614  */
1615 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1617     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1618     WineWindow* window = (WineWindow*)w;
1619     __block WineContentView* view;
1621     if (CGRectIsNull(rect)) rect = CGRectZero;
1623     OnMainThread(^{
1624         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1626         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1627         [view setAutoresizesSubviews:NO];
1628         [nc addObserver:view
1629                selector:@selector(updateGLContexts)
1630                    name:NSViewGlobalFrameDidChangeNotification
1631                  object:view];
1632         [nc addObserver:view
1633                selector:@selector(updateGLContexts)
1634                    name:NSApplicationDidChangeScreenParametersNotification
1635                  object:NSApp];
1636         [[window contentView] addSubview:view];
1637     });
1639     [pool release];
1640     return (macdrv_view)view;
1643 /***********************************************************************
1644  *              macdrv_dispose_view
1646  * Destroys a view previously returned by macdrv_create_view.
1647  */
1648 void macdrv_dispose_view(macdrv_view v)
1650     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1651     WineContentView* view = (WineContentView*)v;
1653     OnMainThread(^{
1654         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1656         [nc removeObserver:view
1657                       name:NSViewGlobalFrameDidChangeNotification
1658                     object:view];
1659         [nc removeObserver:view
1660                       name:NSApplicationDidChangeScreenParametersNotification
1661                     object:NSApp];
1662         [view removeFromSuperview];
1663         [view release];
1664     });
1666     [pool release];
1669 /***********************************************************************
1670  *              macdrv_set_view_window_and_frame
1672  * Move a view to a new window and/or position within its window.  If w
1673  * is NULL, leave the view in its current window and just change its
1674  * frame.
1675  */
1676 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1678     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1679     WineContentView* view = (WineContentView*)v;
1680     WineWindow* window = (WineWindow*)w;
1682     if (CGRectIsNull(rect)) rect = CGRectZero;
1684     OnMainThread(^{
1685         BOOL changedWindow = (window && window != [view window]);
1686         NSRect newFrame = NSRectFromCGRect(rect);
1687         NSRect oldFrame = [view frame];
1689         if (changedWindow)
1690         {
1691             [view removeFromSuperview];
1692             [[window contentView] addSubview:view];
1693         }
1695         if (!NSEqualRects(oldFrame, newFrame))
1696         {
1697             if (!changedWindow)
1698                 [[view superview] setNeedsDisplayInRect:oldFrame];
1699             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1700                 [view setFrameSize:newFrame.size];
1701             else if (NSEqualSizes(oldFrame.size, newFrame.size))
1702                 [view setFrameOrigin:newFrame.origin];
1703             else
1704                 [view setFrame:newFrame];
1705             [view setNeedsDisplay:YES];
1706         }
1707     });
1709     [pool release];
1712 /***********************************************************************
1713  *              macdrv_add_view_opengl_context
1715  * Add an OpenGL context to the list being tracked for each view.
1716  */
1717 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1719     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1720     WineContentView* view = (WineContentView*)v;
1721     WineOpenGLContext *context = (WineOpenGLContext*)c;
1723     OnMainThreadAsync(^{
1724         [view addGLContext:context];
1725     });
1727     [pool release];
1730 /***********************************************************************
1731  *              macdrv_remove_view_opengl_context
1733  * Add an OpenGL context to the list being tracked for each view.
1734  */
1735 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1737     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1738     WineContentView* view = (WineContentView*)v;
1739     WineOpenGLContext *context = (WineOpenGLContext*)c;
1741     OnMainThreadAsync(^{
1742         [view removeGLContext:context];
1743     });
1745     [pool release];