winemac: Ignore window frame changes while minimized but check again when unminimized.
[wine/multimedia.git] / dlls / winemac.drv / cocoa_window.m
blob9d8984cea2a4fa59844143d8bae439521f451973
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 <NSTextInputClient>
123     NSMutableArray* glContexts;
124     NSMutableArray* pendingGlContexts;
126     NSMutableAttributedString* markedText;
127     NSRange markedTextSelection;
130     - (void) addGLContext:(WineOpenGLContext*)context;
131     - (void) removeGLContext:(WineOpenGLContext*)context;
132     - (void) updateGLContexts;
134 @end
137 @interface WineWindow ()
139 @property (nonatomic) BOOL disabled;
140 @property (nonatomic) BOOL noActivate;
141 @property (readwrite, nonatomic) BOOL floating;
142 @property (retain, nonatomic) NSWindow* latentParentWindow;
144 @property (nonatomic) void* hwnd;
145 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
147 @property (nonatomic) void* surface;
148 @property (nonatomic) pthread_mutex_t* surface_mutex;
150 @property (copy, nonatomic) NSBezierPath* shape;
151 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
152 @property (readonly, nonatomic) BOOL needsTransparency;
154 @property (nonatomic) BOOL colorKeyed;
155 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
156 @property (nonatomic) BOOL usePerPixelAlpha;
158 @property (assign, nonatomic) void* imeData;
159 @property (nonatomic) BOOL commandDone;
161 @end
164 @implementation WineContentView
166     - (void) dealloc
167     {
168         [markedText release];
169         [glContexts release];
170         [pendingGlContexts release];
171         [super dealloc];
172     }
174     - (BOOL) isFlipped
175     {
176         return YES;
177     }
179     - (void) drawRect:(NSRect)rect
180     {
181         WineWindow* window = (WineWindow*)[self window];
183         for (WineOpenGLContext* context in pendingGlContexts)
184             context.needsUpdate = TRUE;
185         [glContexts addObjectsFromArray:pendingGlContexts];
186         [pendingGlContexts removeAllObjects];
188         if ([window contentView] != self)
189             return;
191         if (window.surface && window.surface_mutex &&
192             !pthread_mutex_lock(window.surface_mutex))
193         {
194             const CGRect* rects;
195             int count;
197             if (get_surface_blit_rects(window.surface, &rects, &count) && count)
198             {
199                 CGContextRef context;
200                 int i;
202                 [window.shape addClip];
204                 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
205                 CGContextSetBlendMode(context, kCGBlendModeCopy);
207                 for (i = 0; i < count; i++)
208                 {
209                     CGRect imageRect;
210                     CGImageRef image;
212                     imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
213                     image = create_surface_image(window.surface, &imageRect, FALSE);
215                     if (image)
216                     {
217                         if (window.colorKeyed)
218                         {
219                             CGImageRef maskedImage;
220                             CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
221                                                      window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
222                                                      window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
223                             maskedImage = CGImageCreateWithMaskingColors(image, components);
224                             if (maskedImage)
225                             {
226                                 CGImageRelease(image);
227                                 image = maskedImage;
228                             }
229                         }
231                         CGContextDrawImage(context, imageRect, image);
233                         CGImageRelease(image);
234                     }
235                 }
236             }
238             pthread_mutex_unlock(window.surface_mutex);
239         }
241         // If the window may be transparent, then we have to invalidate the
242         // shadow every time we draw.  Also, if this is the first time we've
243         // drawn since changing from transparent to opaque.
244         if (![window isOpaque] || window.shapeChangedSinceLastDraw)
245         {
246             window.shapeChangedSinceLastDraw = FALSE;
247             [window invalidateShadow];
248         }
249     }
251     - (void) addGLContext:(WineOpenGLContext*)context
252     {
253         if (!glContexts)
254             glContexts = [[NSMutableArray alloc] init];
255         if (!pendingGlContexts)
256             pendingGlContexts = [[NSMutableArray alloc] init];
257         [pendingGlContexts addObject:context];
258         [self setNeedsDisplay:YES];
259     }
261     - (void) removeGLContext:(WineOpenGLContext*)context
262     {
263         [glContexts removeObjectIdenticalTo:context];
264         [pendingGlContexts removeObjectIdenticalTo:context];
265     }
267     - (void) updateGLContexts
268     {
269         for (WineOpenGLContext* context in glContexts)
270             context.needsUpdate = TRUE;
271     }
273     - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
274     {
275         return YES;
276     }
278     - (BOOL) preservesContentDuringLiveResize
279     {
280         // Returning YES from this tells Cocoa to keep our view's content during
281         // a Cocoa-driven resize.  In theory, we're also supposed to override
282         // -setFrameSize: to mark exposed sections as needing redisplay, but
283         // user32 will take care of that in a roundabout way.  This way, we don't
284         // redraw until the window surface is flushed.
285         //
286         // This doesn't do anything when we resize the window ourselves.
287         return YES;
288     }
290     - (BOOL)acceptsFirstResponder
291     {
292         return [[self window] contentView] == self;
293     }
295     - (void) completeText:(NSString*)text
296     {
297         macdrv_event* event;
298         WineWindow* window = (WineWindow*)[self window];
300         event = macdrv_create_event(IM_SET_TEXT, window);
301         event->im_set_text.data = [window imeData];
302         event->im_set_text.text = (CFStringRef)[text copy];
303         event->im_set_text.complete = TRUE;
305         [[window queue] postEvent:event];
307         macdrv_release_event(event);
309         [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
310         markedTextSelection = NSMakeRange(0, 0);
311         [[self inputContext] discardMarkedText];
312     }
314     /*
315      * ---------- NSTextInputClient methods ----------
316      */
317     - (NSTextInputContext*) inputContext
318     {
319         if (!markedText)
320             markedText = [[NSMutableAttributedString alloc] init];
321         return [super inputContext];
322     }
324     - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
325     {
326         if ([string isKindOfClass:[NSAttributedString class]])
327             string = [string string];
329         if ([string isKindOfClass:[NSString class]])
330             [self completeText:string];
331     }
333     - (void) doCommandBySelector:(SEL)aSelector
334     {
335         [(WineWindow*)[self window] setCommandDone:TRUE];
336     }
338     - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
339     {
340         if ([string isKindOfClass:[NSAttributedString class]])
341             string = [string string];
343         if ([string isKindOfClass:[NSString class]])
344         {
345             macdrv_event* event;
346             WineWindow* window = (WineWindow*)[self window];
348             if (replacementRange.location == NSNotFound)
349                 replacementRange = NSMakeRange(0, [markedText length]);
351             [markedText replaceCharactersInRange:replacementRange withString:string];
352             markedTextSelection = selectedRange;
353             markedTextSelection.location += replacementRange.location;
355             event = macdrv_create_event(IM_SET_TEXT, window);
356             event->im_set_text.data = [window imeData];
357             event->im_set_text.text = (CFStringRef)[[markedText string] copy];
358             event->im_set_text.complete = FALSE;
359             event->im_set_text.cursor_pos = markedTextSelection.location;
361             [[window queue] postEvent:event];
363             macdrv_release_event(event);
365             [[self inputContext] invalidateCharacterCoordinates];
366         }
367     }
369     - (void) unmarkText
370     {
371         [self completeText:nil];
372     }
374     - (NSRange) selectedRange
375     {
376         return markedTextSelection;
377     }
379     - (NSRange) markedRange
380     {
381         NSRange range = NSMakeRange(0, [markedText length]);
382         if (!range.length)
383             range.location = NSNotFound;
384         return range;
385     }
387     - (BOOL) hasMarkedText
388     {
389         return [markedText length] > 0;
390     }
392     - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
393     {
394         if (aRange.location >= [markedText length])
395             return nil;
397         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
398         if (actualRange)
399             *actualRange = aRange;
400         return [markedText attributedSubstringFromRange:aRange];
401     }
403     - (NSArray*) validAttributesForMarkedText
404     {
405         return [NSArray array];
406     }
408     - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
409     {
410         macdrv_query* query;
411         WineWindow* window = (WineWindow*)[self window];
412         NSRect ret;
414         aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
416         query = macdrv_create_query();
417         query->type = QUERY_IME_CHAR_RECT;
418         query->window = (macdrv_window)[window retain];
419         query->ime_char_rect.data = [window imeData];
420         query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
422         if ([window.queue query:query timeout:1])
423         {
424             aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
425             ret = NSRectFromCGRect(query->ime_char_rect.rect);
426             [[WineApplicationController sharedController] flipRect:&ret];
427         }
428         else
429             ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
431         macdrv_release_query(query);
433         if (actualRange)
434             *actualRange = aRange;
435         return ret;
436     }
438     - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
439     {
440         return NSNotFound;
441     }
443     - (NSInteger) windowLevel
444     {
445         return [[self window] level];
446     }
448 @end
451 @implementation WineWindow
453     @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
454     @synthesize surface, surface_mutex;
455     @synthesize shape, shapeChangedSinceLastDraw;
456     @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
457     @synthesize usePerPixelAlpha;
458     @synthesize imeData, commandDone;
460     + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
461                                  windowFrame:(NSRect)window_frame
462                                         hwnd:(void*)hwnd
463                                        queue:(WineEventQueue*)queue
464     {
465         WineWindow* window;
466         WineContentView* contentView;
467         NSTrackingArea* trackingArea;
469         [[WineApplicationController sharedController] flipRect:&window_frame];
471         window = [[[self alloc] initWithContentRect:window_frame
472                                           styleMask:style_mask_for_features(wf)
473                                             backing:NSBackingStoreBuffered
474                                               defer:YES] autorelease];
476         if (!window) return nil;
478         /* Standardize windows to eliminate differences between titled and
479            borderless windows and between NSWindow and NSPanel. */
480         [window setHidesOnDeactivate:NO];
481         [window setReleasedWhenClosed:NO];
483         [window disableCursorRects];
484         [window setShowsResizeIndicator:NO];
485         [window setHasShadow:wf->shadow];
486         [window setAcceptsMouseMovedEvents:YES];
487         [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
488         [window setDelegate:window];
489         window.hwnd = hwnd;
490         window.queue = queue;
492         [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
493                                                                   (NSString*)kUTTypeContent,
494                                                                   nil]];
496         contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
497         if (!contentView)
498             return nil;
499         [contentView setAutoresizesSubviews:NO];
501         /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
502            because they give us mouse moves in the background. */
503         trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
504                                                      options:(NSTrackingMouseMoved |
505                                                               NSTrackingActiveAlways |
506                                                               NSTrackingInVisibleRect)
507                                                        owner:window
508                                                     userInfo:nil] autorelease];
509         if (!trackingArea)
510             return nil;
511         [contentView addTrackingArea:trackingArea];
513         [window setContentView:contentView];
514         [window setInitialFirstResponder:contentView];
516         return window;
517     }
519     - (void) dealloc
520     {
521         [liveResizeDisplayTimer invalidate];
522         [liveResizeDisplayTimer release];
523         [queue release];
524         [latentParentWindow release];
525         [shape release];
526         [super dealloc];
527     }
529     - (void) adjustFeaturesForState
530     {
531         NSUInteger style = [self styleMask];
533         if (style & NSClosableWindowMask)
534             [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
535         if (style & NSMiniaturizableWindowMask)
536             [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
537         if (style & NSResizableWindowMask)
538             [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
539     }
541     - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
542     {
543         NSUInteger currentStyle = [self styleMask];
544         NSUInteger newStyle = style_mask_for_features(wf);
546         if (newStyle != currentStyle)
547         {
548             BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
549             BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
550             if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
551             {
552                 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
553                 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
554                 // just NSTitledWindowMask, the window buttons should disappear rather
555                 // than just being disabled.  But they don't.  Similarly in reverse.
556                 // The workaround is to also toggle NSClosableWindowMask at the same time.
557                 [self setStyleMask:newStyle ^ NSClosableWindowMask];
558             }
559             [self setStyleMask:newStyle];
560         }
562         [self adjustFeaturesForState];
563         [self setHasShadow:wf->shadow];
564     }
566     - (BOOL) isOrderedIn
567     {
568         return [self isVisible] || [self isMiniaturized];
569     }
571     - (NSInteger) minimumLevelForActive:(BOOL)active
572     {
573         NSScreen* screen;
574         BOOL fullscreen;
575         NSInteger level;
577         screen = screen_covered_by_rect([self frame], [NSScreen screens]);
578         fullscreen = (screen != nil);
580         if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
581                               (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
582             level = NSFloatingWindowLevel;
583         else
584             level = NSNormalWindowLevel;
586         if (active)
587         {
588             BOOL captured;
590             captured = (screen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
592             if (captured || fullscreen)
593             {
594                 if (captured)
595                     level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
596                 else
597                     level = NSMainMenuWindowLevel + 1;
599                 if (self.floating)
600                     level++;
601             }
602         }
604         return level;
605     }
607     - (void) setMacDrvState:(const struct macdrv_window_state*)state
608     {
609         NSWindowCollectionBehavior behavior;
611         self.disabled = state->disabled;
612         self.noActivate = state->no_activate;
614         if (self.floating != state->floating)
615         {
616             self.floating = state->floating;
617             [[WineApplicationController sharedController] adjustWindowLevels];
618         }
620         behavior = NSWindowCollectionBehaviorDefault;
621         if (state->excluded_by_expose)
622             behavior |= NSWindowCollectionBehaviorTransient;
623         else
624             behavior |= NSWindowCollectionBehaviorManaged;
625         if (state->excluded_by_cycle)
626         {
627             behavior |= NSWindowCollectionBehaviorIgnoresCycle;
628             if ([self isOrderedIn])
629                 [NSApp removeWindowsItem:self];
630         }
631         else
632         {
633             behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
634             if ([self isOrderedIn])
635                 [NSApp addWindowsItem:self title:[self title] filename:NO];
636         }
637         [self setCollectionBehavior:behavior];
639         pendingMinimize = FALSE;
640         if (state->minimized && ![self isMiniaturized])
641         {
642             if ([self isVisible])
643             {
644                 ignore_windowMiniaturize = TRUE;
645                 [self miniaturize:nil];
646             }
647             else
648                 pendingMinimize = TRUE;
649         }
650         else if (!state->minimized && [self isMiniaturized])
651         {
652             ignore_windowDeminiaturize = TRUE;
653             [self deminiaturize:nil];
654         }
656         /* Whatever events regarding minimization might have been in the queue are now stale. */
657         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
658                                          event_mask_for_type(WINDOW_DID_UNMINIMIZE)
659                                forWindow:self];
660     }
662     // Determine if, among Wine windows, this window is directly above or below
663     // a given other Wine window with no other Wine window intervening.
664     // Intervening non-Wine windows are ignored.
665     - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
666     {
667         NSNumber* windowNumber;
668         NSNumber* otherWindowNumber;
669         NSArray* windowNumbers;
670         NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
672         if (![self isVisible] || ![otherWindow isVisible])
673             return FALSE;
675         windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
676         otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
677         windowNumbers = [[self class] windowNumbersWithOptions:0];
678         windowIndex = [windowNumbers indexOfObject:windowNumber];
679         otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
681         if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
682             return FALSE;
684         if (orderingMode == NSWindowAbove)
685         {
686             lowIndex = windowIndex;
687             highIndex = otherWindowIndex;
688         }
689         else if (orderingMode == NSWindowBelow)
690         {
691             lowIndex = otherWindowIndex;
692             highIndex = windowIndex;
693         }
694         else
695             return FALSE;
697         if (highIndex <= lowIndex)
698             return FALSE;
700         for (i = lowIndex + 1; i < highIndex; i++)
701         {
702             NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
703             NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
704             if ([interveningWindow isKindOfClass:[WineWindow class]])
705                 return FALSE;
706         }
708         return TRUE;
709     }
711     /* Returns whether or not the window was ordered in, which depends on if
712        its frame intersects any screen. */
713     - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
714     {
715         WineApplicationController* controller = [WineApplicationController sharedController];
716         BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
717         if (on_screen && ![self isMiniaturized])
718         {
719             BOOL needAdjustWindowLevels = FALSE;
721             [controller transformProcessToForeground];
723             NSDisableScreenUpdates();
725             if (latentParentWindow)
726             {
727                 if ([latentParentWindow level] > [self level])
728                     [self setLevel:[latentParentWindow level]];
729                 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
730                 self.latentParentWindow = nil;
731                 needAdjustWindowLevels = TRUE;
732             }
733             if (prev || next)
734             {
735                 WineWindow* other = [prev isVisible] ? prev : next;
736                 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
738                 if (![self isOrdered:orderingMode relativeTo:other])
739                 {
740                     // This window level may not be right for this window based
741                     // on floating-ness, fullscreen-ness, etc.  But we set it
742                     // temporarily to allow us to order the windows properly.
743                     // Then the levels get fixed by -adjustWindowLevels.
744                     if ([self level] != [other level])
745                         [self setLevel:[other level]];
746                     [self orderWindow:orderingMode relativeTo:[other windowNumber]];
747                     needAdjustWindowLevels = TRUE;
748                 }
749             }
750             else
751             {
752                 // Again, temporarily set level to make sure we can order to
753                 // the right place.
754                 next = [controller frontWineWindow];
755                 if (next && [self level] < [next level])
756                     [self setLevel:[next level]];
757                 [self orderFront:nil];
758                 needAdjustWindowLevels = TRUE;
759             }
760             if (needAdjustWindowLevels)
761                 [controller adjustWindowLevels];
763             if (pendingMinimize)
764             {
765                 ignore_windowMiniaturize = TRUE;
766                 [self miniaturize:nil];
767                 pendingMinimize = FALSE;
768             }
770             NSEnableScreenUpdates();
772             /* Cocoa may adjust the frame when the window is ordered onto the screen.
773                Generate a frame-changed event just in case.  The back end will ignore
774                it if nothing actually changed. */
775             [self windowDidResize:nil];
777             if (![self isExcludedFromWindowsMenu])
778                 [NSApp addWindowsItem:self title:[self title] filename:NO];
779         }
781         return on_screen;
782     }
784     - (void) doOrderOut
785     {
786         if ([self isMiniaturized])
787             pendingMinimize = TRUE;
788         self.latentParentWindow = [self parentWindow];
789         [latentParentWindow removeChildWindow:self];
790         [self orderOut:nil];
791         [[WineApplicationController sharedController] adjustWindowLevels];
792         [NSApp removeWindowsItem:self];
793     }
795     - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
796     {
797         NSArray* screens = [NSScreen screens];
798         BOOL on_screen = [self isOrderedIn];
800         if (![screens count]) return on_screen;
802         /* Origin is (left, top) in a top-down space.  Need to convert it to
803            (left, bottom) in a bottom-up space. */
804         [[WineApplicationController sharedController] flipRect:&contentRect];
806         if (on_screen)
807         {
808             on_screen = frame_intersects_screens(contentRect, screens);
809             if (!on_screen)
810                 [self doOrderOut];
811         }
813         /* The back end is establishing a new window size and position.  It's
814            not interested in any stale events regarding those that may be sitting
815            in the queue. */
816         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
817                                forWindow:self];
819         if (!NSIsEmptyRect(contentRect))
820         {
821             NSRect frame, oldFrame;
823             oldFrame = [self frame];
824             frame = [self frameRectForContentRect:contentRect];
825             if (!NSEqualRects(frame, oldFrame))
826             {
827                 if (NSEqualSizes(frame.size, oldFrame.size))
828                     [self setFrameOrigin:frame.origin];
829                 else
830                     [self setFrame:frame display:YES];
832                 if (on_screen)
833                 {
834                     BOOL fullscreen = (screen_covered_by_rect(frame, screens) != nil);
835                     BOOL oldFullscreen = (screen_covered_by_rect(oldFrame, screens) != nil);
837                     if (fullscreen != oldFullscreen)
838                         [[WineApplicationController sharedController] adjustWindowLevels];
840                     /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
841                        event.  The back end will ignore it if nothing actually changed. */
842                     [self windowDidResize:nil];
843                 }
844             }
845         }
847         return on_screen;
848     }
850     - (void) setMacDrvParentWindow:(WineWindow*)parent
851     {
852         if ([self parentWindow] != parent)
853         {
854             [[self parentWindow] removeChildWindow:self];
855             self.latentParentWindow = nil;
856             if ([self isVisible] && parent)
857             {
858                 if ([parent level] > [self level])
859                     [self setLevel:[parent level]];
860                 [parent addChildWindow:self ordered:NSWindowAbove];
861                 [[WineApplicationController sharedController] adjustWindowLevels];
862             }
863             else
864                 self.latentParentWindow = parent;
865         }
866     }
868     - (void) setDisabled:(BOOL)newValue
869     {
870         if (disabled != newValue)
871         {
872             disabled = newValue;
873             [self adjustFeaturesForState];
875             if (disabled)
876             {
877                 NSSize size = [self frame].size;
878                 [self setMinSize:size];
879                 [self setMaxSize:size];
880             }
881             else
882             {
883                 [self setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
884                 [self setMinSize:NSZeroSize];
885             }
886         }
887     }
889     - (BOOL) needsTransparency
890     {
891         return self.shape || self.colorKeyed || self.usePerPixelAlpha;
892     }
894     - (void) checkTransparency
895     {
896         if (![self isOpaque] && !self.needsTransparency)
897         {
898             [self setBackgroundColor:[NSColor windowBackgroundColor]];
899             [self setOpaque:YES];
900         }
901         else if ([self isOpaque] && self.needsTransparency)
902         {
903             [self setBackgroundColor:[NSColor clearColor]];
904             [self setOpaque:NO];
905         }
906     }
908     - (void) setShape:(NSBezierPath*)newShape
909     {
910         if (shape == newShape) return;
911         if (shape && newShape && [shape isEqual:newShape]) return;
913         if (shape)
914         {
915             [[self contentView] setNeedsDisplayInRect:[shape bounds]];
916             [shape release];
917         }
918         if (newShape)
919             [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
921         shape = [newShape copy];
922         self.shapeChangedSinceLastDraw = TRUE;
924         [self checkTransparency];
925     }
927     - (void) makeFocused:(BOOL)activate
928     {
929         WineApplicationController* controller = [WineApplicationController sharedController];
930         NSArray* screens;
931         WineWindow* front;
933         [controller transformProcessToForeground];
935         /* If a borderless window is offscreen, orderFront: won't move
936            it onscreen like it would for a titled window.  Do that ourselves. */
937         screens = [NSScreen screens];
938         if (!([self styleMask] & NSTitledWindowMask) && ![self isOrderedIn] &&
939             !frame_intersects_screens([self frame], screens))
940         {
941             NSScreen* primaryScreen = [screens objectAtIndex:0];
942             NSRect frame = [primaryScreen frame];
943             [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
944             frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
945             [self setFrame:frame display:YES];
946         }
948         if (activate)
949             [NSApp activateIgnoringOtherApps:YES];
951         NSDisableScreenUpdates();
953         if (latentParentWindow)
954         {
955             if ([latentParentWindow level] > [self level])
956                 [self setLevel:[latentParentWindow level]];
957             [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
958             self.latentParentWindow = nil;
959         }
960         front = [controller frontWineWindow];
961         if (front && [self level] < [front level])
962             [self setLevel:[front level]];
963         [self orderFront:nil];
964         [controller adjustWindowLevels];
966         if (pendingMinimize)
967         {
968             ignore_windowMiniaturize = TRUE;
969             [self miniaturize:nil];
970             pendingMinimize = FALSE;
971         }
973         NSEnableScreenUpdates();
975         causing_becomeKeyWindow = TRUE;
976         [self makeKeyWindow];
977         causing_becomeKeyWindow = FALSE;
979         if (![self isExcludedFromWindowsMenu])
980             [NSApp addWindowsItem:self title:[self title] filename:NO];
982         /* Cocoa may adjust the frame when the window is ordered onto the screen.
983            Generate a frame-changed event just in case.  The back end will ignore
984            it if nothing actually changed. */
985         [self windowDidResize:nil];
986     }
988     - (void) postKey:(uint16_t)keyCode
989              pressed:(BOOL)pressed
990            modifiers:(NSUInteger)modifiers
991                event:(NSEvent*)theEvent
992     {
993         macdrv_event* event;
994         CGEventRef cgevent;
995         WineApplicationController* controller = [WineApplicationController sharedController];
997         event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
998         event->key.keycode   = keyCode;
999         event->key.modifiers = modifiers;
1000         event->key.time_ms   = [controller ticksForEventTime:[theEvent timestamp]];
1002         if ((cgevent = [theEvent CGEvent]))
1003         {
1004             CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1005                                                         kCGKeyboardEventKeyboardType);
1006             if (keyboardType != controller.keyboardType)
1007             {
1008                 controller.keyboardType = keyboardType;
1009                 [controller keyboardSelectionDidChange];
1010             }
1011         }
1013         [queue postEvent:event];
1015         macdrv_release_event(event);
1016     }
1018     - (void) postKeyEvent:(NSEvent *)theEvent
1019     {
1020         [self flagsChanged:theEvent];
1021         [self postKey:[theEvent keyCode]
1022               pressed:[theEvent type] == NSKeyDown
1023             modifiers:[theEvent modifierFlags]
1024                 event:theEvent];
1025     }
1028     /*
1029      * ---------- NSWindow method overrides ----------
1030      */
1031     - (BOOL) canBecomeKeyWindow
1032     {
1033         if (causing_becomeKeyWindow) return YES;
1034         if (self.disabled || self.noActivate) return NO;
1035         return [self isKeyWindow];
1036     }
1038     - (BOOL) canBecomeMainWindow
1039     {
1040         return [self canBecomeKeyWindow];
1041     }
1043     - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1044     {
1045         // If a window is sized to completely cover a screen, then it's in
1046         // full-screen mode.  In that case, we don't allow NSWindow to constrain
1047         // it.
1048         NSRect contentRect = [self contentRectForFrameRect:frameRect];
1049         if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
1050             frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1051         return frameRect;
1052     }
1054     - (BOOL) isExcludedFromWindowsMenu
1055     {
1056         return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1057     }
1059     - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1060     {
1061         BOOL ret = [super validateMenuItem:menuItem];
1063         if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1064             ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1066         return ret;
1067     }
1069     /* We don't call this.  It's the action method of the items in the Window menu. */
1070     - (void) makeKeyAndOrderFront:(id)sender
1071     {
1072         WineApplicationController* controller = [WineApplicationController sharedController];
1073         WineWindow* front = [controller frontWineWindow];
1075         if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1076             [controller windowGotFocus:self];
1078         if (front && [self level] < [front level])
1079             [self setLevel:[front level]];
1080         [self orderFront:nil];
1081         [controller adjustWindowLevels];
1083         if (pendingMinimize)
1084         {
1085             ignore_windowMiniaturize = TRUE;
1086             [self miniaturize:nil];
1087             pendingMinimize = FALSE;
1088         }
1089     }
1091     - (void) sendEvent:(NSEvent*)event
1092     {
1093         WineApplicationController* controller = [WineApplicationController sharedController];
1095         /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1096            interface control.  For example, Control-Tab switches focus among
1097            views.  We want to bypass that feature, so directly route key-down
1098            events to -keyDown:. */
1099         if ([event type] == NSKeyDown)
1100             [[self firstResponder] keyDown:event];
1101         else
1102         {
1103             if ([event type] == NSLeftMouseDown)
1104             {
1105                 /* Since our windows generally claim they can't be made key, clicks
1106                    in their title bars are swallowed by the theme frame stuff.  So,
1107                    we hook directly into the event stream and assume that any click
1108                    in the window will activate it, if Wine and the Win32 program
1109                    accept. */
1110                 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1111                     [controller windowGotFocus:self];
1112             }
1114             [super sendEvent:event];
1115         }
1116     }
1119     /*
1120      * ---------- NSResponder method overrides ----------
1121      */
1122     - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1123     - (void) keyUp:(NSEvent *)theEvent   { [self postKeyEvent:theEvent]; }
1125     - (void) flagsChanged:(NSEvent *)theEvent
1126     {
1127         static const struct {
1128             NSUInteger  mask;
1129             uint16_t    keycode;
1130         } modifiers[] = {
1131             { NX_ALPHASHIFTMASK,        kVK_CapsLock },
1132             { NX_DEVICELSHIFTKEYMASK,   kVK_Shift },
1133             { NX_DEVICERSHIFTKEYMASK,   kVK_RightShift },
1134             { NX_DEVICELCTLKEYMASK,     kVK_Control },
1135             { NX_DEVICERCTLKEYMASK,     kVK_RightControl },
1136             { NX_DEVICELALTKEYMASK,     kVK_Option },
1137             { NX_DEVICERALTKEYMASK,     kVK_RightOption },
1138             { NX_DEVICELCMDKEYMASK,     kVK_Command },
1139             { NX_DEVICERCMDKEYMASK,     kVK_RightCommand },
1140         };
1142         NSUInteger modifierFlags = [theEvent modifierFlags];
1143         NSUInteger changed;
1144         int i, last_changed;
1146         fix_device_modifiers_by_generic(&modifierFlags);
1147         changed = modifierFlags ^ lastModifierFlags;
1149         last_changed = -1;
1150         for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1151             if (changed & modifiers[i].mask)
1152                 last_changed = i;
1154         for (i = 0; i <= last_changed; i++)
1155         {
1156             if (changed & modifiers[i].mask)
1157             {
1158                 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1160                 if (i == last_changed)
1161                     lastModifierFlags = modifierFlags;
1162                 else
1163                 {
1164                     lastModifierFlags ^= modifiers[i].mask;
1165                     fix_generic_modifiers_by_device(&lastModifierFlags);
1166                 }
1168                 // Caps lock generates one event for each press-release action.
1169                 // We need to simulate a pair of events for each actual event.
1170                 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1171                 {
1172                     [self postKey:modifiers[i].keycode
1173                           pressed:TRUE
1174                         modifiers:lastModifierFlags
1175                             event:(NSEvent*)theEvent];
1176                     pressed = FALSE;
1177                 }
1179                 [self postKey:modifiers[i].keycode
1180                       pressed:pressed
1181                     modifiers:lastModifierFlags
1182                         event:(NSEvent*)theEvent];
1183             }
1184         }
1185     }
1188     /*
1189      * ---------- NSWindowDelegate methods ----------
1190      */
1191     - (void)windowDidBecomeKey:(NSNotification *)notification
1192     {
1193         WineApplicationController* controller = [WineApplicationController sharedController];
1194         NSEvent* event = [controller lastFlagsChanged];
1195         if (event)
1196             [self flagsChanged:event];
1198         if (causing_becomeKeyWindow) return;
1200         [controller windowGotFocus:self];
1201     }
1203     - (void)windowDidDeminiaturize:(NSNotification *)notification
1204     {
1205         WineApplicationController* controller = [WineApplicationController sharedController];
1207         if (!ignore_windowDeminiaturize)
1208         {
1209             macdrv_event* event;
1211             /* Coalesce events by discarding any previous ones still in the queue. */
1212             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1213                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1214                                    forWindow:self];
1216             event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1217             [queue postEvent:event];
1218             macdrv_release_event(event);
1219         }
1221         ignore_windowDeminiaturize = FALSE;
1223         [controller adjustWindowLevels];
1225         if (!self.disabled && !self.noActivate)
1226         {
1227             causing_becomeKeyWindow = TRUE;
1228             [self makeKeyWindow];
1229             causing_becomeKeyWindow = FALSE;
1230             [controller windowGotFocus:self];
1231         }
1233         [self windowDidResize:notification];
1234     }
1236     - (void) windowDidEndLiveResize:(NSNotification *)notification
1237     {
1238         [liveResizeDisplayTimer invalidate];
1239         [liveResizeDisplayTimer release];
1240         liveResizeDisplayTimer = nil;
1241     }
1243     - (void)windowDidMove:(NSNotification *)notification
1244     {
1245         [self windowDidResize:notification];
1246     }
1248     - (void)windowDidResignKey:(NSNotification *)notification
1249     {
1250         macdrv_event* event;
1252         if (causing_becomeKeyWindow) return;
1254         event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1255         [queue postEvent:event];
1256         macdrv_release_event(event);
1257     }
1259     - (void)windowDidResize:(NSNotification *)notification
1260     {
1261         macdrv_event* event;
1262         NSRect frame = [self frame];
1264         if (self.disabled)
1265         {
1266             NSSize size = frame.size;
1267             [self setMinSize:size];
1268             [self setMaxSize:size];
1269         }
1271         frame = [self contentRectForFrameRect:frame];
1272         [[WineApplicationController sharedController] flipRect:&frame];
1274         /* Coalesce events by discarding any previous ones still in the queue. */
1275         [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1276                                forWindow:self];
1278         event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1279         event->window_frame_changed.frame = NSRectToCGRect(frame);
1280         [queue postEvent:event];
1281         macdrv_release_event(event);
1283         [[[self contentView] inputContext] invalidateCharacterCoordinates];
1284     }
1286     - (BOOL)windowShouldClose:(id)sender
1287     {
1288         macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1289         [queue postEvent:event];
1290         macdrv_release_event(event);
1291         return NO;
1292     }
1294     - (void)windowWillMiniaturize:(NSNotification *)notification
1295     {
1296         if (!ignore_windowMiniaturize)
1297         {
1298             macdrv_event* event;
1300             /* Coalesce events by discarding any previous ones still in the queue. */
1301             [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1302                                              event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1303                                    forWindow:self];
1305             event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
1306             [queue postEvent:event];
1307             macdrv_release_event(event);
1308         }
1310         ignore_windowMiniaturize = FALSE;
1311     }
1313     - (void) windowWillStartLiveResize:(NSNotification *)notification
1314     {
1315         // There's a strange restriction in window redrawing during Cocoa-
1316         // managed window resizing.  Only calls to -[NSView setNeedsDisplay...]
1317         // that happen synchronously when Cocoa tells us that our window size
1318         // has changed or asynchronously in a short interval thereafter provoke
1319         // the window to redraw.  Calls to those methods that happen asynchronously
1320         // a half second or more after the last change of the window size aren't
1321         // heeded until the next resize-related user event (e.g. mouse movement).
1322         //
1323         // Wine often has a significant delay between when it's been told that
1324         // the window has changed size and when it can flush completed drawing.
1325         // So, our windows would get stuck with incomplete drawing for as long
1326         // as the user holds the mouse button down and doesn't move it.
1327         //
1328         // We address this by "manually" asking our windows to check if they need
1329         // redrawing every so often (during live resize only).
1330         [self windowDidEndLiveResize:nil];
1331         liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1332                                                                   target:self
1333                                                                 selector:@selector(displayIfNeeded)
1334                                                                 userInfo:nil
1335                                                                  repeats:YES];
1336         [liveResizeDisplayTimer retain];
1337         [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1338                                      forMode:NSRunLoopCommonModes];
1339     }
1342     /*
1343      * ---------- NSPasteboardOwner methods ----------
1344      */
1345     - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1346     {
1347         macdrv_query* query = macdrv_create_query();
1348         query->type = QUERY_PASTEBOARD_DATA;
1349         query->window = (macdrv_window)[self retain];
1350         query->pasteboard_data.type = (CFStringRef)[type copy];
1352         [self.queue query:query timeout:3];
1353         macdrv_release_query(query);
1354     }
1357     /*
1358      * ---------- NSDraggingDestination methods ----------
1359      */
1360     - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1361     {
1362         return [self draggingUpdated:sender];
1363     }
1365     - (void) draggingExited:(id <NSDraggingInfo>)sender
1366     {
1367         // This isn't really a query.  We don't need any response.  However, it
1368         // has to be processed in a similar manner as the other drag-and-drop
1369         // queries in order to maintain the proper order of operations.
1370         macdrv_query* query = macdrv_create_query();
1371         query->type = QUERY_DRAG_EXITED;
1372         query->window = (macdrv_window)[self retain];
1374         [self.queue query:query timeout:0.1];
1375         macdrv_release_query(query);
1376     }
1378     - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1379     {
1380         NSDragOperation ret;
1381         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1382         NSPasteboard* pb = [sender draggingPasteboard];
1384         macdrv_query* query = macdrv_create_query();
1385         query->type = QUERY_DRAG_OPERATION;
1386         query->window = (macdrv_window)[self retain];
1387         query->drag_operation.x = pt.x;
1388         query->drag_operation.y = pt.y;
1389         query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1390         query->drag_operation.accepted_op = NSDragOperationNone;
1391         query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1393         [self.queue query:query timeout:3];
1394         ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1395         macdrv_release_query(query);
1397         return ret;
1398     }
1400     - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1401     {
1402         BOOL ret;
1403         NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1404         NSPasteboard* pb = [sender draggingPasteboard];
1406         macdrv_query* query = macdrv_create_query();
1407         query->type = QUERY_DRAG_DROP;
1408         query->window = (macdrv_window)[self retain];
1409         query->drag_drop.x = pt.x;
1410         query->drag_drop.y = pt.y;
1411         query->drag_drop.op = [sender draggingSourceOperationMask];
1412         query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1414         [self.queue query:query timeout:3 * 60 processEvents:YES];
1415         ret = query->status;
1416         macdrv_release_query(query);
1418         return ret;
1419     }
1421     - (BOOL) wantsPeriodicDraggingUpdates
1422     {
1423         return NO;
1424     }
1426 @end
1429 /***********************************************************************
1430  *              macdrv_create_cocoa_window
1432  * Create a Cocoa window with the given content frame and features (e.g.
1433  * title bar, close box, etc.).
1434  */
1435 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1436         CGRect frame, void* hwnd, macdrv_event_queue queue)
1438     __block WineWindow* window;
1440     OnMainThread(^{
1441         window = [[WineWindow createWindowWithFeatures:wf
1442                                            windowFrame:NSRectFromCGRect(frame)
1443                                                   hwnd:hwnd
1444                                                  queue:(WineEventQueue*)queue] retain];
1445     });
1447     return (macdrv_window)window;
1450 /***********************************************************************
1451  *              macdrv_destroy_cocoa_window
1453  * Destroy a Cocoa window.
1454  */
1455 void macdrv_destroy_cocoa_window(macdrv_window w)
1457     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1458     WineWindow* window = (WineWindow*)w;
1460     [window.queue discardEventsMatchingMask:-1 forWindow:window];
1461     [window close];
1462     [window release];
1464     [pool release];
1467 /***********************************************************************
1468  *              macdrv_get_window_hwnd
1470  * Get the hwnd that was set for the window at creation.
1471  */
1472 void* macdrv_get_window_hwnd(macdrv_window w)
1474     WineWindow* window = (WineWindow*)w;
1475     return window.hwnd;
1478 /***********************************************************************
1479  *              macdrv_set_cocoa_window_features
1481  * Update a Cocoa window's features.
1482  */
1483 void macdrv_set_cocoa_window_features(macdrv_window w,
1484         const struct macdrv_window_features* wf)
1486     WineWindow* window = (WineWindow*)w;
1488     OnMainThread(^{
1489         [window setWindowFeatures:wf];
1490     });
1493 /***********************************************************************
1494  *              macdrv_set_cocoa_window_state
1496  * Update a Cocoa window's state.
1497  */
1498 void macdrv_set_cocoa_window_state(macdrv_window w,
1499         const struct macdrv_window_state* state)
1501     WineWindow* window = (WineWindow*)w;
1503     OnMainThread(^{
1504         [window setMacDrvState:state];
1505     });
1508 /***********************************************************************
1509  *              macdrv_set_cocoa_window_title
1511  * Set a Cocoa window's title.
1512  */
1513 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1514         size_t length)
1516     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1517     WineWindow* window = (WineWindow*)w;
1518     NSString* titleString;
1520     if (title)
1521         titleString = [NSString stringWithCharacters:title length:length];
1522     else
1523         titleString = @"";
1524     OnMainThreadAsync(^{
1525         [window setTitle:titleString];
1526         if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
1527             [NSApp changeWindowsItem:window title:titleString filename:NO];
1528     });
1530     [pool release];
1533 /***********************************************************************
1534  *              macdrv_order_cocoa_window
1536  * Reorder a Cocoa window relative to other windows.  If prev is
1537  * non-NULL, it is ordered below that window.  Else, if next is non-NULL,
1538  * it is ordered above that window.  Otherwise, it is ordered to the
1539  * front.
1541  * Returns true if the window has actually been ordered onto the screen
1542  * (i.e. if its frame intersects with a screen).  Otherwise, false.
1543  */
1544 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1545         macdrv_window next)
1547     WineWindow* window = (WineWindow*)w;
1548     __block BOOL on_screen;
1550     OnMainThread(^{
1551         on_screen = [window orderBelow:(WineWindow*)prev
1552                                orAbove:(WineWindow*)next];
1553     });
1555     return on_screen;
1558 /***********************************************************************
1559  *              macdrv_hide_cocoa_window
1561  * Hides a Cocoa window.
1562  */
1563 void macdrv_hide_cocoa_window(macdrv_window w)
1565     WineWindow* window = (WineWindow*)w;
1567     OnMainThread(^{
1568         [window doOrderOut];
1569     });
1572 /***********************************************************************
1573  *              macdrv_set_cocoa_window_frame
1575  * Move a Cocoa window.  If the window has been moved out of the bounds
1576  * of the desktop, it is ordered out.  (This routine won't ever order a
1577  * window in, though.)
1579  * Returns true if the window is on screen; false otherwise.
1580  */
1581 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1583     WineWindow* window = (WineWindow*)w;
1584     __block BOOL on_screen;
1586     OnMainThread(^{
1587         on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1588     });
1590     return on_screen;
1593 /***********************************************************************
1594  *              macdrv_get_cocoa_window_frame
1596  * Gets the frame of a Cocoa window.
1597  */
1598 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1600     WineWindow* window = (WineWindow*)w;
1602     OnMainThread(^{
1603         NSRect frame;
1605         frame = [window contentRectForFrameRect:[window frame]];
1606         [[WineApplicationController sharedController] flipRect:&frame];
1607         *out_frame = NSRectToCGRect(frame);
1608     });
1611 /***********************************************************************
1612  *              macdrv_set_cocoa_parent_window
1614  * Sets the parent window for a Cocoa window.  If parent is NULL, clears
1615  * the parent window.
1616  */
1617 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1619     WineWindow* window = (WineWindow*)w;
1621     OnMainThread(^{
1622         [window setMacDrvParentWindow:(WineWindow*)parent];
1623     });
1626 /***********************************************************************
1627  *              macdrv_set_window_surface
1628  */
1629 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1631     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1632     WineWindow* window = (WineWindow*)w;
1634     OnMainThread(^{
1635         window.surface = surface;
1636         window.surface_mutex = mutex;
1637     });
1639     [pool release];
1642 /***********************************************************************
1643  *              macdrv_window_needs_display
1645  * Mark a window as needing display in a specified rect (in non-client
1646  * area coordinates).
1647  */
1648 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1650     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1651     WineWindow* window = (WineWindow*)w;
1653     OnMainThreadAsync(^{
1654         [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1655     });
1657     [pool release];
1660 /***********************************************************************
1661  *              macdrv_set_window_shape
1663  * Sets the shape of a Cocoa window from an array of rectangles.  If
1664  * rects is NULL, resets the window's shape to its frame.
1665  */
1666 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1668     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1669     WineWindow* window = (WineWindow*)w;
1671     OnMainThread(^{
1672         if (!rects || !count)
1673             window.shape = nil;
1674         else
1675         {
1676             NSBezierPath* path;
1677             unsigned int i;
1679             path = [NSBezierPath bezierPath];
1680             for (i = 0; i < count; i++)
1681                 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1682             window.shape = path;
1683         }
1684     });
1686     [pool release];
1689 /***********************************************************************
1690  *              macdrv_set_window_alpha
1691  */
1692 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1694     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1695     WineWindow* window = (WineWindow*)w;
1697     [window setAlphaValue:alpha];
1699     [pool release];
1702 /***********************************************************************
1703  *              macdrv_set_window_color_key
1704  */
1705 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1706                                  CGFloat keyBlue)
1708     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1709     WineWindow* window = (WineWindow*)w;
1711     OnMainThread(^{
1712         window.colorKeyed       = TRUE;
1713         window.colorKeyRed      = keyRed;
1714         window.colorKeyGreen    = keyGreen;
1715         window.colorKeyBlue     = keyBlue;
1716         [window checkTransparency];
1717     });
1719     [pool release];
1722 /***********************************************************************
1723  *              macdrv_clear_window_color_key
1724  */
1725 void macdrv_clear_window_color_key(macdrv_window w)
1727     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1728     WineWindow* window = (WineWindow*)w;
1730     OnMainThread(^{
1731         window.colorKeyed = FALSE;
1732         [window checkTransparency];
1733     });
1735     [pool release];
1738 /***********************************************************************
1739  *              macdrv_window_use_per_pixel_alpha
1740  */
1741 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1743     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1744     WineWindow* window = (WineWindow*)w;
1746     OnMainThread(^{
1747         window.usePerPixelAlpha = use_per_pixel_alpha;
1748         [window checkTransparency];
1749     });
1751     [pool release];
1754 /***********************************************************************
1755  *              macdrv_give_cocoa_window_focus
1757  * Makes the Cocoa window "key" (gives it keyboard focus).  This also
1758  * orders it front and, if its frame was not within the desktop bounds,
1759  * Cocoa will typically move it on-screen.
1760  */
1761 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
1763     WineWindow* window = (WineWindow*)w;
1765     OnMainThread(^{
1766         [window makeFocused:activate];
1767     });
1770 /***********************************************************************
1771  *              macdrv_create_view
1773  * Creates and returns a view in the specified rect of the window.  The
1774  * caller is responsible for calling macdrv_dispose_view() on the view
1775  * when it is done with it.
1776  */
1777 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1779     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1780     WineWindow* window = (WineWindow*)w;
1781     __block WineContentView* view;
1783     if (CGRectIsNull(rect)) rect = CGRectZero;
1785     OnMainThread(^{
1786         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1788         view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1789         [view setAutoresizesSubviews:NO];
1790         [nc addObserver:view
1791                selector:@selector(updateGLContexts)
1792                    name:NSViewGlobalFrameDidChangeNotification
1793                  object:view];
1794         [nc addObserver:view
1795                selector:@selector(updateGLContexts)
1796                    name:NSApplicationDidChangeScreenParametersNotification
1797                  object:NSApp];
1798         [[window contentView] addSubview:view];
1799     });
1801     [pool release];
1802     return (macdrv_view)view;
1805 /***********************************************************************
1806  *              macdrv_dispose_view
1808  * Destroys a view previously returned by macdrv_create_view.
1809  */
1810 void macdrv_dispose_view(macdrv_view v)
1812     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1813     WineContentView* view = (WineContentView*)v;
1815     OnMainThread(^{
1816         NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1818         [nc removeObserver:view
1819                       name:NSViewGlobalFrameDidChangeNotification
1820                     object:view];
1821         [nc removeObserver:view
1822                       name:NSApplicationDidChangeScreenParametersNotification
1823                     object:NSApp];
1824         [view removeFromSuperview];
1825         [view release];
1826     });
1828     [pool release];
1831 /***********************************************************************
1832  *              macdrv_set_view_window_and_frame
1834  * Move a view to a new window and/or position within its window.  If w
1835  * is NULL, leave the view in its current window and just change its
1836  * frame.
1837  */
1838 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1840     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1841     WineContentView* view = (WineContentView*)v;
1842     WineWindow* window = (WineWindow*)w;
1844     if (CGRectIsNull(rect)) rect = CGRectZero;
1846     OnMainThread(^{
1847         BOOL changedWindow = (window && window != [view window]);
1848         NSRect newFrame = NSRectFromCGRect(rect);
1849         NSRect oldFrame = [view frame];
1851         if (changedWindow)
1852         {
1853             [view removeFromSuperview];
1854             [[window contentView] addSubview:view];
1855         }
1857         if (!NSEqualRects(oldFrame, newFrame))
1858         {
1859             if (!changedWindow)
1860                 [[view superview] setNeedsDisplayInRect:oldFrame];
1861             if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1862                 [view setFrameSize:newFrame.size];
1863             else if (NSEqualSizes(oldFrame.size, newFrame.size))
1864                 [view setFrameOrigin:newFrame.origin];
1865             else
1866                 [view setFrame:newFrame];
1867             [view setNeedsDisplay:YES];
1868         }
1869     });
1871     [pool release];
1874 /***********************************************************************
1875  *              macdrv_add_view_opengl_context
1877  * Add an OpenGL context to the list being tracked for each view.
1878  */
1879 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1881     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1882     WineContentView* view = (WineContentView*)v;
1883     WineOpenGLContext *context = (WineOpenGLContext*)c;
1885     OnMainThreadAsync(^{
1886         [view addGLContext:context];
1887     });
1889     [pool release];
1892 /***********************************************************************
1893  *              macdrv_remove_view_opengl_context
1895  * Add an OpenGL context to the list being tracked for each view.
1896  */
1897 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1899     NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1900     WineContentView* view = (WineContentView*)v;
1901     WineOpenGLContext *context = (WineOpenGLContext*)c;
1903     OnMainThreadAsync(^{
1904         [view removeGLContext:context];
1905     });
1907     [pool release];
1910 /***********************************************************************
1911  *              macdrv_window_background_color
1913  * Returns the standard Mac window background color as a 32-bit value of
1914  * the form 0x00rrggbb.
1915  */
1916 uint32_t macdrv_window_background_color(void)
1918     static uint32_t result;
1919     static dispatch_once_t once;
1921     // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
1922     // color spaces (RGB or grayscale).  So, the only way to get RGB values out
1923     // of it is to draw with it.
1924     dispatch_once(&once, ^{
1925         OnMainThread(^{
1926             unsigned char rgbx[4];
1927             unsigned char *planes = rgbx;
1928             NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
1929                                                                                pixelsWide:1
1930                                                                                pixelsHigh:1
1931                                                                             bitsPerSample:8
1932                                                                           samplesPerPixel:3
1933                                                                                  hasAlpha:NO
1934                                                                                  isPlanar:NO
1935                                                                            colorSpaceName:NSCalibratedRGBColorSpace
1936                                                                              bitmapFormat:0
1937                                                                               bytesPerRow:4
1938                                                                              bitsPerPixel:32];
1939             [NSGraphicsContext saveGraphicsState];
1940             [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
1941             [[NSColor windowBackgroundColor] set];
1942             NSRectFill(NSMakeRect(0, 0, 1, 1));
1943             [NSGraphicsContext restoreGraphicsState];
1944             [bitmap release];
1945             result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
1946         });
1947     });
1949     return result;
1952 /***********************************************************************
1953  *              macdrv_send_text_input_event
1954  */
1955 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
1957     __block BOOL ret;
1959     OnMainThread(^{
1960         WineWindow* window = (WineWindow*)[NSApp keyWindow];
1961         if (![window isKindOfClass:[WineWindow class]])
1962         {
1963             window = (WineWindow*)[NSApp mainWindow];
1964             if (![window isKindOfClass:[WineWindow class]])
1965                 window = [[WineApplicationController sharedController] frontWineWindow];
1966         }
1968         if (window)
1969         {
1970             NSUInteger localFlags = flags;
1971             CGEventRef c;
1972             NSEvent* event;
1974             window.imeData = data;
1975             fix_device_modifiers_by_generic(&localFlags);
1977             // An NSEvent created with +keyEventWithType:... is internally marked
1978             // as synthetic and doesn't get sent through input methods.  But one
1979             // created from a CGEvent doesn't have that problem.
1980             c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
1981             CGEventSetFlags(c, localFlags);
1982             CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
1983             event = [NSEvent eventWithCGEvent:c];
1984             CFRelease(c);
1986             window.commandDone = FALSE;
1987             ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
1988         }
1989         else
1990             ret = FALSE;
1991     });
1993     return ret;