2 * MACDRV Cocoa window code
4 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
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.
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.
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
21 #import <Carbon/Carbon.h>
22 #import <CoreVideo/CoreVideo.h>
24 #import "cocoa_window.h"
26 #include "macdrv_cocoa.h"
28 #import "cocoa_event.h"
29 #import "cocoa_opengl.h"
32 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
34 NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
35 NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
36 NSWindowFullScreenButton = 7,
37 NSFullScreenWindowMask = 1 << 14,
40 @interface NSWindow (WineFullScreenExtensions)
41 - (void) toggleFullScreen:(id)sender;
46 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
48 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
52 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
54 NSUInteger style_mask;
58 style_mask = NSTitledWindowMask;
59 if (wf->close_button) style_mask |= NSClosableWindowMask;
60 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
61 if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
62 if (wf->utility) style_mask |= NSUtilityWindowMask;
64 else style_mask = NSBorderlessWindowMask;
70 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
73 for (screen in screens)
75 if (NSIntersectsRect(frame, [screen frame]))
82 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
84 for (NSScreen* screen in screens)
86 if (NSContainsRect(rect, [screen frame]))
93 /* We rely on the supposedly device-dependent modifier flags to distinguish the
94 keys on the left side of the keyboard from those on the right. Some event
95 sources don't set those device-depdendent flags. If we see a device-independent
96 flag for a modifier without either corresponding device-dependent flag, assume
98 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
100 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
101 *modifiers |= NX_DEVICELCMDKEYMASK;
102 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
103 *modifiers |= NX_DEVICELSHIFTKEYMASK;
104 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
105 *modifiers |= NX_DEVICELCTLKEYMASK;
106 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
107 *modifiers |= NX_DEVICELALTKEYMASK;
110 /* As we manipulate individual bits of a modifier mask, we can end up with
111 inconsistent sets of flags. In particular, we might set or clear one of the
112 left/right-specific bits, but not the corresponding non-side-specific bit.
113 Fix that. If either side-specific bit is set, set the non-side-specific bit,
114 otherwise clear it. */
115 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
117 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
118 *modifiers |= NX_COMMANDMASK;
120 *modifiers &= ~NX_COMMANDMASK;
121 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
122 *modifiers |= NX_SHIFTMASK;
124 *modifiers &= ~NX_SHIFTMASK;
125 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
126 *modifiers |= NX_CONTROLMASK;
128 *modifiers &= ~NX_CONTROLMASK;
129 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
130 *modifiers |= NX_ALTERNATEMASK;
132 *modifiers &= ~NX_ALTERNATEMASK;
135 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
137 fix_device_modifiers_by_generic(&modifiers);
138 if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
140 modifiers |= NX_DEVICELCMDKEYMASK;
141 modifiers &= ~NX_DEVICELALTKEYMASK;
143 if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
145 modifiers |= NX_DEVICERCMDKEYMASK;
146 modifiers &= ~NX_DEVICERALTKEYMASK;
148 fix_generic_modifiers_by_device(&modifiers);
154 @interface NSWindow (WineAccessPrivateMethods)
155 - (id) _displayChanged;
159 @interface WineDisplayLink : NSObject
161 CGDirectDisplayID _displayID;
162 CVDisplayLinkRef _link;
163 NSMutableSet* _windows;
165 NSTimeInterval _actualRefreshPeriod;
166 NSTimeInterval _nominalRefreshPeriod;
169 - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
171 - (void) addWindow:(WineWindow*)window;
172 - (void) removeWindow:(WineWindow*)window;
174 - (NSTimeInterval) refreshPeriod;
180 @implementation WineDisplayLink
182 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
184 - (id) initWithDisplayID:(CGDirectDisplayID)displayID
189 CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
190 if (status == kCVReturnSuccess && !_link)
191 status = kCVReturnError;
192 if (status == kCVReturnSuccess)
193 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
194 if (status != kCVReturnSuccess)
200 _displayID = displayID;
201 _windows = [[NSMutableSet alloc] init];
210 CVDisplayLinkStop(_link);
211 CVDisplayLinkRelease(_link);
217 - (void) addWindow:(WineWindow*)window
219 @synchronized(self) {
220 BOOL needsStart = !_windows.count;
221 [_windows addObject:window];
223 CVDisplayLinkStart(_link);
227 - (void) removeWindow:(WineWindow*)window
229 @synchronized(self) {
230 BOOL wasRunning = _windows.count > 0;
231 [_windows removeObject:window];
232 if (wasRunning && !_windows.count)
233 CVDisplayLinkStop(_link);
240 @synchronized(self) {
241 windows = [_windows copy];
243 dispatch_async(dispatch_get_main_queue(), ^{
244 BOOL anyDisplayed = FALSE;
245 for (WineWindow* window in windows)
247 if ([window viewsNeedDisplay])
249 [window displayIfNeeded];
254 CVDisplayLinkStop(_link);
259 - (NSTimeInterval) refreshPeriod
261 if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
262 return _actualRefreshPeriod;
264 if (_nominalRefreshPeriod)
265 return _nominalRefreshPeriod;
267 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
268 if (time.flags & kCVTimeIsIndefinite)
270 _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
271 return _nominalRefreshPeriod;
276 CVDisplayLinkStart(_link);
279 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
281 WineDisplayLink* link = displayLinkContext;
283 return kCVReturnSuccess;
289 @interface WineContentView : NSView <NSTextInputClient>
291 NSMutableArray* glContexts;
292 NSMutableArray* pendingGlContexts;
293 BOOL _cachedHasGLDescendant;
294 BOOL _cachedHasGLDescendantValid;
295 BOOL clearedGlSurface;
297 NSMutableAttributedString* markedText;
298 NSRange markedTextSelection;
303 - (void) addGLContext:(WineOpenGLContext*)context;
304 - (void) removeGLContext:(WineOpenGLContext*)context;
305 - (void) updateGLContexts;
307 - (void) wine_getBackingSize:(int*)outBackingSize;
308 - (void) wine_setBackingSize:(const int*)newBackingSize;
313 @interface WineWindow ()
315 @property (readwrite, nonatomic) BOOL disabled;
316 @property (readwrite, nonatomic) BOOL noActivate;
317 @property (readwrite, nonatomic) BOOL floating;
318 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
319 @property (retain, nonatomic) NSWindow* latentParentWindow;
321 @property (nonatomic) void* hwnd;
322 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
324 @property (nonatomic) void* surface;
325 @property (nonatomic) pthread_mutex_t* surface_mutex;
327 @property (copy, nonatomic) NSBezierPath* shape;
328 @property (copy, nonatomic) NSData* shapeData;
329 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
330 @property (readonly, nonatomic) BOOL needsTransparency;
332 @property (nonatomic) BOOL colorKeyed;
333 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
334 @property (nonatomic) BOOL usePerPixelAlpha;
336 @property (assign, nonatomic) void* imeData;
337 @property (nonatomic) BOOL commandDone;
339 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
341 - (void) updateColorSpace;
342 - (void) updateForGLSubviews;
344 - (BOOL) becameEligibleParentOrChild;
345 - (void) becameIneligibleChild;
350 @implementation WineContentView
354 [markedText release];
355 [glContexts release];
356 [pendingGlContexts release];
365 - (void) drawRect:(NSRect)rect
367 WineWindow* window = (WineWindow*)[self window];
369 for (WineOpenGLContext* context in pendingGlContexts)
371 if (!clearedGlSurface)
373 context.shouldClearToBlack = TRUE;
374 clearedGlSurface = TRUE;
376 context.needsUpdate = TRUE;
378 [glContexts addObjectsFromArray:pendingGlContexts];
379 [pendingGlContexts removeAllObjects];
381 if ([window contentView] != self)
384 if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
386 [[NSColor clearColor] setFill];
389 [window.shape addClip];
391 [[NSColor windowBackgroundColor] setFill];
395 if (window.surface && window.surface_mutex &&
396 !pthread_mutex_lock(window.surface_mutex))
401 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
403 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
404 CGContextRef context;
407 [window.shape addClip];
409 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
410 CGContextSetBlendMode(context, kCGBlendModeCopy);
411 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
413 for (i = 0; i < count; i++)
418 imageRect = CGRectIntersection(rects[i], dirtyRect);
419 image = create_surface_image(window.surface, &imageRect, FALSE);
423 if (window.colorKeyed)
425 CGImageRef maskedImage;
426 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
427 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
428 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
429 maskedImage = CGImageCreateWithMaskingColors(image, components);
432 CGImageRelease(image);
437 CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
439 CGImageRelease(image);
444 pthread_mutex_unlock(window.surface_mutex);
447 // If the window may be transparent, then we have to invalidate the
448 // shadow every time we draw. Also, if this is the first time we've
449 // drawn since changing from transparent to opaque.
450 if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
452 window.shapeChangedSinceLastDraw = FALSE;
453 [window invalidateShadow];
457 - (void) addGLContext:(WineOpenGLContext*)context
459 BOOL hadContext = [self hasGLContext];
461 glContexts = [[NSMutableArray alloc] init];
462 if (!pendingGlContexts)
463 pendingGlContexts = [[NSMutableArray alloc] init];
465 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
467 [glContexts addObject:context];
468 if (!clearedGlSurface)
470 context.shouldClearToBlack = TRUE;
471 clearedGlSurface = TRUE;
473 context.needsUpdate = TRUE;
477 [pendingGlContexts addObject:context];
478 [self setNeedsDisplay:YES];
482 [self invalidateHasGLDescendant];
483 [(WineWindow*)[self window] updateForGLSubviews];
486 - (void) removeGLContext:(WineOpenGLContext*)context
488 BOOL hadContext = [self hasGLContext];
489 [glContexts removeObjectIdenticalTo:context];
490 [pendingGlContexts removeObjectIdenticalTo:context];
491 if (hadContext && ![self hasGLContext])
492 [self invalidateHasGLDescendant];
493 [(WineWindow*)[self window] updateForGLSubviews];
496 - (void) updateGLContexts
498 for (WineOpenGLContext* context in glContexts)
499 context.needsUpdate = TRUE;
502 - (BOOL) hasGLContext
504 return [glContexts count] || [pendingGlContexts count];
507 - (BOOL) _hasGLDescendant
511 if ([self hasGLContext])
513 for (WineContentView* view in [self subviews])
515 if ([view hasGLDescendant])
521 - (BOOL) hasGLDescendant
523 if (!_cachedHasGLDescendantValid)
525 _cachedHasGLDescendant = [self _hasGLDescendant];
526 _cachedHasGLDescendantValid = YES;
528 return _cachedHasGLDescendant;
531 - (void) invalidateHasGLDescendant
533 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
534 _cachedHasGLDescendantValid = NO;
535 if (invalidateAncestors && self != [[self window] contentView])
537 WineContentView* superview = (WineContentView*)[self superview];
538 if ([superview isKindOfClass:[WineContentView class]])
539 [superview invalidateHasGLDescendant];
543 - (void) wine_getBackingSize:(int*)outBackingSize
545 @synchronized(self) {
546 memcpy(outBackingSize, backingSize, sizeof(backingSize));
549 - (void) wine_setBackingSize:(const int*)newBackingSize
551 @synchronized(self) {
552 memcpy(backingSize, newBackingSize, sizeof(backingSize));
556 - (void) setRetinaMode:(int)mode
558 double scale = mode ? 0.5 : 2.0;
559 NSRect frame = self.frame;
560 frame.origin.x *= scale;
561 frame.origin.y *= scale;
562 frame.size.width *= scale;
563 frame.size.height *= scale;
564 [self setFrame:frame];
565 [self updateGLContexts];
567 for (WineContentView* subview in [self subviews])
569 if ([subview isKindOfClass:[WineContentView class]])
570 [subview setRetinaMode:mode];
574 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
579 - (BOOL) preservesContentDuringLiveResize
581 // Returning YES from this tells Cocoa to keep our view's content during
582 // a Cocoa-driven resize. In theory, we're also supposed to override
583 // -setFrameSize: to mark exposed sections as needing redisplay, but
584 // user32 will take care of that in a roundabout way. This way, we don't
585 // redraw until the window surface is flushed.
587 // This doesn't do anything when we resize the window ourselves.
591 - (BOOL)acceptsFirstResponder
593 return [[self window] contentView] == self;
596 - (BOOL) mouseDownCanMoveWindow
601 - (void) completeText:(NSString*)text
604 WineWindow* window = (WineWindow*)[self window];
606 event = macdrv_create_event(IM_SET_TEXT, window);
607 event->im_set_text.data = [window imeData];
608 event->im_set_text.text = (CFStringRef)[text copy];
609 event->im_set_text.complete = TRUE;
611 [[window queue] postEvent:event];
613 macdrv_release_event(event);
615 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
616 markedTextSelection = NSMakeRange(0, 0);
617 [[self inputContext] discardMarkedText];
620 - (NSFocusRingType) focusRingType
622 return NSFocusRingTypeNone;
625 - (void) didAddSubview:(NSView*)subview
627 if ([subview isKindOfClass:[WineContentView class]])
629 WineContentView* view = (WineContentView*)subview;
630 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
631 [self invalidateHasGLDescendant];
633 [super didAddSubview:subview];
636 - (void) willRemoveSubview:(NSView*)subview
638 if ([subview isKindOfClass:[WineContentView class]])
640 WineContentView* view = (WineContentView*)subview;
641 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
642 [self invalidateHasGLDescendant];
644 [super willRemoveSubview:subview];
647 - (void) setHidden:(BOOL)hidden
649 [super setHidden:hidden];
650 [self invalidateHasGLDescendant];
654 * ---------- NSTextInputClient methods ----------
656 - (NSTextInputContext*) inputContext
659 markedText = [[NSMutableAttributedString alloc] init];
660 return [super inputContext];
663 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
665 if ([string isKindOfClass:[NSAttributedString class]])
666 string = [string string];
668 if ([string isKindOfClass:[NSString class]])
669 [self completeText:string];
672 - (void) doCommandBySelector:(SEL)aSelector
674 [(WineWindow*)[self window] setCommandDone:TRUE];
677 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
679 if ([string isKindOfClass:[NSAttributedString class]])
680 string = [string string];
682 if ([string isKindOfClass:[NSString class]])
685 WineWindow* window = (WineWindow*)[self window];
687 if (replacementRange.location == NSNotFound)
688 replacementRange = NSMakeRange(0, [markedText length]);
690 [markedText replaceCharactersInRange:replacementRange withString:string];
691 markedTextSelection = selectedRange;
692 markedTextSelection.location += replacementRange.location;
694 event = macdrv_create_event(IM_SET_TEXT, window);
695 event->im_set_text.data = [window imeData];
696 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
697 event->im_set_text.complete = FALSE;
698 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
700 [[window queue] postEvent:event];
702 macdrv_release_event(event);
704 [[self inputContext] invalidateCharacterCoordinates];
710 [self completeText:nil];
713 - (NSRange) selectedRange
715 return markedTextSelection;
718 - (NSRange) markedRange
720 NSRange range = NSMakeRange(0, [markedText length]);
722 range.location = NSNotFound;
726 - (BOOL) hasMarkedText
728 return [markedText length] > 0;
731 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
733 if (aRange.location >= [markedText length])
736 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
738 *actualRange = aRange;
739 return [markedText attributedSubstringFromRange:aRange];
742 - (NSArray*) validAttributesForMarkedText
744 return [NSArray array];
747 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
750 WineWindow* window = (WineWindow*)[self window];
753 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
755 query = macdrv_create_query();
756 query->type = QUERY_IME_CHAR_RECT;
757 query->window = (macdrv_window)[window retain];
758 query->ime_char_rect.data = [window imeData];
759 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
761 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
763 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
764 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
765 [[WineApplicationController sharedController] flipRect:&ret];
768 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
770 macdrv_release_query(query);
773 *actualRange = aRange;
777 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
782 - (NSInteger) windowLevel
784 return [[self window] level];
790 @implementation WineWindow
792 static WineWindow* causing_becomeKeyWindow;
794 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
795 @synthesize surface, surface_mutex;
796 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
797 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
798 @synthesize usePerPixelAlpha;
799 @synthesize imeData, commandDone;
801 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
802 windowFrame:(NSRect)window_frame
804 queue:(WineEventQueue*)queue
807 WineContentView* contentView;
808 NSTrackingArea* trackingArea;
809 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
811 [[WineApplicationController sharedController] flipRect:&window_frame];
813 window = [[[self alloc] initWithContentRect:window_frame
814 styleMask:style_mask_for_features(wf)
815 backing:NSBackingStoreBuffered
816 defer:YES] autorelease];
818 if (!window) return nil;
820 /* Standardize windows to eliminate differences between titled and
821 borderless windows and between NSWindow and NSPanel. */
822 [window setHidesOnDeactivate:NO];
823 [window setReleasedWhenClosed:NO];
825 [window setOneShot:YES];
826 [window disableCursorRects];
827 [window setShowsResizeIndicator:NO];
828 [window setHasShadow:wf->shadow];
829 [window setAcceptsMouseMovedEvents:YES];
830 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
831 [window setDelegate:window];
832 [window setAutodisplay:NO];
834 window.queue = queue;
835 window->savedContentMinSize = NSZeroSize;
836 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
837 window->resizable = wf->resizable;
838 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
840 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
841 (NSString*)kUTTypeContent,
844 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
847 [contentView setAutoresizesSubviews:NO];
849 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
850 because they give us mouse moves in the background. */
851 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
852 options:(NSTrackingMouseMoved |
853 NSTrackingActiveAlways |
854 NSTrackingInVisibleRect)
856 userInfo:nil] autorelease];
859 [contentView addTrackingArea:trackingArea];
861 [window setContentView:contentView];
862 [window setInitialFirstResponder:contentView];
864 [nc addObserver:window
865 selector:@selector(updateFullscreen)
866 name:NSApplicationDidChangeScreenParametersNotification
868 [window updateFullscreen];
870 [nc addObserver:window
871 selector:@selector(applicationWillHide)
872 name:NSApplicationWillHideNotification
874 [nc addObserver:window
875 selector:@selector(applicationDidUnhide)
876 name:NSApplicationDidUnhideNotification
879 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
880 selector:@selector(checkWineDisplayLink)
881 name:NSWorkspaceActiveSpaceDidChangeNotification
882 object:[NSWorkspace sharedWorkspace]];
884 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
891 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
892 [[NSNotificationCenter defaultCenter] removeObserver:self];
894 [latentChildWindows release];
895 [latentParentWindow release];
901 - (BOOL) preventResizing
903 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
904 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
907 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
909 if (allow_immovable_windows && (disabled || inMaximized))
911 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
917 - (void) adjustFeaturesForState
919 NSUInteger style = [self styleMask];
921 if (style & NSClosableWindowMask)
922 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
923 if (style & NSMiniaturizableWindowMask)
924 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
925 if (style & NSResizableWindowMask)
926 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
927 if ([self respondsToSelector:@selector(toggleFullScreen:)])
929 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
930 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
933 if ([self preventResizing])
935 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
936 [self setContentMinSize:size];
937 [self setContentMaxSize:size];
941 [self setContentMaxSize:savedContentMaxSize];
942 [self setContentMinSize:savedContentMinSize];
945 if (allow_immovable_windows || cursor_clipping_locks_windows)
946 [self setMovable:[self allowsMovingWithMaximized:maximized]];
949 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
951 if ([self respondsToSelector:@selector(toggleFullScreen:)])
953 NSUInteger style = [self styleMask];
955 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
956 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized)
958 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
959 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
963 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
964 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
965 if (style & NSFullScreenWindowMask)
966 [super toggleFullScreen:nil];
970 if (behavior != [self collectionBehavior])
972 [self setCollectionBehavior:behavior];
973 [self adjustFeaturesForState];
977 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
979 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
980 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
981 NSUInteger currentStyle = [self styleMask];
982 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
984 if (newStyle != currentStyle)
986 NSString* title = [[[self title] copy] autorelease];
987 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
988 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
989 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
991 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
992 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
993 // just NSTitledWindowMask, the window buttons should disappear rather
994 // than just being disabled. But they don't. Similarly in reverse.
995 // The workaround is to also toggle NSClosableWindowMask at the same time.
996 [self setStyleMask:newStyle ^ NSClosableWindowMask];
998 [self setStyleMask:newStyle];
1000 // -setStyleMask: resets the firstResponder to the window. Set it
1001 // back to the content view.
1002 if ([[self contentView] acceptsFirstResponder])
1003 [self makeFirstResponder:[self contentView]];
1005 [self adjustFullScreenBehavior:[self collectionBehavior]];
1007 if ([[self title] length] == 0 && [title length] > 0)
1008 [self setTitle:title];
1011 resizable = wf->resizable;
1012 [self adjustFeaturesForState];
1013 [self setHasShadow:wf->shadow];
1016 // Indicates if the window would be visible if the app were not hidden.
1017 - (BOOL) wouldBeVisible
1019 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1022 - (BOOL) isOrderedIn
1024 return [self wouldBeVisible] || [self isMiniaturized];
1027 - (NSInteger) minimumLevelForActive:(BOOL)active
1031 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1032 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1033 level = NSFloatingWindowLevel;
1035 level = NSNormalWindowLevel;
1041 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1043 if (captured || fullscreen)
1046 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1048 level = NSStatusWindowLevel + 1;
1058 - (void) postDidUnminimizeEvent
1060 macdrv_event* event;
1062 /* Coalesce events by discarding any previous ones still in the queue. */
1063 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1066 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1067 [queue postEvent:event];
1068 macdrv_release_event(event);
1071 - (void) sendResizeStartQuery
1073 macdrv_query* query = macdrv_create_query();
1074 query->type = QUERY_RESIZE_START;
1075 query->window = (macdrv_window)[self retain];
1077 [self.queue query:query timeout:0.3];
1078 macdrv_release_query(query);
1081 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1083 NSWindowCollectionBehavior behavior;
1085 self.disabled = state->disabled;
1086 self.noActivate = state->no_activate;
1088 if (self.floating != state->floating)
1090 self.floating = state->floating;
1091 if (state->floating)
1093 // Became floating. If child of non-floating window, make that
1094 // relationship latent.
1095 WineWindow* parent = (WineWindow*)[self parentWindow];
1096 if (parent && !parent.floating)
1097 [self becameIneligibleChild];
1101 // Became non-floating. If parent of floating children, make that
1102 // relationship latent.
1104 for (child in [self childWineWindows])
1107 [child becameIneligibleChild];
1111 // Check our latent relationships. If floating status was the only
1112 // reason they were latent, then make them active.
1113 if ([self isVisible])
1114 [self becameEligibleParentOrChild];
1116 [[WineApplicationController sharedController] adjustWindowLevels];
1119 if (state->minimized_valid)
1121 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1123 pendingMinimize = FALSE;
1124 if (state->minimized && ![self isMiniaturized])
1126 if ([self wouldBeVisible])
1128 if ([self styleMask] & NSFullScreenWindowMask)
1130 [self postDidUnminimizeEvent];
1131 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1135 [super miniaturize:nil];
1136 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1137 event_mask_for_type(WINDOW_GOT_FOCUS) |
1138 event_mask_for_type(WINDOW_LOST_FOCUS);
1142 pendingMinimize = TRUE;
1144 else if (!state->minimized && [self isMiniaturized])
1146 ignore_windowDeminiaturize = TRUE;
1147 [self deminiaturize:nil];
1148 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1152 [queue discardEventsMatchingMask:discard forWindow:self];
1155 if (state->maximized != maximized)
1157 maximized = state->maximized;
1158 [self adjustFeaturesForState];
1160 if (!maximized && [self inLiveResize])
1161 [self sendResizeStartQuery];
1164 behavior = NSWindowCollectionBehaviorDefault;
1165 if (state->excluded_by_expose)
1166 behavior |= NSWindowCollectionBehaviorTransient;
1168 behavior |= NSWindowCollectionBehaviorManaged;
1169 if (state->excluded_by_cycle)
1171 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1172 if ([self isOrderedIn])
1173 [NSApp removeWindowsItem:self];
1177 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1178 if ([self isOrderedIn])
1179 [NSApp addWindowsItem:self title:[self title] filename:NO];
1181 [self adjustFullScreenBehavior:behavior];
1184 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1186 BOOL reordered = FALSE;
1188 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1190 if ([self level] > [child level])
1191 [child setLevel:[self level]];
1192 [self addChildWindow:child ordered:NSWindowAbove];
1193 [child checkWineDisplayLink];
1194 [latentChildWindows removeObjectIdenticalTo:child];
1195 child.latentParentWindow = nil;
1200 if (!latentChildWindows)
1201 latentChildWindows = [[NSMutableArray alloc] init];
1202 if (![latentChildWindows containsObject:child])
1203 [latentChildWindows addObject:child];
1204 child.latentParentWindow = self;
1210 - (BOOL) addChildWineWindow:(WineWindow*)child
1212 return [self addChildWineWindow:child assumeVisible:FALSE];
1215 - (void) removeChildWineWindow:(WineWindow*)child
1217 [self removeChildWindow:child];
1218 if (child.latentParentWindow == self)
1219 child.latentParentWindow = nil;
1220 [latentChildWindows removeObjectIdenticalTo:child];
1223 - (BOOL) becameEligibleParentOrChild
1225 BOOL reordered = FALSE;
1228 if (latentParentWindow.floating || !self.floating)
1230 // If we aren't visible currently, we assume that we should be and soon
1231 // will be. So, if the latent parent is visible that's enough to assume
1232 // we can establish the parent-child relationship in Cocoa. That will
1233 // actually make us visible, which is fine.
1234 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1238 // Here, though, we may not actually be visible yet and adding a child
1239 // won't make us visible. The caller will have to call this method
1240 // again after actually making us visible.
1241 if ([self isVisible] && (count = [latentChildWindows count]))
1243 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1246 for (i = 0; i < count; i++)
1248 WineWindow* child = [latentChildWindows objectAtIndex:i];
1249 if ([child isVisible] && (self.floating || !child.floating))
1251 if (child.latentParentWindow == self)
1253 if ([self level] > [child level])
1254 [child setLevel:[self level]];
1255 [self addChildWindow:child ordered:NSWindowAbove];
1256 child.latentParentWindow = nil;
1260 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1261 [indexesToRemove addIndex:i];
1265 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1271 - (void) becameIneligibleChild
1273 WineWindow* parent = (WineWindow*)[self parentWindow];
1276 if (!parent->latentChildWindows)
1277 parent->latentChildWindows = [[NSMutableArray alloc] init];
1278 [parent->latentChildWindows insertObject:self atIndex:0];
1279 self.latentParentWindow = parent;
1280 [parent removeChildWindow:self];
1284 - (void) becameIneligibleParentOrChild
1286 NSArray* childWindows = [self childWineWindows];
1288 [self becameIneligibleChild];
1290 if ([childWindows count])
1294 for (child in childWindows)
1296 child.latentParentWindow = self;
1297 [self removeChildWindow:child];
1300 if (latentChildWindows)
1301 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1303 latentChildWindows = [childWindows mutableCopy];
1307 // Determine if, among Wine windows, this window is directly above or below
1308 // a given other Wine window with no other Wine window intervening.
1309 // Intervening non-Wine windows are ignored.
1310 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1312 NSNumber* windowNumber;
1313 NSNumber* otherWindowNumber;
1314 NSArray* windowNumbers;
1315 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1317 if (![self isVisible] || ![otherWindow isVisible])
1320 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1321 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1322 windowNumbers = [[self class] windowNumbersWithOptions:0];
1323 windowIndex = [windowNumbers indexOfObject:windowNumber];
1324 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1326 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1329 if (orderingMode == NSWindowAbove)
1331 lowIndex = windowIndex;
1332 highIndex = otherWindowIndex;
1334 else if (orderingMode == NSWindowBelow)
1336 lowIndex = otherWindowIndex;
1337 highIndex = windowIndex;
1342 if (highIndex <= lowIndex)
1345 for (i = lowIndex + 1; i < highIndex; i++)
1347 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1348 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1349 if ([interveningWindow isKindOfClass:[WineWindow class]])
1356 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1358 NSMutableArray* windowNumbers;
1359 NSNumber* childWindowNumber;
1360 NSUInteger otherIndex, limit;
1361 NSArray* origChildren;
1362 NSMutableArray* children;
1364 // Get the z-order from the window server and modify it to reflect the
1365 // requested window ordering.
1366 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1367 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1368 [windowNumbers removeObject:childWindowNumber];
1369 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1370 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1372 // Get our child windows and sort them in the reverse of the desired
1373 // z-order (back-to-front).
1374 origChildren = [self childWineWindows];
1375 children = [[origChildren mutableCopy] autorelease];
1376 [children sortWithOptions:NSSortStable
1377 usingComparator:^NSComparisonResult(id obj1, id obj2){
1378 NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1379 NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1380 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1381 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1382 if (index1 == NSNotFound)
1384 if (index2 == NSNotFound)
1385 return NSOrderedSame;
1387 return NSOrderedAscending;
1389 else if (index2 == NSNotFound)
1390 return NSOrderedDescending;
1391 else if (index1 < index2)
1392 return NSOrderedDescending;
1393 else if (index2 < index1)
1394 return NSOrderedAscending;
1396 return NSOrderedSame;
1399 // If the current and desired children arrays match up to a point, leave
1400 // those matching children alone.
1401 limit = MIN([origChildren count], [children count]);
1402 for (otherIndex = 0; otherIndex < limit; otherIndex++)
1404 if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1407 [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1409 // Remove all of the child windows and re-add them back-to-front so they
1410 // are in the desired order.
1411 for (other in children)
1412 [self removeChildWindow:other];
1413 for (other in children)
1414 [self addChildWindow:other ordered:NSWindowAbove];
1417 /* Returns whether or not the window was ordered in, which depends on if
1418 its frame intersects any screen. */
1419 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1421 WineApplicationController* controller = [WineApplicationController sharedController];
1422 if (![self isMiniaturized])
1424 BOOL needAdjustWindowLevels = FALSE;
1427 [controller transformProcessToForeground];
1429 wasVisible = [self isVisible];
1432 [NSApp activateIgnoringOtherApps:YES];
1434 NSDisableScreenUpdates();
1436 if ([self becameEligibleParentOrChild])
1437 needAdjustWindowLevels = TRUE;
1441 WineWindow* other = [prev isVisible] ? prev : next;
1442 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1444 if (![self isOrdered:orderingMode relativeTo:other])
1446 WineWindow* parent = (WineWindow*)[self parentWindow];
1447 WineWindow* otherParent = (WineWindow*)[other parentWindow];
1449 // This window level may not be right for this window based
1450 // on floating-ness, fullscreen-ness, etc. But we set it
1451 // temporarily to allow us to order the windows properly.
1452 // Then the levels get fixed by -adjustWindowLevels.
1453 if ([self level] != [other level])
1454 [self setLevel:[other level]];
1455 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1456 [self checkWineDisplayLink];
1458 // The above call to -[NSWindow orderWindow:relativeTo:] won't
1459 // reorder windows which are both children of the same parent
1460 // relative to each other, so do that separately.
1461 if (parent && parent == otherParent)
1462 [parent order:orderingMode childWindow:self relativeTo:other];
1464 needAdjustWindowLevels = TRUE;
1469 // Again, temporarily set level to make sure we can order to
1471 next = [controller frontWineWindow];
1472 if (next && [self level] < [next level])
1473 [self setLevel:[next level]];
1474 [self orderFront:nil];
1475 [self checkWineDisplayLink];
1476 needAdjustWindowLevels = TRUE;
1479 if ([self becameEligibleParentOrChild])
1480 needAdjustWindowLevels = TRUE;
1482 if (needAdjustWindowLevels)
1484 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1485 [controller updateFullscreenWindows];
1486 [controller adjustWindowLevels];
1489 if (pendingMinimize)
1491 [super miniaturize:nil];
1492 pendingMinimize = FALSE;
1495 NSEnableScreenUpdates();
1497 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1498 Generate a frame-changed event just in case. The back end will ignore
1499 it if nothing actually changed. */
1500 [self windowDidResize:nil];
1502 if (![self isExcludedFromWindowsMenu])
1503 [NSApp addWindowsItem:self title:[self title] filename:NO];
1509 WineApplicationController* controller = [WineApplicationController sharedController];
1510 BOOL wasVisible = [self isVisible];
1511 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1513 if ([self isMiniaturized])
1514 pendingMinimize = TRUE;
1516 WineWindow* parent = (WineWindow*)self.parentWindow;
1517 if ([parent isKindOfClass:[WineWindow class]])
1518 [parent grabDockIconSnapshotFromWindow:self force:NO];
1520 [self becameIneligibleParentOrChild];
1521 if ([self isMiniaturized])
1525 fakingClose = FALSE;
1528 [self orderOut:nil];
1529 [self checkWineDisplayLink];
1530 savedVisibleState = FALSE;
1531 if (wasVisible && wasOnActiveSpace && fullscreen)
1532 [controller updateFullscreenWindows];
1533 [controller adjustWindowLevels];
1534 [NSApp removeWindowsItem:self];
1536 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1537 event_mask_for_type(WINDOW_GOT_FOCUS) |
1538 event_mask_for_type(WINDOW_LOST_FOCUS) |
1539 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1540 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1541 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1545 - (void) updateFullscreen
1547 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1548 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1550 if (nowFullscreen != fullscreen)
1552 WineApplicationController* controller = [WineApplicationController sharedController];
1554 fullscreen = nowFullscreen;
1555 if ([self isVisible] && [self isOnActiveSpace])
1556 [controller updateFullscreenWindows];
1558 [controller adjustWindowLevels];
1562 - (void) setFrameAndWineFrame:(NSRect)frame
1564 [self setFrame:frame display:YES];
1567 roundedWineFrame = self.frame;
1569 #if CGFLOAT_IS_DOUBLE
1570 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1571 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1572 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1573 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1574 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1575 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1576 roundedWineFrame = wineFrame;
1578 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1579 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1580 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1581 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1582 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1583 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1584 roundedWineFrame = wineFrame;
1588 - (void) setFrameFromWine:(NSRect)contentRect
1590 /* Origin is (left, top) in a top-down space. Need to convert it to
1591 (left, bottom) in a bottom-up space. */
1592 [[WineApplicationController sharedController] flipRect:&contentRect];
1594 /* The back end is establishing a new window size and position. It's
1595 not interested in any stale events regarding those that may be sitting
1597 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1600 if (!NSIsEmptyRect(contentRect))
1602 NSRect frame, oldFrame;
1604 oldFrame = self.wine_fractionalFrame;
1605 frame = [self frameRectForContentRect:contentRect];
1606 if (!NSEqualRects(frame, oldFrame))
1608 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1609 BOOL needEnableScreenUpdates = FALSE;
1611 if ([self preventResizing])
1613 // Allow the following calls to -setFrame:display: to work even
1614 // if they would violate the content size constraints. This
1615 // shouldn't be necessary since the content size constraints are
1616 // documented to not constrain that method, but it seems to be.
1617 [self setContentMinSize:NSZeroSize];
1618 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1621 if (equalSizes && [[self childWineWindows] count])
1623 // If we change the window frame such that the origin moves
1624 // but the size doesn't change, then Cocoa moves child
1625 // windows with the parent. We don't want that so we fake
1626 // a change of the size and then change it back.
1627 NSRect bogusFrame = frame;
1628 bogusFrame.size.width++;
1630 NSDisableScreenUpdates();
1631 needEnableScreenUpdates = TRUE;
1633 ignore_windowResize = TRUE;
1634 [self setFrame:bogusFrame display:NO];
1635 ignore_windowResize = FALSE;
1638 [self setFrameAndWineFrame:frame];
1639 if ([self preventResizing])
1641 [self setContentMinSize:contentRect.size];
1642 [self setContentMaxSize:contentRect.size];
1645 if (needEnableScreenUpdates)
1646 NSEnableScreenUpdates();
1649 [self updateColorSpace];
1651 if (!enteringFullScreen &&
1652 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1653 nonFullscreenFrame = frame;
1655 [self updateFullscreen];
1657 if ([self isOrderedIn])
1659 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1660 event. The back end will ignore it if nothing actually changed. */
1661 [self windowDidResize:nil];
1667 - (NSRect) wine_fractionalFrame
1669 NSRect frame = self.frame;
1670 if (NSEqualRects(frame, roundedWineFrame))
1675 - (void) setMacDrvParentWindow:(WineWindow*)parent
1677 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1678 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1680 [oldParent removeChildWineWindow:self];
1681 [latentParentWindow removeChildWineWindow:self];
1682 if ([parent addChildWineWindow:self])
1683 [[WineApplicationController sharedController] adjustWindowLevels];
1687 - (void) setDisabled:(BOOL)newValue
1689 if (disabled != newValue)
1691 disabled = newValue;
1692 [self adjustFeaturesForState];
1696 - (BOOL) needsTransparency
1698 return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1699 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1702 - (void) checkTransparency
1704 if (![self isOpaque] && !self.needsTransparency)
1706 self.shapeChangedSinceLastDraw = TRUE;
1707 [[self contentView] setNeedsDisplay:YES];
1708 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1709 [self setOpaque:YES];
1711 else if ([self isOpaque] && self.needsTransparency)
1713 self.shapeChangedSinceLastDraw = TRUE;
1714 [[self contentView] setNeedsDisplay:YES];
1715 [self setBackgroundColor:[NSColor clearColor]];
1716 [self setOpaque:NO];
1720 - (void) setShape:(NSBezierPath*)newShape
1722 if (shape == newShape) return;
1726 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1730 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1732 shape = [newShape copy];
1733 self.shapeChangedSinceLastDraw = TRUE;
1735 [self checkTransparency];
1738 - (void) makeFocused:(BOOL)activate
1742 [[WineApplicationController sharedController] transformProcessToForeground];
1743 [NSApp activateIgnoringOtherApps:YES];
1746 causing_becomeKeyWindow = self;
1747 [self makeKeyWindow];
1748 causing_becomeKeyWindow = nil;
1750 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1751 event_mask_for_type(WINDOW_LOST_FOCUS)
1755 - (void) postKey:(uint16_t)keyCode
1756 pressed:(BOOL)pressed
1757 modifiers:(NSUInteger)modifiers
1758 event:(NSEvent*)theEvent
1760 macdrv_event* event;
1762 WineApplicationController* controller = [WineApplicationController sharedController];
1764 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1765 event->key.keycode = keyCode;
1766 event->key.modifiers = modifiers;
1767 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1769 if ((cgevent = [theEvent CGEvent]))
1771 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1772 kCGKeyboardEventKeyboardType);
1773 if (keyboardType != controller.keyboardType)
1775 controller.keyboardType = keyboardType;
1776 [controller keyboardSelectionDidChange];
1780 [queue postEvent:event];
1782 macdrv_release_event(event);
1784 [controller noteKey:keyCode pressed:pressed];
1787 - (void) postKeyEvent:(NSEvent *)theEvent
1789 [self flagsChanged:theEvent];
1790 [self postKey:[theEvent keyCode]
1791 pressed:[theEvent type] == NSKeyDown
1792 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1796 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1798 savedContentMinSize = minSize;
1799 savedContentMaxSize = maxSize;
1800 if (![self preventResizing])
1802 [self setContentMinSize:minSize];
1803 [self setContentMaxSize:maxSize];
1807 - (WineWindow*) ancestorWineWindow
1809 WineWindow* ancestor = self;
1812 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1813 if ([parent isKindOfClass:[WineWindow class]])
1821 - (void) postBroughtForwardEvent
1823 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1824 [queue postEvent:event];
1825 macdrv_release_event(event);
1828 - (void) updateForCursorClipping
1830 [self adjustFeaturesForState];
1833 - (void) endWindowDragging
1837 if (draggingPhase == 3)
1839 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
1840 [queue postEvent:event];
1841 macdrv_release_event(event);
1845 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
1849 - (NSMutableDictionary*) displayIDToDisplayLinkMap
1851 static NSMutableDictionary* displayIDToDisplayLinkMap;
1852 if (!displayIDToDisplayLinkMap)
1854 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
1856 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
1859 usingBlock:^(NSNotification *note){
1860 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
1861 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
1862 [badDisplayIDs minusSet:validDisplayIDs];
1863 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
1866 return displayIDToDisplayLinkMap;
1869 - (WineDisplayLink*) wineDisplayLink
1871 if (!_lastDisplayID)
1874 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
1875 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
1878 - (void) checkWineDisplayLink
1880 NSScreen* screen = self.screen;
1881 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
1883 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
1884 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
1888 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
1889 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
1890 if (displayID == _lastDisplayID)
1893 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
1897 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
1898 [link removeWindow:self];
1902 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
1905 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
1906 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
1908 [link addWindow:self];
1909 [self displayIfNeeded];
1911 _lastDisplayID = displayID;
1914 - (BOOL) isEmptyShaped
1916 return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
1919 - (BOOL) canProvideSnapshot
1921 return (self.windowNumber > 0 && ![self isEmptyShaped]);
1924 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
1926 if (![self isEmptyShaped])
1929 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
1930 if (!force && now < lastDockIconSnapshot + 1)
1935 if (![window canProvideSnapshot])
1941 for (WineWindow* childWindow in self.childWindows)
1943 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
1946 NSSize size = childWindow.frame.size;
1947 CGFloat area = size.width * size.height;
1948 if (!window || area > bestArea)
1950 window = childWindow;
1959 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
1960 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
1961 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
1962 CFRelease(windowIDs);
1966 NSImage* appImage = [NSApp applicationIconImage];
1968 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
1970 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
1971 [dockIcon lockFocus];
1973 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
1975 CGRect rect = CGRectMake(8, 8, 240, 240);
1976 size_t width = CGImageGetWidth(windowImage);
1977 size_t height = CGImageGetHeight(windowImage);
1980 rect.size.height *= height / (double)width;
1981 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
1983 else if (width != height)
1985 rect.size.width *= width / (double)height;
1986 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
1989 CGContextDrawImage(cgcontext, rect, windowImage);
1990 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
1992 operation:NSCompositeSourceOver
1997 [dockIcon unlockFocus];
1999 CGImageRelease(windowImage);
2001 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2002 if (![imageView isKindOfClass:[NSImageView class]])
2004 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2005 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2006 self.dockTile.contentView = imageView;
2008 imageView.image = dockIcon;
2009 [self.dockTile display];
2010 lastDockIconSnapshot = now;
2013 - (void) checkEmptyShaped
2015 if (self.dockTile.contentView && ![self isEmptyShaped])
2017 self.dockTile.contentView = nil;
2018 lastDockIconSnapshot = 0;
2020 [self checkWineDisplayLink];
2025 * ---------- NSWindow method overrides ----------
2027 - (BOOL) canBecomeKeyWindow
2029 if (causing_becomeKeyWindow == self) return YES;
2030 if (self.disabled || self.noActivate) return NO;
2031 return [self isKeyWindow];
2034 - (BOOL) canBecomeMainWindow
2036 return [self canBecomeKeyWindow];
2039 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2041 // If a window is sized to completely cover a screen, then it's in
2042 // full-screen mode. In that case, we don't allow NSWindow to constrain
2044 NSArray* screens = [NSScreen screens];
2045 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2046 if (!screen_covered_by_rect(contentRect, screens) &&
2047 frame_intersects_screens(frameRect, screens))
2048 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2052 // This private method of NSWindow is called as Cocoa reacts to the display
2053 // configuration changing. Among other things, it adjusts the window's
2054 // frame based on how the screen(s) changed size. That tells Wine that the
2055 // window has been moved. We don't want that. Rather, we want to make
2056 // sure that the WinAPI notion of the window position is maintained/
2057 // restored, possibly undoing or overriding Cocoa's adjustment.
2059 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2060 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2061 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2062 // reassert its notion of the position. That call won't get processed
2063 // until after this method returns, so it will override whatever this
2064 // method does to the window position. It will also discard any pending
2065 // WINDOW_FRAME_CHANGED events.
2067 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2068 // adjust the window's position due to a display change is to hook into
2069 // this private method. This private method has remained stable from 10.6
2070 // through 10.11. If it does change, the most likely thing is that it
2071 // will be removed and no longer called and this fix will simply stop
2072 // working. The only real danger would be if Apple changed the return type
2073 // to a struct or floating-point type, which would change the calling
2075 - (id) _displayChanged
2077 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2078 [queue postEvent:event];
2079 macdrv_release_event(event);
2081 return [super _displayChanged];
2084 - (BOOL) isExcludedFromWindowsMenu
2086 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2089 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2091 BOOL ret = [super validateMenuItem:menuItem];
2093 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2094 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2095 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2101 /* We don't call this. It's the action method of the items in the Window menu. */
2102 - (void) makeKeyAndOrderFront:(id)sender
2104 if ([self isMiniaturized])
2105 [self deminiaturize:nil];
2106 [self orderBelow:nil orAbove:nil activate:NO];
2107 [[self ancestorWineWindow] postBroughtForwardEvent];
2109 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2110 [[WineApplicationController sharedController] windowGotFocus:self];
2113 - (void) sendEvent:(NSEvent*)event
2115 NSEventType type = event.type;
2117 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2118 interface control. For example, Control-Tab switches focus among
2119 views. We want to bypass that feature, so directly route key-down
2120 events to -keyDown:. */
2121 if (type == NSKeyDown)
2122 [[self firstResponder] keyDown:event];
2125 if (!draggingPhase && maximized && ![self isMovable] &&
2126 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2127 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2129 NSRect titleBar = self.frame;
2130 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2131 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2132 titleBar.origin.y = NSMaxY(contentRect);
2134 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2136 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2138 static const NSWindowButton buttons[] = {
2139 NSWindowCloseButton,
2140 NSWindowMiniaturizeButton,
2142 NSWindowFullScreenButton,
2144 BOOL hitButton = NO;
2147 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2151 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2154 button = [self standardWindowButton:buttons[i]];
2155 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2165 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2166 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2170 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2172 if ([self isMovable])
2174 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2175 NSPoint newTopLeft = dragWindowStartPosition;
2177 newTopLeft.x += point.x - dragStartPosition.x;
2178 newTopLeft.y += point.y - dragStartPosition.y;
2180 if (draggingPhase == 2)
2182 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2183 [queue postEvent:event];
2184 macdrv_release_event(event);
2189 [self setFrameTopLeftPoint:newTopLeft];
2191 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2193 macdrv_event* event;
2194 NSRect frame = [self contentRectForFrameRect:self.frame];
2196 [[WineApplicationController sharedController] flipRect:&frame];
2198 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2199 event->window_restore_requested.keep_frame = TRUE;
2200 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2201 [queue postEvent:event];
2202 macdrv_release_event(event);
2207 if (type == NSLeftMouseUp)
2208 [self endWindowDragging];
2211 [super sendEvent:event];
2215 - (void) miniaturize:(id)sender
2217 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2218 [queue postEvent:event];
2219 macdrv_release_event(event);
2221 WineWindow* parent = (WineWindow*)self.parentWindow;
2222 if ([parent isKindOfClass:[WineWindow class]])
2223 [parent grabDockIconSnapshotFromWindow:self force:YES];
2226 - (void) toggleFullScreen:(id)sender
2228 if (!self.disabled && !maximized)
2229 [super toggleFullScreen:sender];
2232 - (void) setViewsNeedDisplay:(BOOL)value
2234 if (value && ![self viewsNeedDisplay])
2236 WineDisplayLink* link = [self wineDisplayLink];
2239 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2240 if (_lastDisplayTime + [link refreshPeriod] < now)
2241 [self setAutodisplay:YES];
2245 _lastDisplayTime = now;
2249 [super setViewsNeedDisplay:value];
2254 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2256 [self setAutodisplay:NO];
2259 - (void) displayIfNeeded
2261 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2262 [super displayIfNeeded];
2263 [self setAutodisplay:NO];
2266 - (NSArray*) childWineWindows
2268 NSArray* childWindows = self.childWindows;
2269 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2270 return [child isKindOfClass:[WineWindow class]];
2272 return [childWindows objectsAtIndexes:indexes];
2275 // We normally use the generic/calibrated RGB color space for the window,
2276 // rather than the device color space, to avoid expensive color conversion
2277 // which slows down drawing. However, for windows displaying OpenGL, having
2278 // a different color space than the screen greatly reduces frame rates, often
2279 // limiting it to the display refresh rate.
2281 // To avoid this, we switch back to the screen color space whenever the
2282 // window is covered by a view with an attached OpenGL context.
2283 - (void) updateColorSpace
2285 NSRect contentRect = [[self contentView] frame];
2286 BOOL coveredByGLView = FALSE;
2287 WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2288 if ([view isKindOfClass:[WineContentView class]] && [view hasGLContext])
2290 NSRect frame = [view convertRect:[view bounds] toView:nil];
2291 if (NSContainsRect(frame, contentRect))
2292 coveredByGLView = TRUE;
2295 if (coveredByGLView)
2296 [self setColorSpace:nil];
2298 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2301 - (void) updateForGLSubviews
2303 [self updateColorSpace];
2304 if (gl_surface_mode == GL_SURFACE_BEHIND)
2305 [self checkTransparency];
2308 - (void) setRetinaMode:(int)mode
2311 double scale = mode ? 0.5 : 2.0;
2312 NSAffineTransform* transform = [NSAffineTransform transform];
2314 [transform scaleBy:scale];
2317 [shape transformUsingAffineTransform:transform];
2319 for (WineContentView* subview in [self.contentView subviews])
2321 if ([subview isKindOfClass:[WineContentView class]])
2322 [subview setRetinaMode:mode];
2325 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2326 frame.origin.x *= scale;
2327 frame.origin.y *= scale;
2328 frame.size.width *= scale;
2329 frame.size.height *= scale;
2330 frame = [self frameRectForContentRect:frame];
2332 savedContentMinSize = [transform transformSize:savedContentMinSize];
2333 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2334 savedContentMaxSize.width *= scale;
2335 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2336 savedContentMaxSize.height *= scale;
2338 self.contentMinSize = [transform transformSize:self.contentMinSize];
2339 NSSize temp = self.contentMaxSize;
2340 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2341 temp.width *= scale;
2342 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2343 temp.height *= scale;
2344 self.contentMaxSize = temp;
2346 ignore_windowResize = TRUE;
2347 [self setFrameAndWineFrame:frame];
2348 ignore_windowResize = FALSE;
2353 * ---------- NSResponder method overrides ----------
2355 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
2357 - (void) flagsChanged:(NSEvent *)theEvent
2359 static const struct {
2363 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2364 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2365 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2366 { NX_DEVICELCTLKEYMASK, kVK_Control },
2367 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2368 { NX_DEVICELALTKEYMASK, kVK_Option },
2369 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2370 { NX_DEVICELCMDKEYMASK, kVK_Command },
2371 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2374 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2376 int i, last_changed;
2378 fix_device_modifiers_by_generic(&modifierFlags);
2379 changed = modifierFlags ^ lastModifierFlags;
2382 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2383 if (changed & modifiers[i].mask)
2386 for (i = 0; i <= last_changed; i++)
2388 if (changed & modifiers[i].mask)
2390 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2392 if (i == last_changed)
2393 lastModifierFlags = modifierFlags;
2396 lastModifierFlags ^= modifiers[i].mask;
2397 fix_generic_modifiers_by_device(&lastModifierFlags);
2400 // Caps lock generates one event for each press-release action.
2401 // We need to simulate a pair of events for each actual event.
2402 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2404 [self postKey:modifiers[i].keycode
2406 modifiers:lastModifierFlags
2407 event:(NSEvent*)theEvent];
2411 [self postKey:modifiers[i].keycode
2413 modifiers:lastModifierFlags
2414 event:(NSEvent*)theEvent];
2419 - (void) applicationWillHide
2421 savedVisibleState = [self isVisible];
2424 - (void) applicationDidUnhide
2426 if ([self isVisible])
2427 [self becameEligibleParentOrChild];
2432 * ---------- NSWindowDelegate methods ----------
2434 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2436 macdrv_query* query;
2439 query = macdrv_create_query();
2440 query->type = QUERY_MIN_MAX_INFO;
2441 query->window = (macdrv_window)[self retain];
2442 [self.queue query:query timeout:0.5];
2443 macdrv_release_query(query);
2445 size = [self contentMaxSize];
2446 if (proposedSize.width < size.width)
2447 size.width = proposedSize.width;
2448 if (proposedSize.height < size.height)
2449 size.height = proposedSize.height;
2453 - (void)windowDidBecomeKey:(NSNotification *)notification
2455 WineApplicationController* controller = [WineApplicationController sharedController];
2456 NSEvent* event = [controller lastFlagsChanged];
2458 [self flagsChanged:event];
2460 if (causing_becomeKeyWindow == self) return;
2462 [controller windowGotFocus:self];
2465 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2467 [self checkWineDisplayLink];
2470 - (void) windowDidChangeScreen:(NSNotification*)notification
2472 [self checkWineDisplayLink];
2475 - (void)windowDidDeminiaturize:(NSNotification *)notification
2477 WineApplicationController* controller = [WineApplicationController sharedController];
2479 if (!ignore_windowDeminiaturize)
2480 [self postDidUnminimizeEvent];
2481 ignore_windowDeminiaturize = FALSE;
2483 [self becameEligibleParentOrChild];
2485 if (fullscreen && [self isOnActiveSpace])
2486 [controller updateFullscreenWindows];
2487 [controller adjustWindowLevels];
2489 if (![self parentWindow])
2490 [self postBroughtForwardEvent];
2492 if (!self.disabled && !self.noActivate)
2494 causing_becomeKeyWindow = self;
2495 [self makeKeyWindow];
2496 causing_becomeKeyWindow = nil;
2497 [controller windowGotFocus:self];
2500 [self windowDidResize:notification];
2501 [self checkWineDisplayLink];
2504 - (void) windowDidEndLiveResize:(NSNotification *)notification
2508 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2509 [queue postEvent:event];
2510 macdrv_release_event(event);
2514 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2516 enteringFullScreen = FALSE;
2517 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2520 - (void) windowDidExitFullScreen:(NSNotification*)notification
2522 exitingFullScreen = FALSE;
2523 [self setFrameAndWineFrame:nonFullscreenFrame];
2524 [self windowDidResize:nil];
2527 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2529 enteringFullScreen = FALSE;
2530 enteredFullScreenTime = 0;
2533 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2535 exitingFullScreen = FALSE;
2536 [self windowDidResize:nil];
2539 - (void)windowDidMiniaturize:(NSNotification *)notification
2541 if (fullscreen && [self isOnActiveSpace])
2542 [[WineApplicationController sharedController] updateFullscreenWindows];
2543 [self checkWineDisplayLink];
2546 - (void)windowDidMove:(NSNotification *)notification
2548 [self windowDidResize:notification];
2551 - (void)windowDidResignKey:(NSNotification *)notification
2553 macdrv_event* event;
2555 if (causing_becomeKeyWindow) return;
2557 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2558 [queue postEvent:event];
2559 macdrv_release_event(event);
2562 - (void)windowDidResize:(NSNotification *)notification
2564 macdrv_event* event;
2565 NSRect frame = self.wine_fractionalFrame;
2567 if ([self inLiveResize])
2569 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2570 resizingFromLeft = TRUE;
2571 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2572 resizingFromTop = TRUE;
2575 frame = [self contentRectForFrameRect:frame];
2577 if (ignore_windowResize || exitingFullScreen) return;
2579 if ([self preventResizing])
2581 [self setContentMinSize:frame.size];
2582 [self setContentMaxSize:frame.size];
2585 [[WineApplicationController sharedController] flipRect:&frame];
2587 /* Coalesce events by discarding any previous ones still in the queue. */
2588 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2591 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2592 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2593 event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
2594 event->window_frame_changed.in_resize = [self inLiveResize];
2595 [queue postEvent:event];
2596 macdrv_release_event(event);
2598 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2599 [self updateFullscreen];
2602 - (BOOL)windowShouldClose:(id)sender
2604 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2605 [queue postEvent:event];
2606 macdrv_release_event(event);
2610 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2614 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2615 [queue postEvent:event];
2616 macdrv_release_event(event);
2619 else if (!resizable)
2621 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2622 [queue postEvent:event];
2623 macdrv_release_event(event);
2630 - (void) windowWillClose:(NSNotification*)notification
2634 if (fakingClose) return;
2635 if (latentParentWindow)
2637 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2638 self.latentParentWindow = nil;
2641 for (child in latentChildWindows)
2643 if (child.latentParentWindow == self)
2644 child.latentParentWindow = nil;
2646 [latentChildWindows removeAllObjects];
2649 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2651 enteringFullScreen = TRUE;
2652 nonFullscreenFrame = self.wine_fractionalFrame;
2655 - (void) windowWillExitFullScreen:(NSNotification*)notification
2657 exitingFullScreen = TRUE;
2660 - (void)windowWillMiniaturize:(NSNotification *)notification
2662 [self becameIneligibleParentOrChild];
2663 [self grabDockIconSnapshotFromWindow:nil force:NO];
2666 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2668 if ([self inLiveResize])
2671 return self.wine_fractionalFrame.size;
2674 macdrv_query* query;
2676 rect = [self frame];
2677 if (resizingFromLeft)
2678 rect.origin.x = NSMaxX(rect) - frameSize.width;
2679 if (!resizingFromTop)
2680 rect.origin.y = NSMaxY(rect) - frameSize.height;
2681 rect.size = frameSize;
2682 rect = [self contentRectForFrameRect:rect];
2683 [[WineApplicationController sharedController] flipRect:&rect];
2685 query = macdrv_create_query();
2686 query->type = QUERY_RESIZE_SIZE;
2687 query->window = (macdrv_window)[self retain];
2688 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2689 query->resize_size.from_left = resizingFromLeft;
2690 query->resize_size.from_top = resizingFromTop;
2692 if ([self.queue query:query timeout:0.1])
2694 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2695 rect = [self frameRectForContentRect:rect];
2696 frameSize = rect.size;
2699 macdrv_release_query(query);
2705 - (void) windowWillStartLiveResize:(NSNotification *)notification
2707 [self endWindowDragging];
2711 macdrv_event* event;
2712 NSRect frame = [self contentRectForFrameRect:self.frame];
2714 [[WineApplicationController sharedController] flipRect:&frame];
2716 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2717 event->window_restore_requested.keep_frame = TRUE;
2718 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2719 [queue postEvent:event];
2720 macdrv_release_event(event);
2723 [self sendResizeStartQuery];
2725 frameAtResizeStart = [self frame];
2726 resizingFromLeft = resizingFromTop = FALSE;
2729 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2731 macdrv_query* query;
2732 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2735 query = macdrv_create_query();
2736 query->type = QUERY_MIN_MAX_INFO;
2737 query->window = (macdrv_window)[self retain];
2738 [self.queue query:query timeout:0.5];
2739 macdrv_release_query(query);
2741 currentContentRect = [self contentRectForFrameRect:[self frame]];
2742 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2744 maxSize = [self contentMaxSize];
2745 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2746 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2748 // Try to keep the top-left corner where it is.
2749 newContentRect.origin.x = NSMinX(currentContentRect);
2750 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2752 // If that pushes the bottom or right off the screen, pull it up and to the left.
2753 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2754 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2755 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2756 if (NSMinY(newContentRect) < NSMinY(screenRect))
2757 newContentRect.origin.y = NSMinY(screenRect);
2759 // If that pushes the top or left off the screen, push it down and the right
2760 // again. Do this last because the top-left corner is more important than the
2762 if (NSMinX(newContentRect) < NSMinX(screenRect))
2763 newContentRect.origin.x = NSMinX(screenRect);
2764 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2765 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2767 return [self frameRectForContentRect:newContentRect];
2772 * ---------- NSPasteboardOwner methods ----------
2774 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2776 macdrv_query* query = macdrv_create_query();
2777 query->type = QUERY_PASTEBOARD_DATA;
2778 query->window = (macdrv_window)[self retain];
2779 query->pasteboard_data.type = (CFStringRef)[type copy];
2781 [self.queue query:query timeout:3];
2782 macdrv_release_query(query);
2787 * ---------- NSDraggingDestination methods ----------
2789 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2791 return [self draggingUpdated:sender];
2794 - (void) draggingExited:(id <NSDraggingInfo>)sender
2796 // This isn't really a query. We don't need any response. However, it
2797 // has to be processed in a similar manner as the other drag-and-drop
2798 // queries in order to maintain the proper order of operations.
2799 macdrv_query* query = macdrv_create_query();
2800 query->type = QUERY_DRAG_EXITED;
2801 query->window = (macdrv_window)[self retain];
2803 [self.queue query:query timeout:0.1];
2804 macdrv_release_query(query);
2807 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2809 NSDragOperation ret;
2810 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2811 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
2812 NSPasteboard* pb = [sender draggingPasteboard];
2814 macdrv_query* query = macdrv_create_query();
2815 query->type = QUERY_DRAG_OPERATION;
2816 query->window = (macdrv_window)[self retain];
2817 query->drag_operation.x = floor(cgpt.x);
2818 query->drag_operation.y = floor(cgpt.y);
2819 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2820 query->drag_operation.accepted_op = NSDragOperationNone;
2821 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2823 [self.queue query:query timeout:3];
2824 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2825 macdrv_release_query(query);
2830 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2833 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2834 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
2835 NSPasteboard* pb = [sender draggingPasteboard];
2837 macdrv_query* query = macdrv_create_query();
2838 query->type = QUERY_DRAG_DROP;
2839 query->window = (macdrv_window)[self retain];
2840 query->drag_drop.x = floor(cgpt.x);
2841 query->drag_drop.y = floor(cgpt.y);
2842 query->drag_drop.op = [sender draggingSourceOperationMask];
2843 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2845 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
2846 ret = query->status;
2847 macdrv_release_query(query);
2852 - (BOOL) wantsPeriodicDraggingUpdates
2860 /***********************************************************************
2861 * macdrv_create_cocoa_window
2863 * Create a Cocoa window with the given content frame and features (e.g.
2864 * title bar, close box, etc.).
2866 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2867 CGRect frame, void* hwnd, macdrv_event_queue queue)
2869 __block WineWindow* window;
2872 window = [[WineWindow createWindowWithFeatures:wf
2873 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
2875 queue:(WineEventQueue*)queue] retain];
2878 return (macdrv_window)window;
2881 /***********************************************************************
2882 * macdrv_destroy_cocoa_window
2884 * Destroy a Cocoa window.
2886 void macdrv_destroy_cocoa_window(macdrv_window w)
2888 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2889 WineWindow* window = (WineWindow*)w;
2892 [window doOrderOut];
2895 [window.queue discardEventsMatchingMask:-1 forWindow:window];
2901 /***********************************************************************
2902 * macdrv_get_window_hwnd
2904 * Get the hwnd that was set for the window at creation.
2906 void* macdrv_get_window_hwnd(macdrv_window w)
2908 WineWindow* window = (WineWindow*)w;
2912 /***********************************************************************
2913 * macdrv_set_cocoa_window_features
2915 * Update a Cocoa window's features.
2917 void macdrv_set_cocoa_window_features(macdrv_window w,
2918 const struct macdrv_window_features* wf)
2920 WineWindow* window = (WineWindow*)w;
2923 [window setWindowFeatures:wf];
2927 /***********************************************************************
2928 * macdrv_set_cocoa_window_state
2930 * Update a Cocoa window's state.
2932 void macdrv_set_cocoa_window_state(macdrv_window w,
2933 const struct macdrv_window_state* state)
2935 WineWindow* window = (WineWindow*)w;
2938 [window setMacDrvState:state];
2942 /***********************************************************************
2943 * macdrv_set_cocoa_window_title
2945 * Set a Cocoa window's title.
2947 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2950 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2951 WineWindow* window = (WineWindow*)w;
2952 NSString* titleString;
2955 titleString = [NSString stringWithCharacters:title length:length];
2958 OnMainThreadAsync(^{
2959 [window setTitle:titleString];
2960 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2961 [NSApp changeWindowsItem:window title:titleString filename:NO];
2967 /***********************************************************************
2968 * macdrv_order_cocoa_window
2970 * Reorder a Cocoa window relative to other windows. If prev is
2971 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
2972 * it is ordered above that window. Otherwise, it is ordered to the
2975 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2976 macdrv_window n, int activate)
2978 WineWindow* window = (WineWindow*)w;
2979 WineWindow* prev = (WineWindow*)p;
2980 WineWindow* next = (WineWindow*)n;
2982 OnMainThreadAsync(^{
2983 [window orderBelow:prev
2987 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2989 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2993 /***********************************************************************
2994 * macdrv_hide_cocoa_window
2996 * Hides a Cocoa window.
2998 void macdrv_hide_cocoa_window(macdrv_window w)
3000 WineWindow* window = (WineWindow*)w;
3003 [window doOrderOut];
3007 /***********************************************************************
3008 * macdrv_set_cocoa_window_frame
3010 * Move a Cocoa window.
3012 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3014 WineWindow* window = (WineWindow*)w;
3017 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3021 /***********************************************************************
3022 * macdrv_get_cocoa_window_frame
3024 * Gets the frame of a Cocoa window.
3026 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3028 WineWindow* window = (WineWindow*)w;
3033 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3034 [[WineApplicationController sharedController] flipRect:&frame];
3035 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3039 /***********************************************************************
3040 * macdrv_set_cocoa_parent_window
3042 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3043 * the parent window.
3045 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3047 WineWindow* window = (WineWindow*)w;
3050 [window setMacDrvParentWindow:(WineWindow*)parent];
3054 /***********************************************************************
3055 * macdrv_set_window_surface
3057 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3059 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3060 WineWindow* window = (WineWindow*)w;
3063 window.surface = surface;
3064 window.surface_mutex = mutex;
3070 /***********************************************************************
3071 * macdrv_window_needs_display
3073 * Mark a window as needing display in a specified rect (in non-client
3074 * area coordinates).
3076 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3078 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3079 WineWindow* window = (WineWindow*)w;
3081 OnMainThreadAsync(^{
3082 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3088 /***********************************************************************
3089 * macdrv_set_window_shape
3091 * Sets the shape of a Cocoa window from an array of rectangles. If
3092 * rects is NULL, resets the window's shape to its frame.
3094 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3096 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3097 WineWindow* window = (WineWindow*)w;
3100 if (!rects || !count)
3103 window.shapeData = nil;
3104 [window checkEmptyShaped];
3108 size_t length = sizeof(*rects) * count;
3109 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3114 path = [NSBezierPath bezierPath];
3115 for (i = 0; i < count; i++)
3116 [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3117 window.shape = path;
3118 window.shapeData = [NSData dataWithBytes:rects length:length];
3119 [window checkEmptyShaped];
3127 /***********************************************************************
3128 * macdrv_set_window_alpha
3130 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3132 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3133 WineWindow* window = (WineWindow*)w;
3135 [window setAlphaValue:alpha];
3140 /***********************************************************************
3141 * macdrv_set_window_color_key
3143 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3146 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3147 WineWindow* window = (WineWindow*)w;
3150 window.colorKeyed = TRUE;
3151 window.colorKeyRed = keyRed;
3152 window.colorKeyGreen = keyGreen;
3153 window.colorKeyBlue = keyBlue;
3154 [window checkTransparency];
3160 /***********************************************************************
3161 * macdrv_clear_window_color_key
3163 void macdrv_clear_window_color_key(macdrv_window w)
3165 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3166 WineWindow* window = (WineWindow*)w;
3169 window.colorKeyed = FALSE;
3170 [window checkTransparency];
3176 /***********************************************************************
3177 * macdrv_window_use_per_pixel_alpha
3179 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3181 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3182 WineWindow* window = (WineWindow*)w;
3185 window.usePerPixelAlpha = use_per_pixel_alpha;
3186 [window checkTransparency];
3192 /***********************************************************************
3193 * macdrv_give_cocoa_window_focus
3195 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3196 * orders it front and, if its frame was not within the desktop bounds,
3197 * Cocoa will typically move it on-screen.
3199 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3201 WineWindow* window = (WineWindow*)w;
3204 [window makeFocused:activate];
3208 /***********************************************************************
3209 * macdrv_set_window_min_max_sizes
3211 * Sets the window's minimum and maximum content sizes.
3213 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3215 WineWindow* window = (WineWindow*)w;
3218 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3222 /***********************************************************************
3223 * macdrv_create_view
3225 * Creates and returns a view with the specified frame rect. The
3226 * caller is responsible for calling macdrv_dispose_view() on the view
3227 * when it is done with it.
3229 macdrv_view macdrv_create_view(CGRect rect)
3231 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3232 __block WineContentView* view;
3234 if (CGRectIsNull(rect)) rect = CGRectZero;
3237 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3239 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3240 [view setAutoresizesSubviews:NO];
3241 [view setHidden:YES];
3242 [nc addObserver:view
3243 selector:@selector(updateGLContexts)
3244 name:NSViewGlobalFrameDidChangeNotification
3246 [nc addObserver:view
3247 selector:@selector(updateGLContexts)
3248 name:NSApplicationDidChangeScreenParametersNotification
3253 return (macdrv_view)view;
3256 /***********************************************************************
3257 * macdrv_dispose_view
3259 * Destroys a view previously returned by macdrv_create_view.
3261 void macdrv_dispose_view(macdrv_view v)
3263 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3264 WineContentView* view = (WineContentView*)v;
3267 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3268 WineWindow* window = (WineWindow*)[view window];
3270 [nc removeObserver:view
3271 name:NSViewGlobalFrameDidChangeNotification
3273 [nc removeObserver:view
3274 name:NSApplicationDidChangeScreenParametersNotification
3276 [view removeFromSuperview];
3278 [window updateForGLSubviews];
3284 /***********************************************************************
3285 * macdrv_set_view_frame
3287 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3289 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3290 WineContentView* view = (WineContentView*)v;
3292 if (CGRectIsNull(rect)) rect = CGRectZero;
3295 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3296 NSRect oldFrame = [view frame];
3298 if (!NSEqualRects(oldFrame, newFrame))
3300 [[view superview] setNeedsDisplayInRect:oldFrame];
3301 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3302 [view setFrameSize:newFrame.size];
3303 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3304 [view setFrameOrigin:newFrame.origin];
3306 [view setFrame:newFrame];
3307 [view setNeedsDisplay:YES];
3311 int backing_size[2] = { 0 };
3312 [view wine_setBackingSize:backing_size];
3314 [(WineWindow*)[view window] updateForGLSubviews];
3321 /***********************************************************************
3322 * macdrv_set_view_superview
3324 * Move a view to a new superview and position it relative to its
3325 * siblings. If p is non-NULL, the view is ordered behind it.
3326 * Otherwise, the view is ordered above n. If s is NULL, use the
3327 * content view of w as the new superview.
3329 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3331 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3332 WineContentView* view = (WineContentView*)v;
3333 WineContentView* superview = (WineContentView*)s;
3334 WineWindow* window = (WineWindow*)w;
3335 WineContentView* prev = (WineContentView*)p;
3336 WineContentView* next = (WineContentView*)n;
3339 superview = [window contentView];
3342 if (superview == [view superview])
3344 NSArray* subviews = [superview subviews];
3345 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3346 if (!prev && !next && index == [subviews count] - 1)
3348 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3350 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3354 WineWindow* oldWindow = (WineWindow*)[view window];
3355 WineWindow* newWindow = (WineWindow*)[superview window];
3358 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3360 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3362 if (oldWindow != newWindow)
3364 [oldWindow updateForGLSubviews];
3365 [newWindow updateForGLSubviews];
3372 /***********************************************************************
3373 * macdrv_set_view_hidden
3375 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3377 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3378 WineContentView* view = (WineContentView*)v;
3381 [view setHidden:hidden];
3387 /***********************************************************************
3388 * macdrv_add_view_opengl_context
3390 * Add an OpenGL context to the list being tracked for each view.
3392 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3394 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3395 WineContentView* view = (WineContentView*)v;
3396 WineOpenGLContext *context = (WineOpenGLContext*)c;
3399 [view addGLContext:context];
3405 /***********************************************************************
3406 * macdrv_remove_view_opengl_context
3408 * Add an OpenGL context to the list being tracked for each view.
3410 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3412 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3413 WineContentView* view = (WineContentView*)v;
3414 WineOpenGLContext *context = (WineOpenGLContext*)c;
3416 OnMainThreadAsync(^{
3417 [view removeGLContext:context];
3423 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3425 WineContentView* view = (WineContentView*)v;
3427 if (![view isKindOfClass:[WineContentView class]])
3430 [view wine_getBackingSize:backing_size];
3434 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3436 WineContentView* view = (WineContentView*)v;
3438 if ([view isKindOfClass:[WineContentView class]])
3439 [view wine_setBackingSize:backing_size];
3442 /***********************************************************************
3443 * macdrv_window_background_color
3445 * Returns the standard Mac window background color as a 32-bit value of
3446 * the form 0x00rrggbb.
3448 uint32_t macdrv_window_background_color(void)
3450 static uint32_t result;
3451 static dispatch_once_t once;
3453 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3454 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3455 // of it is to draw with it.
3456 dispatch_once(&once, ^{
3458 unsigned char rgbx[4];
3459 unsigned char *planes = rgbx;
3460 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3467 colorSpaceName:NSCalibratedRGBColorSpace
3471 [NSGraphicsContext saveGraphicsState];
3472 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3473 [[NSColor windowBackgroundColor] set];
3474 NSRectFill(NSMakeRect(0, 0, 1, 1));
3475 [NSGraphicsContext restoreGraphicsState];
3477 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3484 /***********************************************************************
3485 * macdrv_send_text_input_event
3487 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3489 OnMainThreadAsync(^{
3491 macdrv_event* event;
3492 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3493 if (![window isKindOfClass:[WineWindow class]])
3495 window = (WineWindow*)[NSApp mainWindow];
3496 if (![window isKindOfClass:[WineWindow class]])
3497 window = [[WineApplicationController sharedController] frontWineWindow];
3502 NSUInteger localFlags = flags;
3506 window.imeData = data;
3507 fix_device_modifiers_by_generic(&localFlags);
3509 // An NSEvent created with +keyEventWithType:... is internally marked
3510 // as synthetic and doesn't get sent through input methods. But one
3511 // created from a CGEvent doesn't have that problem.
3512 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3513 CGEventSetFlags(c, localFlags);
3514 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3515 event = [NSEvent eventWithCGEvent:c];
3518 window.commandDone = FALSE;
3519 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3524 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3525 event->sent_text_input.handled = ret;
3526 event->sent_text_input.done = done;
3527 [[window queue] postEvent:event];
3528 macdrv_release_event(event);