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 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
47 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
49 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
54 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
56 NSUInteger style_mask;
60 style_mask = NSTitledWindowMask;
61 if (wf->close_button) style_mask |= NSClosableWindowMask;
62 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
63 if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
64 if (wf->utility) style_mask |= NSUtilityWindowMask;
66 else style_mask = NSBorderlessWindowMask;
72 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
75 for (screen in screens)
77 if (NSIntersectsRect(frame, [screen frame]))
84 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
86 for (NSScreen* screen in screens)
88 if (NSContainsRect(rect, [screen frame]))
95 /* We rely on the supposedly device-dependent modifier flags to distinguish the
96 keys on the left side of the keyboard from those on the right. Some event
97 sources don't set those device-depdendent flags. If we see a device-independent
98 flag for a modifier without either corresponding device-dependent flag, assume
100 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
102 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
103 *modifiers |= NX_DEVICELCMDKEYMASK;
104 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
105 *modifiers |= NX_DEVICELSHIFTKEYMASK;
106 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
107 *modifiers |= NX_DEVICELCTLKEYMASK;
108 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
109 *modifiers |= NX_DEVICELALTKEYMASK;
112 /* As we manipulate individual bits of a modifier mask, we can end up with
113 inconsistent sets of flags. In particular, we might set or clear one of the
114 left/right-specific bits, but not the corresponding non-side-specific bit.
115 Fix that. If either side-specific bit is set, set the non-side-specific bit,
116 otherwise clear it. */
117 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
119 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
120 *modifiers |= NX_COMMANDMASK;
122 *modifiers &= ~NX_COMMANDMASK;
123 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
124 *modifiers |= NX_SHIFTMASK;
126 *modifiers &= ~NX_SHIFTMASK;
127 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
128 *modifiers |= NX_CONTROLMASK;
130 *modifiers &= ~NX_CONTROLMASK;
131 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
132 *modifiers |= NX_ALTERNATEMASK;
134 *modifiers &= ~NX_ALTERNATEMASK;
137 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
139 fix_device_modifiers_by_generic(&modifiers);
140 if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
142 modifiers |= NX_DEVICELCMDKEYMASK;
143 modifiers &= ~NX_DEVICELALTKEYMASK;
145 if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
147 modifiers |= NX_DEVICERCMDKEYMASK;
148 modifiers &= ~NX_DEVICERALTKEYMASK;
150 fix_generic_modifiers_by_device(&modifiers);
156 @interface NSWindow (WineAccessPrivateMethods)
157 - (id) _displayChanged;
161 @interface WineDisplayLink : NSObject
163 CGDirectDisplayID _displayID;
164 CVDisplayLinkRef _link;
165 NSMutableSet* _windows;
167 NSTimeInterval _actualRefreshPeriod;
168 NSTimeInterval _nominalRefreshPeriod;
170 NSTimeInterval _lastDisplayTime;
173 - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
175 - (void) addWindow:(WineWindow*)window;
176 - (void) removeWindow:(WineWindow*)window;
178 - (NSTimeInterval) refreshPeriod;
184 @implementation WineDisplayLink
186 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
188 - (id) initWithDisplayID:(CGDirectDisplayID)displayID
193 CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
194 if (status == kCVReturnSuccess && !_link)
195 status = kCVReturnError;
196 if (status == kCVReturnSuccess)
197 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
198 if (status != kCVReturnSuccess)
204 _displayID = displayID;
205 _windows = [[NSMutableSet alloc] init];
214 CVDisplayLinkStop(_link);
215 CVDisplayLinkRelease(_link);
221 - (void) addWindow:(WineWindow*)window
224 @synchronized(self) {
225 firstWindow = !_windows.count;
226 [_windows addObject:window];
228 if (firstWindow || !CVDisplayLinkIsRunning(_link))
232 - (void) removeWindow:(WineWindow*)window
234 BOOL lastWindow = FALSE;
235 @synchronized(self) {
236 BOOL hadWindows = _windows.count > 0;
237 [_windows removeObject:window];
238 if (hadWindows && !_windows.count)
241 if (lastWindow && CVDisplayLinkIsRunning(_link))
242 CVDisplayLinkStop(_link);
248 @synchronized(self) {
249 windows = [_windows copy];
251 dispatch_async(dispatch_get_main_queue(), ^{
252 BOOL anyDisplayed = FALSE;
253 for (WineWindow* window in windows)
255 if ([window viewsNeedDisplay])
257 [window displayIfNeeded];
262 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
264 _lastDisplayTime = now;
265 else if (_lastDisplayTime + 2.0 < now)
266 CVDisplayLinkStop(_link);
271 - (NSTimeInterval) refreshPeriod
273 if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
274 return _actualRefreshPeriod;
276 if (_nominalRefreshPeriod)
277 return _nominalRefreshPeriod;
279 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
280 if (time.flags & kCVTimeIsIndefinite)
282 _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
283 return _nominalRefreshPeriod;
288 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
289 CVDisplayLinkStart(_link);
292 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
294 WineDisplayLink* link = displayLinkContext;
296 return kCVReturnSuccess;
302 @interface WineContentView : NSView <NSTextInputClient>
304 NSMutableArray* glContexts;
305 NSMutableArray* pendingGlContexts;
306 BOOL _everHadGLContext;
307 BOOL _cachedHasGLDescendant;
308 BOOL _cachedHasGLDescendantValid;
309 BOOL clearedGlSurface;
311 NSMutableAttributedString* markedText;
312 NSRange markedTextSelection;
317 @property (readonly, nonatomic) BOOL everHadGLContext;
319 - (void) addGLContext:(WineOpenGLContext*)context;
320 - (void) removeGLContext:(WineOpenGLContext*)context;
321 - (void) updateGLContexts;
323 - (void) wine_getBackingSize:(int*)outBackingSize;
324 - (void) wine_setBackingSize:(const int*)newBackingSize;
329 @interface WineWindow ()
331 @property (readwrite, nonatomic) BOOL disabled;
332 @property (readwrite, nonatomic) BOOL noActivate;
333 @property (readwrite, nonatomic) BOOL floating;
334 @property (readwrite, nonatomic) BOOL drawnSinceShown;
335 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
336 @property (retain, nonatomic) NSWindow* latentParentWindow;
338 @property (nonatomic) void* hwnd;
339 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
341 @property (nonatomic) void* surface;
342 @property (nonatomic) pthread_mutex_t* surface_mutex;
344 @property (copy, nonatomic) NSBezierPath* shape;
345 @property (copy, nonatomic) NSData* shapeData;
346 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
347 @property (readonly, nonatomic) BOOL needsTransparency;
349 @property (nonatomic) BOOL colorKeyed;
350 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
351 @property (nonatomic) BOOL usePerPixelAlpha;
353 @property (assign, nonatomic) void* imeData;
354 @property (nonatomic) BOOL commandDone;
356 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
358 - (void) updateColorSpace;
359 - (void) updateForGLSubviews;
361 - (BOOL) becameEligibleParentOrChild;
362 - (void) becameIneligibleChild;
364 - (void) windowDidDrawContent;
369 @implementation WineContentView
371 @synthesize everHadGLContext = _everHadGLContext;
375 [markedText release];
376 [glContexts release];
377 [pendingGlContexts release];
386 - (void) drawRect:(NSRect)rect
388 WineWindow* window = (WineWindow*)[self window];
390 for (WineOpenGLContext* context in pendingGlContexts)
392 if (!clearedGlSurface)
394 context.shouldClearToBlack = TRUE;
395 clearedGlSurface = TRUE;
397 context.needsUpdate = TRUE;
399 [glContexts addObjectsFromArray:pendingGlContexts];
400 [pendingGlContexts removeAllObjects];
402 if ([window contentView] != self)
405 if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
407 [[NSColor clearColor] setFill];
410 [window.shape addClip];
412 [[NSColor windowBackgroundColor] setFill];
416 if (window.surface && window.surface_mutex &&
417 !pthread_mutex_lock(window.surface_mutex))
422 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
424 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
425 CGContextRef context;
428 [window.shape addClip];
430 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
431 CGContextSetBlendMode(context, kCGBlendModeCopy);
432 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
434 for (i = 0; i < count; i++)
439 imageRect = CGRectIntersection(rects[i], dirtyRect);
440 image = create_surface_image(window.surface, &imageRect, FALSE);
444 if (window.colorKeyed)
446 CGImageRef maskedImage;
447 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
448 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
449 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
450 maskedImage = CGImageCreateWithMaskingColors(image, components);
453 CGImageRelease(image);
458 CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
460 CGImageRelease(image);
464 [window windowDidDrawContent];
467 pthread_mutex_unlock(window.surface_mutex);
470 // If the window may be transparent, then we have to invalidate the
471 // shadow every time we draw. Also, if this is the first time we've
472 // drawn since changing from transparent to opaque.
473 if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
475 window.shapeChangedSinceLastDraw = FALSE;
476 [window invalidateShadow];
480 - (void) addGLContext:(WineOpenGLContext*)context
482 BOOL hadContext = _everHadGLContext;
484 glContexts = [[NSMutableArray alloc] init];
485 if (!pendingGlContexts)
486 pendingGlContexts = [[NSMutableArray alloc] init];
488 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
490 [glContexts addObject:context];
491 if (!clearedGlSurface)
493 context.shouldClearToBlack = TRUE;
494 clearedGlSurface = TRUE;
496 context.needsUpdate = TRUE;
500 [pendingGlContexts addObject:context];
501 [self setNeedsDisplay:YES];
504 _everHadGLContext = YES;
506 [self invalidateHasGLDescendant];
507 [(WineWindow*)[self window] updateForGLSubviews];
510 - (void) removeGLContext:(WineOpenGLContext*)context
512 [glContexts removeObjectIdenticalTo:context];
513 [pendingGlContexts removeObjectIdenticalTo:context];
514 [(WineWindow*)[self window] updateForGLSubviews];
517 - (void) updateGLContexts:(BOOL)reattach
519 for (WineOpenGLContext* context in glContexts)
521 context.needsUpdate = TRUE;
523 context.needsReattach = TRUE;
527 - (void) updateGLContexts
529 [self updateGLContexts:NO];
532 - (BOOL) _hasGLDescendant
536 if (_everHadGLContext)
538 for (WineContentView* view in [self subviews])
540 if ([view hasGLDescendant])
546 - (BOOL) hasGLDescendant
548 if (!_cachedHasGLDescendantValid)
550 _cachedHasGLDescendant = [self _hasGLDescendant];
551 _cachedHasGLDescendantValid = YES;
553 return _cachedHasGLDescendant;
556 - (void) invalidateHasGLDescendant
558 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
559 _cachedHasGLDescendantValid = NO;
560 if (invalidateAncestors && self != [[self window] contentView])
562 WineContentView* superview = (WineContentView*)[self superview];
563 if ([superview isKindOfClass:[WineContentView class]])
564 [superview invalidateHasGLDescendant];
568 - (void) wine_getBackingSize:(int*)outBackingSize
570 @synchronized(self) {
571 memcpy(outBackingSize, backingSize, sizeof(backingSize));
574 - (void) wine_setBackingSize:(const int*)newBackingSize
576 @synchronized(self) {
577 memcpy(backingSize, newBackingSize, sizeof(backingSize));
581 - (void) setRetinaMode:(int)mode
583 double scale = mode ? 0.5 : 2.0;
584 NSRect frame = self.frame;
585 frame.origin.x *= scale;
586 frame.origin.y *= scale;
587 frame.size.width *= scale;
588 frame.size.height *= scale;
589 [self setFrame:frame];
590 [self updateGLContexts];
592 for (WineContentView* subview in [self subviews])
594 if ([subview isKindOfClass:[WineContentView class]])
595 [subview setRetinaMode:mode];
599 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
604 - (BOOL) preservesContentDuringLiveResize
606 // Returning YES from this tells Cocoa to keep our view's content during
607 // a Cocoa-driven resize. In theory, we're also supposed to override
608 // -setFrameSize: to mark exposed sections as needing redisplay, but
609 // user32 will take care of that in a roundabout way. This way, we don't
610 // redraw until the window surface is flushed.
612 // This doesn't do anything when we resize the window ourselves.
616 - (BOOL)acceptsFirstResponder
618 return [[self window] contentView] == self;
621 - (BOOL) mouseDownCanMoveWindow
629 [self invalidateHasGLDescendant];
632 - (void) viewDidUnhide
634 [super viewDidUnhide];
635 [self updateGLContexts:YES];
636 [self invalidateHasGLDescendant];
639 - (void) completeText:(NSString*)text
642 WineWindow* window = (WineWindow*)[self window];
644 event = macdrv_create_event(IM_SET_TEXT, window);
645 event->im_set_text.data = [window imeData];
646 event->im_set_text.text = (CFStringRef)[text copy];
647 event->im_set_text.complete = TRUE;
649 [[window queue] postEvent:event];
651 macdrv_release_event(event);
653 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
654 markedTextSelection = NSMakeRange(0, 0);
655 [[self inputContext] discardMarkedText];
658 - (NSFocusRingType) focusRingType
660 return NSFocusRingTypeNone;
663 - (void) didAddSubview:(NSView*)subview
665 if ([subview isKindOfClass:[WineContentView class]])
667 WineContentView* view = (WineContentView*)subview;
668 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
669 [self invalidateHasGLDescendant];
671 [super didAddSubview:subview];
674 - (void) willRemoveSubview:(NSView*)subview
676 if ([subview isKindOfClass:[WineContentView class]])
678 WineContentView* view = (WineContentView*)subview;
679 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
680 [self invalidateHasGLDescendant];
682 [super willRemoveSubview:subview];
686 * ---------- NSTextInputClient methods ----------
688 - (NSTextInputContext*) inputContext
691 markedText = [[NSMutableAttributedString alloc] init];
692 return [super inputContext];
695 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
697 if ([string isKindOfClass:[NSAttributedString class]])
698 string = [string string];
700 if ([string isKindOfClass:[NSString class]])
701 [self completeText:string];
704 - (void) doCommandBySelector:(SEL)aSelector
706 [(WineWindow*)[self window] setCommandDone:TRUE];
709 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
711 if ([string isKindOfClass:[NSAttributedString class]])
712 string = [string string];
714 if ([string isKindOfClass:[NSString class]])
717 WineWindow* window = (WineWindow*)[self window];
719 if (replacementRange.location == NSNotFound)
720 replacementRange = NSMakeRange(0, [markedText length]);
722 [markedText replaceCharactersInRange:replacementRange withString:string];
723 markedTextSelection = selectedRange;
724 markedTextSelection.location += replacementRange.location;
726 event = macdrv_create_event(IM_SET_TEXT, window);
727 event->im_set_text.data = [window imeData];
728 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
729 event->im_set_text.complete = FALSE;
730 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
732 [[window queue] postEvent:event];
734 macdrv_release_event(event);
736 [[self inputContext] invalidateCharacterCoordinates];
742 [self completeText:nil];
745 - (NSRange) selectedRange
747 return markedTextSelection;
750 - (NSRange) markedRange
752 NSRange range = NSMakeRange(0, [markedText length]);
754 range.location = NSNotFound;
758 - (BOOL) hasMarkedText
760 return [markedText length] > 0;
763 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
765 if (aRange.location >= [markedText length])
768 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
770 *actualRange = aRange;
771 return [markedText attributedSubstringFromRange:aRange];
774 - (NSArray*) validAttributesForMarkedText
776 return [NSArray array];
779 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
782 WineWindow* window = (WineWindow*)[self window];
785 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
787 query = macdrv_create_query();
788 query->type = QUERY_IME_CHAR_RECT;
789 query->window = (macdrv_window)[window retain];
790 query->ime_char_rect.data = [window imeData];
791 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
793 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
795 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
796 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
797 [[WineApplicationController sharedController] flipRect:&ret];
800 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
802 macdrv_release_query(query);
805 *actualRange = aRange;
809 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
814 - (NSInteger) windowLevel
816 return [[self window] level];
822 @implementation WineWindow
824 static WineWindow* causing_becomeKeyWindow;
826 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
827 @synthesize drawnSinceShown;
828 @synthesize surface, surface_mutex;
829 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
830 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
831 @synthesize usePerPixelAlpha;
832 @synthesize imeData, commandDone;
834 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
835 windowFrame:(NSRect)window_frame
837 queue:(WineEventQueue*)queue
840 WineContentView* contentView;
841 NSTrackingArea* trackingArea;
842 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
844 [[WineApplicationController sharedController] flipRect:&window_frame];
846 window = [[[self alloc] initWithContentRect:window_frame
847 styleMask:style_mask_for_features(wf)
848 backing:NSBackingStoreBuffered
849 defer:YES] autorelease];
851 if (!window) return nil;
853 /* Standardize windows to eliminate differences between titled and
854 borderless windows and between NSWindow and NSPanel. */
855 [window setHidesOnDeactivate:NO];
856 [window setReleasedWhenClosed:NO];
858 [window setOneShot:YES];
859 [window disableCursorRects];
860 [window setShowsResizeIndicator:NO];
861 [window setHasShadow:wf->shadow];
862 [window setAcceptsMouseMovedEvents:YES];
863 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
864 [window setDelegate:window];
865 [window setBackgroundColor:[NSColor clearColor]];
866 [window setOpaque:NO];
868 window.queue = queue;
869 window->savedContentMinSize = NSZeroSize;
870 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
871 window->resizable = wf->resizable;
872 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
874 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
875 (NSString*)kUTTypeContent,
878 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
881 [contentView setAutoresizesSubviews:NO];
883 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
884 because they give us mouse moves in the background. */
885 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
886 options:(NSTrackingMouseMoved |
887 NSTrackingActiveAlways |
888 NSTrackingInVisibleRect)
890 userInfo:nil] autorelease];
893 [contentView addTrackingArea:trackingArea];
895 [window setContentView:contentView];
896 [window setInitialFirstResponder:contentView];
898 [nc addObserver:window
899 selector:@selector(updateFullscreen)
900 name:NSApplicationDidChangeScreenParametersNotification
902 [window updateFullscreen];
904 [nc addObserver:window
905 selector:@selector(applicationWillHide)
906 name:NSApplicationWillHideNotification
908 [nc addObserver:window
909 selector:@selector(applicationDidUnhide)
910 name:NSApplicationDidUnhideNotification
913 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
914 selector:@selector(checkWineDisplayLink)
915 name:NSWorkspaceActiveSpaceDidChangeNotification
916 object:[NSWorkspace sharedWorkspace]];
918 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
925 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
926 [[NSNotificationCenter defaultCenter] removeObserver:self];
928 [latentChildWindows release];
929 [latentParentWindow release];
935 - (BOOL) preventResizing
937 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
938 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
941 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
943 if (allow_immovable_windows && (disabled || inMaximized))
945 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
951 - (void) adjustFeaturesForState
953 NSUInteger style = [self styleMask];
955 if (style & NSClosableWindowMask)
956 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
957 if (style & NSMiniaturizableWindowMask)
958 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
959 if (style & NSResizableWindowMask)
960 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
961 if ([self respondsToSelector:@selector(toggleFullScreen:)])
963 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
964 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
967 if ([self preventResizing])
969 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
970 [self setContentMinSize:size];
971 [self setContentMaxSize:size];
975 [self setContentMaxSize:savedContentMaxSize];
976 [self setContentMinSize:savedContentMinSize];
979 if (allow_immovable_windows || cursor_clipping_locks_windows)
980 [self setMovable:[self allowsMovingWithMaximized:maximized]];
983 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
985 if ([self respondsToSelector:@selector(toggleFullScreen:)])
987 NSUInteger style = [self styleMask];
989 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
990 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
991 !(self.parentWindow || self.latentParentWindow))
993 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
994 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
998 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
999 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1000 if (style & NSFullScreenWindowMask)
1001 [super toggleFullScreen:nil];
1005 if (behavior != [self collectionBehavior])
1007 [self setCollectionBehavior:behavior];
1008 [self adjustFeaturesForState];
1012 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1014 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1015 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1016 NSUInteger currentStyle = [self styleMask];
1017 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1019 if (newStyle != currentStyle)
1021 NSString* title = [[[self title] copy] autorelease];
1022 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1023 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1024 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1026 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1027 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1028 // just NSTitledWindowMask, the window buttons should disappear rather
1029 // than just being disabled. But they don't. Similarly in reverse.
1030 // The workaround is to also toggle NSClosableWindowMask at the same time.
1031 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1033 [self setStyleMask:newStyle];
1035 // -setStyleMask: resets the firstResponder to the window. Set it
1036 // back to the content view.
1037 if ([[self contentView] acceptsFirstResponder])
1038 [self makeFirstResponder:[self contentView]];
1040 [self adjustFullScreenBehavior:[self collectionBehavior]];
1042 if ([[self title] length] == 0 && [title length] > 0)
1043 [self setTitle:title];
1046 resizable = wf->resizable;
1047 [self adjustFeaturesForState];
1048 [self setHasShadow:wf->shadow];
1051 // Indicates if the window would be visible if the app were not hidden.
1052 - (BOOL) wouldBeVisible
1054 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1057 - (BOOL) isOrderedIn
1059 return [self wouldBeVisible] || [self isMiniaturized];
1062 - (NSInteger) minimumLevelForActive:(BOOL)active
1066 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1067 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1068 level = NSFloatingWindowLevel;
1070 level = NSNormalWindowLevel;
1076 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1078 if (captured || fullscreen)
1081 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1083 level = NSStatusWindowLevel + 1;
1093 - (void) postDidUnminimizeEvent
1095 macdrv_event* event;
1097 /* Coalesce events by discarding any previous ones still in the queue. */
1098 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1101 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1102 [queue postEvent:event];
1103 macdrv_release_event(event);
1106 - (void) sendResizeStartQuery
1108 macdrv_query* query = macdrv_create_query();
1109 query->type = QUERY_RESIZE_START;
1110 query->window = (macdrv_window)[self retain];
1112 [self.queue query:query timeout:0.3];
1113 macdrv_release_query(query);
1116 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1118 NSWindowCollectionBehavior behavior;
1120 self.disabled = state->disabled;
1121 self.noActivate = state->no_activate;
1123 if (self.floating != state->floating)
1125 self.floating = state->floating;
1126 if (state->floating)
1128 // Became floating. If child of non-floating window, make that
1129 // relationship latent.
1130 WineWindow* parent = (WineWindow*)[self parentWindow];
1131 if (parent && !parent.floating)
1132 [self becameIneligibleChild];
1136 // Became non-floating. If parent of floating children, make that
1137 // relationship latent.
1139 for (child in [self childWineWindows])
1142 [child becameIneligibleChild];
1146 // Check our latent relationships. If floating status was the only
1147 // reason they were latent, then make them active.
1148 if ([self isVisible])
1149 [self becameEligibleParentOrChild];
1151 [[WineApplicationController sharedController] adjustWindowLevels];
1154 if (state->minimized_valid)
1156 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1158 pendingMinimize = FALSE;
1159 if (state->minimized && ![self isMiniaturized])
1161 if ([self wouldBeVisible])
1163 if ([self styleMask] & NSFullScreenWindowMask)
1165 [self postDidUnminimizeEvent];
1166 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1170 [super miniaturize:nil];
1171 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1172 event_mask_for_type(WINDOW_GOT_FOCUS) |
1173 event_mask_for_type(WINDOW_LOST_FOCUS);
1177 pendingMinimize = TRUE;
1179 else if (!state->minimized && [self isMiniaturized])
1181 ignore_windowDeminiaturize = TRUE;
1182 [self deminiaturize:nil];
1183 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1187 [queue discardEventsMatchingMask:discard forWindow:self];
1190 if (state->maximized != maximized)
1192 maximized = state->maximized;
1193 [self adjustFeaturesForState];
1195 if (!maximized && [self inLiveResize])
1196 [self sendResizeStartQuery];
1199 behavior = NSWindowCollectionBehaviorDefault;
1200 if (state->excluded_by_expose)
1201 behavior |= NSWindowCollectionBehaviorTransient;
1203 behavior |= NSWindowCollectionBehaviorManaged;
1204 if (state->excluded_by_cycle)
1206 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1207 if ([self isOrderedIn])
1208 [NSApp removeWindowsItem:self];
1212 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1213 if ([self isOrderedIn])
1214 [NSApp addWindowsItem:self title:[self title] filename:NO];
1216 [self adjustFullScreenBehavior:behavior];
1219 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1221 BOOL reordered = FALSE;
1223 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1225 if ([self level] > [child level])
1226 [child setLevel:[self level]];
1227 if (![child isVisible])
1228 [child setAutodisplay:YES];
1229 [self addChildWindow:child ordered:NSWindowAbove];
1230 [child checkWineDisplayLink];
1231 [latentChildWindows removeObjectIdenticalTo:child];
1232 child.latentParentWindow = nil;
1237 if (!latentChildWindows)
1238 latentChildWindows = [[NSMutableArray alloc] init];
1239 if (![latentChildWindows containsObject:child])
1240 [latentChildWindows addObject:child];
1241 child.latentParentWindow = self;
1247 - (BOOL) addChildWineWindow:(WineWindow*)child
1249 return [self addChildWineWindow:child assumeVisible:FALSE];
1252 - (void) removeChildWineWindow:(WineWindow*)child
1254 [self removeChildWindow:child];
1255 if (child.latentParentWindow == self)
1256 child.latentParentWindow = nil;
1257 [latentChildWindows removeObjectIdenticalTo:child];
1260 - (void) setChildWineWindows:(NSArray*)childWindows
1262 NSArray* origChildren;
1263 NSUInteger count, start, limit, i;
1265 origChildren = self.childWineWindows;
1267 // If the current and desired children arrays match up to a point, leave
1268 // those matching children alone.
1269 count = childWindows.count;
1270 limit = MIN(origChildren.count, count);
1271 for (start = 0; start < limit; start++)
1273 if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1277 // Remove all of the child windows and re-add them back-to-front so they
1278 // are in the desired order.
1279 for (i = start; i < count; i++)
1281 WineWindow* child = [childWindows objectAtIndex:i];
1282 [self removeChildWindow:child];
1284 for (i = start; i < count; i++)
1286 WineWindow* child = [childWindows objectAtIndex:i];
1287 [self addChildWindow:child ordered:NSWindowAbove];
1291 static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1293 NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1294 NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1295 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1296 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1297 if (index1 == NSNotFound)
1299 if (index2 == NSNotFound)
1300 return NSOrderedSame;
1302 return NSOrderedAscending;
1304 else if (index2 == NSNotFound)
1305 return NSOrderedDescending;
1306 else if (index1 < index2)
1307 return NSOrderedDescending;
1308 else if (index2 < index1)
1309 return NSOrderedAscending;
1311 return NSOrderedSame;
1314 - (BOOL) becameEligibleParentOrChild
1316 BOOL reordered = FALSE;
1319 if (latentParentWindow.floating || !self.floating)
1321 // If we aren't visible currently, we assume that we should be and soon
1322 // will be. So, if the latent parent is visible that's enough to assume
1323 // we can establish the parent-child relationship in Cocoa. That will
1324 // actually make us visible, which is fine.
1325 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1329 // Here, though, we may not actually be visible yet and adding a child
1330 // won't make us visible. The caller will have to call this method
1331 // again after actually making us visible.
1332 if ([self isVisible] && (count = [latentChildWindows count]))
1334 NSMutableArray* windowNumbers;
1335 NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1336 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1339 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1341 for (i = 0; i < count; i++)
1343 WineWindow* child = [latentChildWindows objectAtIndex:i];
1344 if ([child isVisible] && (self.floating || !child.floating))
1346 if (child.latentParentWindow == self)
1348 if ([self level] > [child level])
1349 [child setLevel:[self level]];
1350 [childWindows addObject:child];
1351 child.latentParentWindow = nil;
1355 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1356 [indexesToRemove addIndex:i];
1360 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1362 [childWindows sortWithOptions:NSSortStable
1363 usingComparator:^NSComparisonResult(id obj1, id obj2){
1364 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1366 [self setChildWineWindows:childWindows];
1372 - (void) becameIneligibleChild
1374 WineWindow* parent = (WineWindow*)[self parentWindow];
1377 if (!parent->latentChildWindows)
1378 parent->latentChildWindows = [[NSMutableArray alloc] init];
1379 [parent->latentChildWindows insertObject:self atIndex:0];
1380 self.latentParentWindow = parent;
1381 [parent removeChildWindow:self];
1385 - (void) becameIneligibleParentOrChild
1387 NSArray* childWindows = [self childWineWindows];
1389 [self becameIneligibleChild];
1391 if ([childWindows count])
1395 for (child in childWindows)
1397 child.latentParentWindow = self;
1398 [self removeChildWindow:child];
1401 if (latentChildWindows)
1402 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1404 latentChildWindows = [childWindows mutableCopy];
1408 // Determine if, among Wine windows, this window is directly above or below
1409 // a given other Wine window with no other Wine window intervening.
1410 // Intervening non-Wine windows are ignored.
1411 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1413 NSNumber* windowNumber;
1414 NSNumber* otherWindowNumber;
1415 NSArray* windowNumbers;
1416 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1418 if (![self isVisible] || ![otherWindow isVisible])
1421 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1422 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1423 windowNumbers = [[self class] windowNumbersWithOptions:0];
1424 windowIndex = [windowNumbers indexOfObject:windowNumber];
1425 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1427 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1430 if (orderingMode == NSWindowAbove)
1432 lowIndex = windowIndex;
1433 highIndex = otherWindowIndex;
1435 else if (orderingMode == NSWindowBelow)
1437 lowIndex = otherWindowIndex;
1438 highIndex = windowIndex;
1443 if (highIndex <= lowIndex)
1446 for (i = lowIndex + 1; i < highIndex; i++)
1448 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1449 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1450 if ([interveningWindow isKindOfClass:[WineWindow class]])
1457 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1459 NSMutableArray* windowNumbers;
1460 NSNumber* childWindowNumber;
1461 NSUInteger otherIndex;
1462 NSArray* origChildren;
1463 NSMutableArray* children;
1465 // Get the z-order from the window server and modify it to reflect the
1466 // requested window ordering.
1467 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1468 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1469 [windowNumbers removeObject:childWindowNumber];
1472 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1473 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1475 else if (mode == NSWindowAbove)
1476 [windowNumbers insertObject:childWindowNumber atIndex:0];
1478 [windowNumbers addObject:childWindowNumber];
1480 // Get our child windows and sort them in the reverse of the desired
1481 // z-order (back-to-front).
1482 origChildren = [self childWineWindows];
1483 children = [[origChildren mutableCopy] autorelease];
1484 [children sortWithOptions:NSSortStable
1485 usingComparator:^NSComparisonResult(id obj1, id obj2){
1486 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1489 [self setChildWineWindows:children];
1492 // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1493 // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1494 // (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
1495 // if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1496 // otherwise, the two ancestors are each roots of disjoint window trees
1497 // (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
1498 // (non-nil, nil) self is a descendent of other and *ancestor is the direct child
1499 - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1501 NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1505 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1511 *ancestorOfOther = child;
1515 [otherAncestors addObject:parent];
1519 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1522 NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1523 if (index != NSNotFound)
1527 *ancestorOfOther = nil;
1529 *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1535 *ancestorOfOther = otherAncestors.lastObject;;
1538 /* Returns whether or not the window was ordered in, which depends on if
1539 its frame intersects any screen. */
1540 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1542 WineApplicationController* controller = [WineApplicationController sharedController];
1543 if (![self isMiniaturized])
1545 BOOL needAdjustWindowLevels = FALSE;
1550 [controller transformProcessToForeground];
1551 if ([NSApp isHidden])
1553 wasVisible = [self isVisible];
1556 [NSApp activateIgnoringOtherApps:YES];
1558 NSDisableScreenUpdates();
1560 if ([self becameEligibleParentOrChild])
1561 needAdjustWindowLevels = TRUE;
1565 WineWindow* other = [prev isVisible] ? prev : next;
1566 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1568 if (![self isOrdered:orderingMode relativeTo:other])
1570 WineWindow* ancestor;
1571 WineWindow* ancestorOfOther;
1573 [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1576 [self setAutodisplay:YES];
1577 if (ancestorOfOther)
1579 // This window level may not be right for this window based
1580 // on floating-ness, fullscreen-ness, etc. But we set it
1581 // temporarily to allow us to order the windows properly.
1582 // Then the levels get fixed by -adjustWindowLevels.
1583 if ([ancestor level] != [ancestorOfOther level])
1584 [ancestor setLevel:[ancestorOfOther level]];
1586 parent = (WineWindow*)ancestor.parentWindow;
1587 if ([parent isKindOfClass:[WineWindow class]])
1588 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1590 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1594 (parent = (WineWindow*)child.parentWindow);
1597 if ([parent isKindOfClass:[WineWindow class]])
1598 [parent order:-orderingMode childWindow:child relativeTo:nil];
1599 if (parent == ancestor)
1603 [self checkWineDisplayLink];
1604 needAdjustWindowLevels = TRUE;
1611 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1614 [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1617 // Again, temporarily set level to make sure we can order to
1619 next = [controller frontWineWindow];
1620 if (next && [self level] < [next level])
1621 [self setLevel:[next level]];
1622 [self setAutodisplay:YES];
1623 [self orderFront:nil];
1624 [self checkWineDisplayLink];
1625 needAdjustWindowLevels = TRUE;
1627 pendingOrderOut = FALSE;
1629 if ([self becameEligibleParentOrChild])
1630 needAdjustWindowLevels = TRUE;
1632 if (needAdjustWindowLevels)
1634 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1635 [controller updateFullscreenWindows];
1636 [controller adjustWindowLevels];
1639 if (pendingMinimize)
1641 [super miniaturize:nil];
1642 pendingMinimize = FALSE;
1645 NSEnableScreenUpdates();
1647 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1648 Generate a frame-changed event just in case. The back end will ignore
1649 it if nothing actually changed. */
1650 [self windowDidResize:nil];
1652 if (![self isExcludedFromWindowsMenu])
1653 [NSApp addWindowsItem:self title:[self title] filename:NO];
1659 WineApplicationController* controller = [WineApplicationController sharedController];
1660 BOOL wasVisible = [self isVisible];
1661 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1663 [self endWindowDragging];
1664 [controller windowWillOrderOut:self];
1666 if (enteringFullScreen || exitingFullScreen)
1668 pendingOrderOut = TRUE;
1669 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1670 event_mask_for_type(WINDOW_GOT_FOCUS) |
1671 event_mask_for_type(WINDOW_LOST_FOCUS) |
1672 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1673 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1674 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1679 pendingOrderOut = FALSE;
1681 if ([self isMiniaturized])
1682 pendingMinimize = TRUE;
1684 WineWindow* parent = (WineWindow*)self.parentWindow;
1685 if ([parent isKindOfClass:[WineWindow class]])
1686 [parent grabDockIconSnapshotFromWindow:self force:NO];
1688 [self becameIneligibleParentOrChild];
1689 if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1693 fakingClose = FALSE;
1696 [self orderOut:nil];
1697 [self checkWineDisplayLink];
1698 [self setBackgroundColor:[NSColor clearColor]];
1699 [self setOpaque:NO];
1700 drawnSinceShown = NO;
1701 savedVisibleState = FALSE;
1702 if (wasVisible && wasOnActiveSpace && fullscreen)
1703 [controller updateFullscreenWindows];
1704 [controller adjustWindowLevels];
1705 [NSApp removeWindowsItem:self];
1707 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1708 event_mask_for_type(WINDOW_GOT_FOCUS) |
1709 event_mask_for_type(WINDOW_LOST_FOCUS) |
1710 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1711 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1712 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1716 - (void) updateFullscreen
1718 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1719 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1721 if (nowFullscreen != fullscreen)
1723 WineApplicationController* controller = [WineApplicationController sharedController];
1725 fullscreen = nowFullscreen;
1726 if ([self isVisible] && [self isOnActiveSpace])
1727 [controller updateFullscreenWindows];
1729 [controller adjustWindowLevels];
1733 - (void) setFrameAndWineFrame:(NSRect)frame
1735 [self setFrame:frame display:YES];
1738 roundedWineFrame = self.frame;
1740 #if CGFLOAT_IS_DOUBLE
1741 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1742 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1743 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1744 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1745 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1746 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1747 roundedWineFrame = wineFrame;
1749 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1750 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1751 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1752 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1753 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1754 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1755 roundedWineFrame = wineFrame;
1759 - (void) setFrameFromWine:(NSRect)contentRect
1761 /* Origin is (left, top) in a top-down space. Need to convert it to
1762 (left, bottom) in a bottom-up space. */
1763 [[WineApplicationController sharedController] flipRect:&contentRect];
1765 /* The back end is establishing a new window size and position. It's
1766 not interested in any stale events regarding those that may be sitting
1768 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1771 if (!NSIsEmptyRect(contentRect))
1773 NSRect frame, oldFrame;
1775 oldFrame = self.wine_fractionalFrame;
1776 frame = [self frameRectForContentRect:contentRect];
1777 if (!NSEqualRects(frame, oldFrame))
1779 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1780 BOOL needEnableScreenUpdates = FALSE;
1782 if ([self preventResizing])
1784 // Allow the following calls to -setFrame:display: to work even
1785 // if they would violate the content size constraints. This
1786 // shouldn't be necessary since the content size constraints are
1787 // documented to not constrain that method, but it seems to be.
1788 [self setContentMinSize:NSZeroSize];
1789 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1792 if (equalSizes && [[self childWineWindows] count])
1794 // If we change the window frame such that the origin moves
1795 // but the size doesn't change, then Cocoa moves child
1796 // windows with the parent. We don't want that so we fake
1797 // a change of the size and then change it back.
1798 NSRect bogusFrame = frame;
1799 bogusFrame.size.width++;
1801 NSDisableScreenUpdates();
1802 needEnableScreenUpdates = TRUE;
1804 ignore_windowResize = TRUE;
1805 [self setFrame:bogusFrame display:NO];
1806 ignore_windowResize = FALSE;
1809 [self setFrameAndWineFrame:frame];
1810 if ([self preventResizing])
1812 [self setContentMinSize:contentRect.size];
1813 [self setContentMaxSize:contentRect.size];
1816 if (needEnableScreenUpdates)
1817 NSEnableScreenUpdates();
1820 [self updateColorSpace];
1822 if (!enteringFullScreen &&
1823 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1824 nonFullscreenFrame = frame;
1826 [self updateFullscreen];
1828 if ([self isOrderedIn])
1830 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1831 event. The back end will ignore it if nothing actually changed. */
1832 [self windowDidResize:nil];
1838 - (NSRect) wine_fractionalFrame
1840 NSRect frame = self.frame;
1841 if (NSEqualRects(frame, roundedWineFrame))
1846 - (void) setMacDrvParentWindow:(WineWindow*)parent
1848 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1849 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1851 [oldParent removeChildWineWindow:self];
1852 [latentParentWindow removeChildWineWindow:self];
1853 if ([parent addChildWineWindow:self])
1854 [[WineApplicationController sharedController] adjustWindowLevels];
1855 [self adjustFullScreenBehavior:[self collectionBehavior]];
1859 - (void) setDisabled:(BOOL)newValue
1861 if (disabled != newValue)
1863 disabled = newValue;
1864 [self adjustFeaturesForState];
1868 - (BOOL) needsTransparency
1870 return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1871 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1874 - (void) checkTransparency
1876 if (![self isOpaque] && !self.needsTransparency)
1878 self.shapeChangedSinceLastDraw = TRUE;
1879 [[self contentView] setNeedsDisplay:YES];
1880 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1881 [self setOpaque:YES];
1883 else if ([self isOpaque] && self.needsTransparency)
1885 self.shapeChangedSinceLastDraw = TRUE;
1886 [[self contentView] setNeedsDisplay:YES];
1887 [self setBackgroundColor:[NSColor clearColor]];
1888 [self setOpaque:NO];
1892 - (void) setShape:(NSBezierPath*)newShape
1894 if (shape == newShape) return;
1898 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1902 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1904 shape = [newShape copy];
1905 self.shapeChangedSinceLastDraw = TRUE;
1907 [self checkTransparency];
1910 - (void) makeFocused:(BOOL)activate
1914 [[WineApplicationController sharedController] transformProcessToForeground];
1915 [NSApp activateIgnoringOtherApps:YES];
1918 causing_becomeKeyWindow = self;
1919 [self makeKeyWindow];
1920 causing_becomeKeyWindow = nil;
1922 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1923 event_mask_for_type(WINDOW_LOST_FOCUS)
1927 - (void) postKey:(uint16_t)keyCode
1928 pressed:(BOOL)pressed
1929 modifiers:(NSUInteger)modifiers
1930 event:(NSEvent*)theEvent
1932 macdrv_event* event;
1934 WineApplicationController* controller = [WineApplicationController sharedController];
1936 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1937 event->key.keycode = keyCode;
1938 event->key.modifiers = modifiers;
1939 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1941 if ((cgevent = [theEvent CGEvent]))
1942 controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
1944 [queue postEvent:event];
1946 macdrv_release_event(event);
1948 [controller noteKey:keyCode pressed:pressed];
1951 - (void) postKeyEvent:(NSEvent *)theEvent
1953 [self flagsChanged:theEvent];
1954 [self postKey:[theEvent keyCode]
1955 pressed:[theEvent type] == NSKeyDown
1956 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1960 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1962 savedContentMinSize = minSize;
1963 savedContentMaxSize = maxSize;
1964 if (![self preventResizing])
1966 [self setContentMinSize:minSize];
1967 [self setContentMaxSize:maxSize];
1971 - (WineWindow*) ancestorWineWindow
1973 WineWindow* ancestor = self;
1976 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1977 if ([parent isKindOfClass:[WineWindow class]])
1985 - (void) postBroughtForwardEvent
1987 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1988 [queue postEvent:event];
1989 macdrv_release_event(event);
1992 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
1994 macdrv_event* event;
1995 NSUInteger style = self.styleMask;
1998 style |= NSFullScreenWindowMask;
2000 style &= ~NSFullScreenWindowMask;
2001 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2002 [[WineApplicationController sharedController] flipRect:&frame];
2004 /* Coalesce events by discarding any previous ones still in the queue. */
2005 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2008 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2009 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2010 event->window_frame_changed.fullscreen = isFullscreen;
2011 event->window_frame_changed.in_resize = resizing;
2012 [queue postEvent:event];
2013 macdrv_release_event(event);
2016 - (void) updateForCursorClipping
2018 [self adjustFeaturesForState];
2021 - (void) endWindowDragging
2025 if (draggingPhase == 3)
2027 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2028 [queue postEvent:event];
2029 macdrv_release_event(event);
2033 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2037 - (NSMutableDictionary*) displayIDToDisplayLinkMap
2039 static NSMutableDictionary* displayIDToDisplayLinkMap;
2040 if (!displayIDToDisplayLinkMap)
2042 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2044 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2047 usingBlock:^(NSNotification *note){
2048 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2049 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2050 [badDisplayIDs minusSet:validDisplayIDs];
2051 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2054 return displayIDToDisplayLinkMap;
2057 - (WineDisplayLink*) wineDisplayLink
2059 if (!_lastDisplayID)
2062 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2063 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2066 - (void) checkWineDisplayLink
2068 NSScreen* screen = self.screen;
2069 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2071 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2072 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2076 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2077 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2078 if (displayID == _lastDisplayID)
2081 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2085 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2086 [link removeWindow:self];
2090 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2093 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2094 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2096 [link addWindow:self];
2097 [self displayIfNeeded];
2099 _lastDisplayID = displayID;
2102 - (BOOL) isEmptyShaped
2104 return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2107 - (BOOL) canProvideSnapshot
2109 return (self.windowNumber > 0 && ![self isEmptyShaped]);
2112 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2114 if (![self isEmptyShaped])
2117 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2118 if (!force && now < lastDockIconSnapshot + 1)
2123 if (![window canProvideSnapshot])
2129 for (WineWindow* childWindow in self.childWindows)
2131 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2134 NSSize size = childWindow.frame.size;
2135 CGFloat area = size.width * size.height;
2136 if (!window || area > bestArea)
2138 window = childWindow;
2147 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2148 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2149 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2150 CFRelease(windowIDs);
2154 NSImage* appImage = [NSApp applicationIconImage];
2156 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2158 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2159 [dockIcon lockFocus];
2161 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2163 CGRect rect = CGRectMake(8, 8, 240, 240);
2164 size_t width = CGImageGetWidth(windowImage);
2165 size_t height = CGImageGetHeight(windowImage);
2168 rect.size.height *= height / (double)width;
2169 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2171 else if (width != height)
2173 rect.size.width *= width / (double)height;
2174 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2177 CGContextDrawImage(cgcontext, rect, windowImage);
2178 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2180 operation:NSCompositeSourceOver
2185 [dockIcon unlockFocus];
2187 CGImageRelease(windowImage);
2189 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2190 if (![imageView isKindOfClass:[NSImageView class]])
2192 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2193 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2194 self.dockTile.contentView = imageView;
2196 imageView.image = dockIcon;
2197 [self.dockTile display];
2198 lastDockIconSnapshot = now;
2201 - (void) checkEmptyShaped
2203 if (self.dockTile.contentView && ![self isEmptyShaped])
2205 self.dockTile.contentView = nil;
2206 lastDockIconSnapshot = 0;
2208 [self checkWineDisplayLink];
2213 * ---------- NSWindow method overrides ----------
2215 - (BOOL) canBecomeKeyWindow
2217 if (causing_becomeKeyWindow == self) return YES;
2218 if (self.disabled || self.noActivate) return NO;
2219 return [self isKeyWindow];
2222 - (BOOL) canBecomeMainWindow
2224 return [self canBecomeKeyWindow];
2227 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2229 // If a window is sized to completely cover a screen, then it's in
2230 // full-screen mode. In that case, we don't allow NSWindow to constrain
2232 NSArray* screens = [NSScreen screens];
2233 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2234 if (!screen_covered_by_rect(contentRect, screens) &&
2235 frame_intersects_screens(frameRect, screens))
2236 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2240 // This private method of NSWindow is called as Cocoa reacts to the display
2241 // configuration changing. Among other things, it adjusts the window's
2242 // frame based on how the screen(s) changed size. That tells Wine that the
2243 // window has been moved. We don't want that. Rather, we want to make
2244 // sure that the WinAPI notion of the window position is maintained/
2245 // restored, possibly undoing or overriding Cocoa's adjustment.
2247 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2248 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2249 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2250 // reassert its notion of the position. That call won't get processed
2251 // until after this method returns, so it will override whatever this
2252 // method does to the window position. It will also discard any pending
2253 // WINDOW_FRAME_CHANGED events.
2255 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2256 // adjust the window's position due to a display change is to hook into
2257 // this private method. This private method has remained stable from 10.6
2258 // through 10.11. If it does change, the most likely thing is that it
2259 // will be removed and no longer called and this fix will simply stop
2260 // working. The only real danger would be if Apple changed the return type
2261 // to a struct or floating-point type, which would change the calling
2263 - (id) _displayChanged
2265 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2266 [queue postEvent:event];
2267 macdrv_release_event(event);
2269 return [super _displayChanged];
2272 - (BOOL) isExcludedFromWindowsMenu
2274 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2277 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2279 BOOL ret = [super validateMenuItem:menuItem];
2281 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2282 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2283 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2289 /* We don't call this. It's the action method of the items in the Window menu. */
2290 - (void) makeKeyAndOrderFront:(id)sender
2292 if ([self isMiniaturized])
2293 [self deminiaturize:nil];
2294 [self orderBelow:nil orAbove:nil activate:NO];
2295 [[self ancestorWineWindow] postBroughtForwardEvent];
2297 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2298 [[WineApplicationController sharedController] windowGotFocus:self];
2301 - (void) sendEvent:(NSEvent*)event
2303 NSEventType type = event.type;
2305 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2306 interface control. For example, Control-Tab switches focus among
2307 views. We want to bypass that feature, so directly route key-down
2308 events to -keyDown:. */
2309 if (type == NSKeyDown)
2310 [[self firstResponder] keyDown:event];
2313 if (!draggingPhase && maximized && ![self isMovable] &&
2314 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2315 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2317 NSRect titleBar = self.frame;
2318 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2319 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2320 titleBar.origin.y = NSMaxY(contentRect);
2322 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2324 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2326 static const NSWindowButton buttons[] = {
2327 NSWindowCloseButton,
2328 NSWindowMiniaturizeButton,
2330 NSWindowFullScreenButton,
2332 BOOL hitButton = NO;
2335 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2339 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2342 button = [self standardWindowButton:buttons[i]];
2343 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2353 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2354 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2358 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2360 if ([self isMovable])
2362 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2363 NSPoint newTopLeft = dragWindowStartPosition;
2365 newTopLeft.x += point.x - dragStartPosition.x;
2366 newTopLeft.y += point.y - dragStartPosition.y;
2368 if (draggingPhase == 2)
2370 macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2371 mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2372 [queue postEvent:mevent];
2373 macdrv_release_event(mevent);
2378 [self setFrameTopLeftPoint:newTopLeft];
2380 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2382 macdrv_event* event;
2383 NSRect frame = [self contentRectForFrameRect:self.frame];
2385 [[WineApplicationController sharedController] flipRect:&frame];
2387 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2388 event->window_restore_requested.keep_frame = TRUE;
2389 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2390 [queue postEvent:event];
2391 macdrv_release_event(event);
2396 if (type == NSLeftMouseUp)
2397 [self endWindowDragging];
2400 [super sendEvent:event];
2404 - (void) miniaturize:(id)sender
2406 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2407 [queue postEvent:event];
2408 macdrv_release_event(event);
2410 WineWindow* parent = (WineWindow*)self.parentWindow;
2411 if ([parent isKindOfClass:[WineWindow class]])
2412 [parent grabDockIconSnapshotFromWindow:self force:YES];
2415 - (void) toggleFullScreen:(id)sender
2417 if (!self.disabled && !maximized)
2418 [super toggleFullScreen:sender];
2421 - (void) setViewsNeedDisplay:(BOOL)value
2423 if (value && ![self viewsNeedDisplay])
2425 WineDisplayLink* link = [self wineDisplayLink];
2428 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2429 if (_lastDisplayTime + [link refreshPeriod] < now)
2430 [self setAutodisplay:YES];
2434 _lastDisplayTime = now;
2438 [self setAutodisplay:YES];
2440 [super setViewsNeedDisplay:value];
2445 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2448 [self setAutodisplay:NO];
2451 - (void) displayIfNeeded
2453 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2454 [super displayIfNeeded];
2456 [self setAutodisplay:NO];
2459 - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2462 [self setAutodisplay:YES];
2463 [super setFrame:frameRect display:flag];
2466 - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2469 [self setAutodisplay:YES];
2470 [super setFrame:frameRect display:displayFlag animate:animateFlag];
2473 - (void) windowDidDrawContent
2475 if (!drawnSinceShown)
2477 drawnSinceShown = YES;
2478 dispatch_async(dispatch_get_main_queue(), ^{
2479 [self checkTransparency];
2484 - (NSArray*) childWineWindows
2486 NSArray* childWindows = self.childWindows;
2487 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2488 return [child isKindOfClass:[WineWindow class]];
2490 return [childWindows objectsAtIndexes:indexes];
2493 // We normally use the generic/calibrated RGB color space for the window,
2494 // rather than the device color space, to avoid expensive color conversion
2495 // which slows down drawing. However, for windows displaying OpenGL, having
2496 // a different color space than the screen greatly reduces frame rates, often
2497 // limiting it to the display refresh rate.
2499 // To avoid this, we switch back to the screen color space whenever the
2500 // window is covered by a view with an attached OpenGL context.
2501 - (void) updateColorSpace
2503 NSRect contentRect = [[self contentView] frame];
2504 BOOL coveredByGLView = FALSE;
2505 WineContentView* view = (WineContentView*)[[self contentView] hitTest:NSMakePoint(NSMidX(contentRect), NSMidY(contentRect))];
2506 if ([view isKindOfClass:[WineContentView class]] && view.everHadGLContext)
2508 NSRect frame = [view convertRect:[view bounds] toView:nil];
2509 if (NSContainsRect(frame, contentRect))
2510 coveredByGLView = TRUE;
2513 if (coveredByGLView)
2514 [self setColorSpace:nil];
2516 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
2519 - (void) updateForGLSubviews
2521 [self updateColorSpace];
2522 if (gl_surface_mode == GL_SURFACE_BEHIND)
2523 [self checkTransparency];
2526 - (void) setRetinaMode:(int)mode
2529 double scale = mode ? 0.5 : 2.0;
2530 NSAffineTransform* transform = [NSAffineTransform transform];
2532 [transform scaleBy:scale];
2535 [shape transformUsingAffineTransform:transform];
2537 for (WineContentView* subview in [self.contentView subviews])
2539 if ([subview isKindOfClass:[WineContentView class]])
2540 [subview setRetinaMode:mode];
2543 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2544 frame.origin.x *= scale;
2545 frame.origin.y *= scale;
2546 frame.size.width *= scale;
2547 frame.size.height *= scale;
2548 frame = [self frameRectForContentRect:frame];
2550 savedContentMinSize = [transform transformSize:savedContentMinSize];
2551 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2552 savedContentMaxSize.width *= scale;
2553 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2554 savedContentMaxSize.height *= scale;
2556 self.contentMinSize = [transform transformSize:self.contentMinSize];
2557 NSSize temp = self.contentMaxSize;
2558 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2559 temp.width *= scale;
2560 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2561 temp.height *= scale;
2562 self.contentMaxSize = temp;
2564 ignore_windowResize = TRUE;
2565 [self setFrameAndWineFrame:frame];
2566 ignore_windowResize = FALSE;
2571 * ---------- NSResponder method overrides ----------
2573 - (void) keyDown:(NSEvent *)theEvent
2575 if ([theEvent isARepeat])
2577 if (!allowKeyRepeats)
2581 allowKeyRepeats = YES;
2583 [self postKeyEvent:theEvent];
2586 - (void) flagsChanged:(NSEvent *)theEvent
2588 static const struct {
2592 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2593 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2594 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2595 { NX_DEVICELCTLKEYMASK, kVK_Control },
2596 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2597 { NX_DEVICELALTKEYMASK, kVK_Option },
2598 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2599 { NX_DEVICELCMDKEYMASK, kVK_Command },
2600 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2603 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
2605 int i, last_changed;
2607 fix_device_modifiers_by_generic(&modifierFlags);
2608 changed = modifierFlags ^ lastModifierFlags;
2611 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2612 if (changed & modifiers[i].mask)
2615 for (i = 0; i <= last_changed; i++)
2617 if (changed & modifiers[i].mask)
2619 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2622 allowKeyRepeats = NO;
2624 if (i == last_changed)
2625 lastModifierFlags = modifierFlags;
2628 lastModifierFlags ^= modifiers[i].mask;
2629 fix_generic_modifiers_by_device(&lastModifierFlags);
2632 // Caps lock generates one event for each press-release action.
2633 // We need to simulate a pair of events for each actual event.
2634 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2636 [self postKey:modifiers[i].keycode
2638 modifiers:lastModifierFlags
2639 event:(NSEvent*)theEvent];
2643 [self postKey:modifiers[i].keycode
2645 modifiers:lastModifierFlags
2646 event:(NSEvent*)theEvent];
2651 - (void) applicationWillHide
2653 savedVisibleState = [self isVisible];
2656 - (void) applicationDidUnhide
2658 if ([self isVisible])
2659 [self becameEligibleParentOrChild];
2664 * ---------- NSWindowDelegate methods ----------
2666 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2668 macdrv_query* query;
2671 query = macdrv_create_query();
2672 query->type = QUERY_MIN_MAX_INFO;
2673 query->window = (macdrv_window)[self retain];
2674 [self.queue query:query timeout:0.5];
2675 macdrv_release_query(query);
2677 size = [self contentMaxSize];
2678 if (proposedSize.width < size.width)
2679 size.width = proposedSize.width;
2680 if (proposedSize.height < size.height)
2681 size.height = proposedSize.height;
2685 - (void)windowDidBecomeKey:(NSNotification *)notification
2687 WineApplicationController* controller = [WineApplicationController sharedController];
2688 NSEvent* event = [controller lastFlagsChanged];
2690 [self flagsChanged:event];
2692 if (causing_becomeKeyWindow == self) return;
2694 [controller windowGotFocus:self];
2697 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2699 [self checkWineDisplayLink];
2702 - (void) windowDidChangeScreen:(NSNotification*)notification
2704 [self checkWineDisplayLink];
2707 - (void)windowDidDeminiaturize:(NSNotification *)notification
2709 WineApplicationController* controller = [WineApplicationController sharedController];
2711 if (!ignore_windowDeminiaturize)
2712 [self postDidUnminimizeEvent];
2713 ignore_windowDeminiaturize = FALSE;
2715 [self becameEligibleParentOrChild];
2717 if (fullscreen && [self isOnActiveSpace])
2718 [controller updateFullscreenWindows];
2719 [controller adjustWindowLevels];
2721 if (![self parentWindow])
2722 [self postBroughtForwardEvent];
2724 if (!self.disabled && !self.noActivate)
2726 causing_becomeKeyWindow = self;
2727 [self makeKeyWindow];
2728 causing_becomeKeyWindow = nil;
2729 [controller windowGotFocus:self];
2732 [self windowDidResize:notification];
2733 [self checkWineDisplayLink];
2736 - (void) windowDidEndLiveResize:(NSNotification *)notification
2740 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2741 [queue postEvent:event];
2742 macdrv_release_event(event);
2746 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2748 enteringFullScreen = FALSE;
2749 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2750 if (pendingOrderOut)
2754 - (void) windowDidExitFullScreen:(NSNotification*)notification
2756 exitingFullScreen = FALSE;
2757 [self setFrameAndWineFrame:nonFullscreenFrame];
2758 [self windowDidResize:nil];
2759 if (pendingOrderOut)
2763 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2765 enteringFullScreen = FALSE;
2766 enteredFullScreenTime = 0;
2767 if (pendingOrderOut)
2771 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2773 exitingFullScreen = FALSE;
2774 [self windowDidResize:nil];
2775 if (pendingOrderOut)
2779 - (void)windowDidMiniaturize:(NSNotification *)notification
2781 if (fullscreen && [self isOnActiveSpace])
2782 [[WineApplicationController sharedController] updateFullscreenWindows];
2783 [self checkWineDisplayLink];
2786 - (void)windowDidMove:(NSNotification *)notification
2788 [self windowDidResize:notification];
2791 - (void)windowDidResignKey:(NSNotification *)notification
2793 macdrv_event* event;
2795 if (causing_becomeKeyWindow) return;
2797 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2798 [queue postEvent:event];
2799 macdrv_release_event(event);
2802 - (void)windowDidResize:(NSNotification *)notification
2804 NSRect frame = self.wine_fractionalFrame;
2806 if ([self inLiveResize])
2808 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2809 resizingFromLeft = TRUE;
2810 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2811 resizingFromTop = TRUE;
2814 if (ignore_windowResize || exitingFullScreen) return;
2816 if ([self preventResizing])
2818 NSRect contentRect = [self contentRectForFrameRect:frame];
2819 [self setContentMinSize:contentRect.size];
2820 [self setContentMaxSize:contentRect.size];
2823 [self postWindowFrameChanged:frame
2824 fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2825 resizing:[self inLiveResize]];
2827 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2828 [self updateFullscreen];
2831 - (BOOL)windowShouldClose:(id)sender
2833 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2834 [queue postEvent:event];
2835 macdrv_release_event(event);
2839 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2843 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2844 [queue postEvent:event];
2845 macdrv_release_event(event);
2848 else if (!resizable)
2850 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2851 [queue postEvent:event];
2852 macdrv_release_event(event);
2859 - (void) windowWillClose:(NSNotification*)notification
2863 if (fakingClose) return;
2864 if (latentParentWindow)
2866 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2867 self.latentParentWindow = nil;
2870 for (child in latentChildWindows)
2872 if (child.latentParentWindow == self)
2873 child.latentParentWindow = nil;
2875 [latentChildWindows removeAllObjects];
2878 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2880 enteringFullScreen = TRUE;
2881 nonFullscreenFrame = self.wine_fractionalFrame;
2884 - (void) windowWillExitFullScreen:(NSNotification*)notification
2886 exitingFullScreen = TRUE;
2887 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2890 - (void)windowWillMiniaturize:(NSNotification *)notification
2892 [self becameIneligibleParentOrChild];
2893 [self grabDockIconSnapshotFromWindow:nil force:NO];
2896 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2898 if ([self inLiveResize])
2901 return self.wine_fractionalFrame.size;
2904 macdrv_query* query;
2906 rect = [self frame];
2907 if (resizingFromLeft)
2908 rect.origin.x = NSMaxX(rect) - frameSize.width;
2909 if (!resizingFromTop)
2910 rect.origin.y = NSMaxY(rect) - frameSize.height;
2911 rect.size = frameSize;
2912 rect = [self contentRectForFrameRect:rect];
2913 [[WineApplicationController sharedController] flipRect:&rect];
2915 query = macdrv_create_query();
2916 query->type = QUERY_RESIZE_SIZE;
2917 query->window = (macdrv_window)[self retain];
2918 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
2919 query->resize_size.from_left = resizingFromLeft;
2920 query->resize_size.from_top = resizingFromTop;
2922 if ([self.queue query:query timeout:0.1])
2924 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
2925 rect = [self frameRectForContentRect:rect];
2926 frameSize = rect.size;
2929 macdrv_release_query(query);
2935 - (void) windowWillStartLiveResize:(NSNotification *)notification
2937 [self endWindowDragging];
2941 macdrv_event* event;
2942 NSRect frame = [self contentRectForFrameRect:self.frame];
2944 [[WineApplicationController sharedController] flipRect:&frame];
2946 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2947 event->window_restore_requested.keep_frame = TRUE;
2948 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2949 [queue postEvent:event];
2950 macdrv_release_event(event);
2953 [self sendResizeStartQuery];
2955 frameAtResizeStart = [self frame];
2956 resizingFromLeft = resizingFromTop = FALSE;
2959 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2961 macdrv_query* query;
2962 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2965 query = macdrv_create_query();
2966 query->type = QUERY_MIN_MAX_INFO;
2967 query->window = (macdrv_window)[self retain];
2968 [self.queue query:query timeout:0.5];
2969 macdrv_release_query(query);
2971 currentContentRect = [self contentRectForFrameRect:[self frame]];
2972 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2974 maxSize = [self contentMaxSize];
2975 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2976 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2978 // Try to keep the top-left corner where it is.
2979 newContentRect.origin.x = NSMinX(currentContentRect);
2980 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2982 // If that pushes the bottom or right off the screen, pull it up and to the left.
2983 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2984 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2985 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2986 if (NSMinY(newContentRect) < NSMinY(screenRect))
2987 newContentRect.origin.y = NSMinY(screenRect);
2989 // If that pushes the top or left off the screen, push it down and the right
2990 // again. Do this last because the top-left corner is more important than the
2992 if (NSMinX(newContentRect) < NSMinX(screenRect))
2993 newContentRect.origin.x = NSMinX(screenRect);
2994 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2995 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2997 return [self frameRectForContentRect:newContentRect];
3002 * ---------- NSPasteboardOwner methods ----------
3004 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3006 macdrv_query* query = macdrv_create_query();
3007 query->type = QUERY_PASTEBOARD_DATA;
3008 query->window = (macdrv_window)[self retain];
3009 query->pasteboard_data.type = (CFStringRef)[type copy];
3011 [self.queue query:query timeout:3];
3012 macdrv_release_query(query);
3015 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3017 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3018 [queue postEvent:event];
3019 macdrv_release_event(event);
3024 * ---------- NSDraggingDestination methods ----------
3026 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3028 return [self draggingUpdated:sender];
3031 - (void) draggingExited:(id <NSDraggingInfo>)sender
3033 // This isn't really a query. We don't need any response. However, it
3034 // has to be processed in a similar manner as the other drag-and-drop
3035 // queries in order to maintain the proper order of operations.
3036 macdrv_query* query = macdrv_create_query();
3037 query->type = QUERY_DRAG_EXITED;
3038 query->window = (macdrv_window)[self retain];
3040 [self.queue query:query timeout:0.1];
3041 macdrv_release_query(query);
3044 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3046 NSDragOperation ret;
3047 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3048 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3049 NSPasteboard* pb = [sender draggingPasteboard];
3051 macdrv_query* query = macdrv_create_query();
3052 query->type = QUERY_DRAG_OPERATION;
3053 query->window = (macdrv_window)[self retain];
3054 query->drag_operation.x = floor(cgpt.x);
3055 query->drag_operation.y = floor(cgpt.y);
3056 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3057 query->drag_operation.accepted_op = NSDragOperationNone;
3058 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3060 [self.queue query:query timeout:3];
3061 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3062 macdrv_release_query(query);
3067 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3070 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3071 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3072 NSPasteboard* pb = [sender draggingPasteboard];
3074 macdrv_query* query = macdrv_create_query();
3075 query->type = QUERY_DRAG_DROP;
3076 query->window = (macdrv_window)[self retain];
3077 query->drag_drop.x = floor(cgpt.x);
3078 query->drag_drop.y = floor(cgpt.y);
3079 query->drag_drop.op = [sender draggingSourceOperationMask];
3080 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3082 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3083 ret = query->status;
3084 macdrv_release_query(query);
3089 - (BOOL) wantsPeriodicDraggingUpdates
3097 /***********************************************************************
3098 * macdrv_create_cocoa_window
3100 * Create a Cocoa window with the given content frame and features (e.g.
3101 * title bar, close box, etc.).
3103 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3104 CGRect frame, void* hwnd, macdrv_event_queue queue)
3106 __block WineWindow* window;
3109 window = [[WineWindow createWindowWithFeatures:wf
3110 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3112 queue:(WineEventQueue*)queue] retain];
3115 return (macdrv_window)window;
3118 /***********************************************************************
3119 * macdrv_destroy_cocoa_window
3121 * Destroy a Cocoa window.
3123 void macdrv_destroy_cocoa_window(macdrv_window w)
3125 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3126 WineWindow* window = (WineWindow*)w;
3129 [window doOrderOut];
3132 [window.queue discardEventsMatchingMask:-1 forWindow:window];
3138 /***********************************************************************
3139 * macdrv_get_window_hwnd
3141 * Get the hwnd that was set for the window at creation.
3143 void* macdrv_get_window_hwnd(macdrv_window w)
3145 WineWindow* window = (WineWindow*)w;
3149 /***********************************************************************
3150 * macdrv_set_cocoa_window_features
3152 * Update a Cocoa window's features.
3154 void macdrv_set_cocoa_window_features(macdrv_window w,
3155 const struct macdrv_window_features* wf)
3157 WineWindow* window = (WineWindow*)w;
3160 [window setWindowFeatures:wf];
3164 /***********************************************************************
3165 * macdrv_set_cocoa_window_state
3167 * Update a Cocoa window's state.
3169 void macdrv_set_cocoa_window_state(macdrv_window w,
3170 const struct macdrv_window_state* state)
3172 WineWindow* window = (WineWindow*)w;
3175 [window setMacDrvState:state];
3179 /***********************************************************************
3180 * macdrv_set_cocoa_window_title
3182 * Set a Cocoa window's title.
3184 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3187 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3188 WineWindow* window = (WineWindow*)w;
3189 NSString* titleString;
3192 titleString = [NSString stringWithCharacters:title length:length];
3195 OnMainThreadAsync(^{
3196 [window setTitle:titleString];
3197 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3198 [NSApp changeWindowsItem:window title:titleString filename:NO];
3204 /***********************************************************************
3205 * macdrv_order_cocoa_window
3207 * Reorder a Cocoa window relative to other windows. If prev is
3208 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3209 * it is ordered above that window. Otherwise, it is ordered to the
3212 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3213 macdrv_window n, int activate)
3215 WineWindow* window = (WineWindow*)w;
3216 WineWindow* prev = (WineWindow*)p;
3217 WineWindow* next = (WineWindow*)n;
3219 OnMainThreadAsync(^{
3220 [window orderBelow:prev
3224 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3226 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3230 /***********************************************************************
3231 * macdrv_hide_cocoa_window
3233 * Hides a Cocoa window.
3235 void macdrv_hide_cocoa_window(macdrv_window w)
3237 WineWindow* window = (WineWindow*)w;
3240 [window doOrderOut];
3244 /***********************************************************************
3245 * macdrv_set_cocoa_window_frame
3247 * Move a Cocoa window.
3249 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3251 WineWindow* window = (WineWindow*)w;
3254 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3258 /***********************************************************************
3259 * macdrv_get_cocoa_window_frame
3261 * Gets the frame of a Cocoa window.
3263 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3265 WineWindow* window = (WineWindow*)w;
3270 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3271 [[WineApplicationController sharedController] flipRect:&frame];
3272 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3276 /***********************************************************************
3277 * macdrv_set_cocoa_parent_window
3279 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3280 * the parent window.
3282 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3284 WineWindow* window = (WineWindow*)w;
3287 [window setMacDrvParentWindow:(WineWindow*)parent];
3291 /***********************************************************************
3292 * macdrv_set_window_surface
3294 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3296 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3297 WineWindow* window = (WineWindow*)w;
3300 window.surface = surface;
3301 window.surface_mutex = mutex;
3307 /***********************************************************************
3308 * macdrv_window_needs_display
3310 * Mark a window as needing display in a specified rect (in non-client
3311 * area coordinates).
3313 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3315 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3316 WineWindow* window = (WineWindow*)w;
3318 OnMainThreadAsync(^{
3319 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3325 /***********************************************************************
3326 * macdrv_set_window_shape
3328 * Sets the shape of a Cocoa window from an array of rectangles. If
3329 * rects is NULL, resets the window's shape to its frame.
3331 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3333 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3334 WineWindow* window = (WineWindow*)w;
3337 if (!rects || !count)
3340 window.shapeData = nil;
3341 [window checkEmptyShaped];
3345 size_t length = sizeof(*rects) * count;
3346 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3351 path = [NSBezierPath bezierPath];
3352 for (i = 0; i < count; i++)
3353 [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3354 window.shape = path;
3355 window.shapeData = [NSData dataWithBytes:rects length:length];
3356 [window checkEmptyShaped];
3364 /***********************************************************************
3365 * macdrv_set_window_alpha
3367 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3369 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3370 WineWindow* window = (WineWindow*)w;
3372 [window setAlphaValue:alpha];
3377 /***********************************************************************
3378 * macdrv_set_window_color_key
3380 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3383 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3384 WineWindow* window = (WineWindow*)w;
3387 window.colorKeyed = TRUE;
3388 window.colorKeyRed = keyRed;
3389 window.colorKeyGreen = keyGreen;
3390 window.colorKeyBlue = keyBlue;
3391 [window checkTransparency];
3397 /***********************************************************************
3398 * macdrv_clear_window_color_key
3400 void macdrv_clear_window_color_key(macdrv_window w)
3402 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3403 WineWindow* window = (WineWindow*)w;
3406 window.colorKeyed = FALSE;
3407 [window checkTransparency];
3413 /***********************************************************************
3414 * macdrv_window_use_per_pixel_alpha
3416 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3418 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3419 WineWindow* window = (WineWindow*)w;
3422 window.usePerPixelAlpha = use_per_pixel_alpha;
3423 [window checkTransparency];
3429 /***********************************************************************
3430 * macdrv_give_cocoa_window_focus
3432 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3433 * orders it front and, if its frame was not within the desktop bounds,
3434 * Cocoa will typically move it on-screen.
3436 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3438 WineWindow* window = (WineWindow*)w;
3441 [window makeFocused:activate];
3445 /***********************************************************************
3446 * macdrv_set_window_min_max_sizes
3448 * Sets the window's minimum and maximum content sizes.
3450 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3452 WineWindow* window = (WineWindow*)w;
3455 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3459 /***********************************************************************
3460 * macdrv_create_view
3462 * Creates and returns a view with the specified frame rect. The
3463 * caller is responsible for calling macdrv_dispose_view() on the view
3464 * when it is done with it.
3466 macdrv_view macdrv_create_view(CGRect rect)
3468 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3469 __block WineContentView* view;
3471 if (CGRectIsNull(rect)) rect = CGRectZero;
3474 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3476 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3477 [view setAutoresizesSubviews:NO];
3478 [view setHidden:YES];
3479 [nc addObserver:view
3480 selector:@selector(updateGLContexts)
3481 name:NSViewGlobalFrameDidChangeNotification
3483 [nc addObserver:view
3484 selector:@selector(updateGLContexts)
3485 name:NSApplicationDidChangeScreenParametersNotification
3490 return (macdrv_view)view;
3493 /***********************************************************************
3494 * macdrv_dispose_view
3496 * Destroys a view previously returned by macdrv_create_view.
3498 void macdrv_dispose_view(macdrv_view v)
3500 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3501 WineContentView* view = (WineContentView*)v;
3504 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3505 WineWindow* window = (WineWindow*)[view window];
3507 [nc removeObserver:view
3508 name:NSViewGlobalFrameDidChangeNotification
3510 [nc removeObserver:view
3511 name:NSApplicationDidChangeScreenParametersNotification
3513 [view removeFromSuperview];
3515 [window updateForGLSubviews];
3521 /***********************************************************************
3522 * macdrv_set_view_frame
3524 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3526 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3527 WineContentView* view = (WineContentView*)v;
3529 if (CGRectIsNull(rect)) rect = CGRectZero;
3531 OnMainThreadAsync(^{
3532 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3533 NSRect oldFrame = [view frame];
3535 if (!NSEqualRects(oldFrame, newFrame))
3537 [[view superview] setNeedsDisplayInRect:oldFrame];
3538 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3539 [view setFrameSize:newFrame.size];
3540 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3541 [view setFrameOrigin:newFrame.origin];
3543 [view setFrame:newFrame];
3544 [view setNeedsDisplay:YES];
3548 int backing_size[2] = { 0 };
3549 [view wine_setBackingSize:backing_size];
3551 [(WineWindow*)[view window] updateForGLSubviews];
3558 /***********************************************************************
3559 * macdrv_set_view_superview
3561 * Move a view to a new superview and position it relative to its
3562 * siblings. If p is non-NULL, the view is ordered behind it.
3563 * Otherwise, the view is ordered above n. If s is NULL, use the
3564 * content view of w as the new superview.
3566 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3568 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3569 WineContentView* view = (WineContentView*)v;
3570 WineContentView* superview = (WineContentView*)s;
3571 WineWindow* window = (WineWindow*)w;
3572 WineContentView* prev = (WineContentView*)p;
3573 WineContentView* next = (WineContentView*)n;
3576 superview = [window contentView];
3578 OnMainThreadAsync(^{
3579 if (superview == [view superview])
3581 NSArray* subviews = [superview subviews];
3582 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3583 if (!prev && !next && index == [subviews count] - 1)
3585 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3587 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3591 WineWindow* oldWindow = (WineWindow*)[view window];
3592 WineWindow* newWindow = (WineWindow*)[superview window];
3594 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3595 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3596 [view removeFromSuperview];
3599 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3601 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3603 if (oldWindow != newWindow)
3605 [oldWindow updateForGLSubviews];
3606 [newWindow updateForGLSubviews];
3613 /***********************************************************************
3614 * macdrv_set_view_hidden
3616 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3618 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3619 WineContentView* view = (WineContentView*)v;
3621 OnMainThreadAsync(^{
3622 [view setHidden:hidden];
3623 [(WineWindow*)view.window updateForGLSubviews];
3629 /***********************************************************************
3630 * macdrv_add_view_opengl_context
3632 * Add an OpenGL context to the list being tracked for each view.
3634 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3636 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3637 WineContentView* view = (WineContentView*)v;
3638 WineOpenGLContext *context = (WineOpenGLContext*)c;
3641 [view addGLContext:context];
3647 /***********************************************************************
3648 * macdrv_remove_view_opengl_context
3650 * Add an OpenGL context to the list being tracked for each view.
3652 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3654 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3655 WineContentView* view = (WineContentView*)v;
3656 WineOpenGLContext *context = (WineOpenGLContext*)c;
3658 OnMainThreadAsync(^{
3659 [view removeGLContext:context];
3665 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3667 WineContentView* view = (WineContentView*)v;
3669 if (![view isKindOfClass:[WineContentView class]])
3672 [view wine_getBackingSize:backing_size];
3676 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3678 WineContentView* view = (WineContentView*)v;
3680 if ([view isKindOfClass:[WineContentView class]])
3681 [view wine_setBackingSize:backing_size];
3684 /***********************************************************************
3685 * macdrv_window_background_color
3687 * Returns the standard Mac window background color as a 32-bit value of
3688 * the form 0x00rrggbb.
3690 uint32_t macdrv_window_background_color(void)
3692 static uint32_t result;
3693 static dispatch_once_t once;
3695 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3696 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3697 // of it is to draw with it.
3698 dispatch_once(&once, ^{
3700 unsigned char rgbx[4];
3701 unsigned char *planes = rgbx;
3702 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3709 colorSpaceName:NSCalibratedRGBColorSpace
3713 [NSGraphicsContext saveGraphicsState];
3714 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3715 [[NSColor windowBackgroundColor] set];
3716 NSRectFill(NSMakeRect(0, 0, 1, 1));
3717 [NSGraphicsContext restoreGraphicsState];
3719 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3726 /***********************************************************************
3727 * macdrv_send_text_input_event
3729 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3731 OnMainThreadAsync(^{
3733 macdrv_event* event;
3734 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3735 if (![window isKindOfClass:[WineWindow class]])
3737 window = (WineWindow*)[NSApp mainWindow];
3738 if (![window isKindOfClass:[WineWindow class]])
3739 window = [[WineApplicationController sharedController] frontWineWindow];
3744 NSUInteger localFlags = flags;
3748 window.imeData = data;
3749 fix_device_modifiers_by_generic(&localFlags);
3751 // An NSEvent created with +keyEventWithType:... is internally marked
3752 // as synthetic and doesn't get sent through input methods. But one
3753 // created from a CGEvent doesn't have that problem.
3754 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3755 CGEventSetFlags(c, localFlags);
3756 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3757 event = [NSEvent eventWithCGEvent:c];
3760 window.commandDone = FALSE;
3761 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3766 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3767 event->sent_text_input.handled = ret;
3768 event->sent_text_input.done = done;
3769 [[window queue] postEvent:event];
3770 macdrv_release_event(event);