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
23 #import <Carbon/Carbon.h>
24 #import <CoreVideo/CoreVideo.h>
25 #ifdef HAVE_METAL_METAL_H
26 #import <Metal/Metal.h>
27 #import <QuartzCore/QuartzCore.h>
30 #import "cocoa_window.h"
32 #include "macdrv_cocoa.h"
34 #import "cocoa_event.h"
35 #import "cocoa_opengl.h"
38 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
40 NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
41 NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
42 NSWindowFullScreenButton = 7,
43 NSFullScreenWindowMask = 1 << 14,
46 @interface NSWindow (WineFullScreenExtensions)
47 - (void) toggleFullScreen:(id)sender;
52 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
53 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
55 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
60 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
62 NSUInteger style_mask;
66 style_mask = NSTitledWindowMask;
67 if (wf->close_button) style_mask |= NSClosableWindowMask;
68 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
69 if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
70 if (wf->utility) style_mask |= NSUtilityWindowMask;
72 else style_mask = NSBorderlessWindowMask;
78 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
81 for (screen in screens)
83 if (NSIntersectsRect(frame, [screen frame]))
90 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
92 for (NSScreen* screen in screens)
94 if (NSContainsRect(rect, [screen frame]))
101 /* We rely on the supposedly device-dependent modifier flags to distinguish the
102 keys on the left side of the keyboard from those on the right. Some event
103 sources don't set those device-depdendent flags. If we see a device-independent
104 flag for a modifier without either corresponding device-dependent flag, assume
106 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
108 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
109 *modifiers |= NX_DEVICELCMDKEYMASK;
110 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
111 *modifiers |= NX_DEVICELSHIFTKEYMASK;
112 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
113 *modifiers |= NX_DEVICELCTLKEYMASK;
114 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
115 *modifiers |= NX_DEVICELALTKEYMASK;
118 /* As we manipulate individual bits of a modifier mask, we can end up with
119 inconsistent sets of flags. In particular, we might set or clear one of the
120 left/right-specific bits, but not the corresponding non-side-specific bit.
121 Fix that. If either side-specific bit is set, set the non-side-specific bit,
122 otherwise clear it. */
123 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
125 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
126 *modifiers |= NX_COMMANDMASK;
128 *modifiers &= ~NX_COMMANDMASK;
129 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
130 *modifiers |= NX_SHIFTMASK;
132 *modifiers &= ~NX_SHIFTMASK;
133 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
134 *modifiers |= NX_CONTROLMASK;
136 *modifiers &= ~NX_CONTROLMASK;
137 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
138 *modifiers |= NX_ALTERNATEMASK;
140 *modifiers &= ~NX_ALTERNATEMASK;
143 static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
145 fix_device_modifiers_by_generic(&modifiers);
146 NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
147 NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
149 // The MACDRV keyboard driver translates Command keys to Alt. If the
150 // Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
151 // Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
152 if (modifiers & NX_DEVICELALTKEYMASK)
153 new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
154 if (modifiers & NX_DEVICERALTKEYMASK)
155 new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
157 if (modifiers & NX_DEVICELCMDKEYMASK)
158 new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
159 if (modifiers & NX_DEVICERCMDKEYMASK)
160 new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
162 fix_generic_modifiers_by_device(&new_modifiers);
163 return new_modifiers;
167 @interface NSWindow (WineAccessPrivateMethods)
168 - (id) _displayChanged;
172 @interface WineDisplayLink : NSObject
174 CGDirectDisplayID _displayID;
175 CVDisplayLinkRef _link;
176 NSMutableSet* _windows;
178 NSTimeInterval _actualRefreshPeriod;
179 NSTimeInterval _nominalRefreshPeriod;
181 NSTimeInterval _lastDisplayTime;
184 - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
186 - (void) addWindow:(WineWindow*)window;
187 - (void) removeWindow:(WineWindow*)window;
189 - (NSTimeInterval) refreshPeriod;
195 @implementation WineDisplayLink
197 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
199 - (id) initWithDisplayID:(CGDirectDisplayID)displayID
204 CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
205 if (status == kCVReturnSuccess && !_link)
206 status = kCVReturnError;
207 if (status == kCVReturnSuccess)
208 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
209 if (status != kCVReturnSuccess)
215 _displayID = displayID;
216 _windows = [[NSMutableSet alloc] init];
225 CVDisplayLinkStop(_link);
226 CVDisplayLinkRelease(_link);
232 - (void) addWindow:(WineWindow*)window
235 @synchronized(self) {
236 firstWindow = !_windows.count;
237 [_windows addObject:window];
239 if (firstWindow || !CVDisplayLinkIsRunning(_link))
243 - (void) removeWindow:(WineWindow*)window
245 BOOL lastWindow = FALSE;
246 @synchronized(self) {
247 BOOL hadWindows = _windows.count > 0;
248 [_windows removeObject:window];
249 if (hadWindows && !_windows.count)
252 if (lastWindow && CVDisplayLinkIsRunning(_link))
253 CVDisplayLinkStop(_link);
259 @synchronized(self) {
260 windows = [_windows copy];
262 dispatch_async(dispatch_get_main_queue(), ^{
263 BOOL anyDisplayed = FALSE;
264 for (WineWindow* window in windows)
266 if ([window viewsNeedDisplay])
268 [window displayIfNeeded];
273 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
275 _lastDisplayTime = now;
276 else if (_lastDisplayTime + 2.0 < now)
277 CVDisplayLinkStop(_link);
282 - (NSTimeInterval) refreshPeriod
284 if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
285 return _actualRefreshPeriod;
287 if (_nominalRefreshPeriod)
288 return _nominalRefreshPeriod;
290 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
291 if (time.flags & kCVTimeIsIndefinite)
293 _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
294 return _nominalRefreshPeriod;
299 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
300 CVDisplayLinkStart(_link);
303 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
305 WineDisplayLink* link = displayLinkContext;
307 return kCVReturnSuccess;
313 @interface WineBaseView : NSView
317 #ifdef HAVE_METAL_METAL_H
318 @interface WineMetalView : WineBaseView
320 id<MTLDevice> _device;
323 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
329 @interface WineContentView : WineBaseView <NSTextInputClient>
331 NSMutableArray* glContexts;
332 NSMutableArray* pendingGlContexts;
333 BOOL _everHadGLContext;
334 BOOL _cachedHasGLDescendant;
335 BOOL _cachedHasGLDescendantValid;
336 BOOL clearedGlSurface;
338 NSMutableAttributedString* markedText;
339 NSRange markedTextSelection;
343 #ifdef HAVE_METAL_METAL_H
344 WineMetalView *_metalView;
348 @property (readonly, nonatomic) BOOL everHadGLContext;
350 - (void) addGLContext:(WineOpenGLContext*)context;
351 - (void) removeGLContext:(WineOpenGLContext*)context;
352 - (void) updateGLContexts;
354 - (void) wine_getBackingSize:(int*)outBackingSize;
355 - (void) wine_setBackingSize:(const int*)newBackingSize;
357 #ifdef HAVE_METAL_METAL_H
358 - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
364 @interface WineWindow ()
366 @property (readwrite, nonatomic) BOOL disabled;
367 @property (readwrite, nonatomic) BOOL noActivate;
368 @property (readwrite, nonatomic) BOOL floating;
369 @property (readwrite, nonatomic) BOOL drawnSinceShown;
370 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
371 @property (retain, nonatomic) NSWindow* latentParentWindow;
373 @property (nonatomic) void* hwnd;
374 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
376 @property (nonatomic) void* surface;
377 @property (nonatomic) pthread_mutex_t* surface_mutex;
379 @property (copy, nonatomic) NSBezierPath* shape;
380 @property (copy, nonatomic) NSData* shapeData;
381 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
382 @property (readonly, nonatomic) BOOL needsTransparency;
384 @property (nonatomic) BOOL colorKeyed;
385 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
386 @property (nonatomic) BOOL usePerPixelAlpha;
388 @property (assign, nonatomic) void* imeData;
389 @property (nonatomic) BOOL commandDone;
391 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
393 - (void) updateForGLSubviews;
395 - (BOOL) becameEligibleParentOrChild;
396 - (void) becameIneligibleChild;
398 - (void) windowDidDrawContent;
403 @implementation WineBaseView
405 - (void) setRetinaMode:(int)mode
407 for (WineBaseView* subview in [self subviews])
409 if ([subview isKindOfClass:[WineBaseView class]])
410 [subview setRetinaMode:mode];
414 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
419 - (BOOL) preservesContentDuringLiveResize
421 // Returning YES from this tells Cocoa to keep our view's content during
422 // a Cocoa-driven resize. In theory, we're also supposed to override
423 // -setFrameSize: to mark exposed sections as needing redisplay, but
424 // user32 will take care of that in a roundabout way. This way, we don't
425 // redraw until the window surface is flushed.
427 // This doesn't do anything when we resize the window ourselves.
431 - (BOOL)acceptsFirstResponder
433 return [[self window] contentView] == self;
436 - (BOOL) mouseDownCanMoveWindow
441 - (NSFocusRingType) focusRingType
443 return NSFocusRingTypeNone;
449 @implementation WineContentView
451 @synthesize everHadGLContext = _everHadGLContext;
455 [markedText release];
456 [glContexts release];
457 [pendingGlContexts release];
466 - (void) drawRect:(NSRect)rect
468 WineWindow* window = (WineWindow*)[self window];
470 for (WineOpenGLContext* context in pendingGlContexts)
472 if (!clearedGlSurface)
474 context.shouldClearToBlack = TRUE;
475 clearedGlSurface = TRUE;
477 context.needsUpdate = TRUE;
479 [glContexts addObjectsFromArray:pendingGlContexts];
480 [pendingGlContexts removeAllObjects];
482 if ([window contentView] != self)
485 if (window.drawnSinceShown && window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
487 [[NSColor clearColor] setFill];
490 [window.shape addClip];
492 [[NSColor windowBackgroundColor] setFill];
496 if (window.surface && window.surface_mutex &&
497 !pthread_mutex_lock(window.surface_mutex))
502 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
504 CGRect dirtyRect = cgrect_win_from_mac(NSRectToCGRect(rect));
505 CGContextRef context;
508 [window.shape addClip];
510 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
511 CGContextSetBlendMode(context, kCGBlendModeCopy);
512 CGContextSetInterpolationQuality(context, retina_on ? kCGInterpolationHigh : kCGInterpolationNone);
514 for (i = 0; i < count; i++)
519 imageRect = CGRectIntersection(rects[i], dirtyRect);
520 image = create_surface_image(window.surface, &imageRect, FALSE);
524 if (window.colorKeyed)
526 CGImageRef maskedImage;
527 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
528 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
529 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
530 maskedImage = CGImageCreateWithMaskingColors(image, components);
533 CGImageRelease(image);
538 CGContextDrawImage(context, cgrect_mac_from_win(imageRect), image);
540 CGImageRelease(image);
544 [window windowDidDrawContent];
547 pthread_mutex_unlock(window.surface_mutex);
550 // If the window may be transparent, then we have to invalidate the
551 // shadow every time we draw. Also, if this is the first time we've
552 // drawn since changing from transparent to opaque.
553 if (window.drawnSinceShown && (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw))
555 window.shapeChangedSinceLastDraw = FALSE;
556 [window invalidateShadow];
560 - (void) addGLContext:(WineOpenGLContext*)context
562 BOOL hadContext = _everHadGLContext;
564 glContexts = [[NSMutableArray alloc] init];
565 if (!pendingGlContexts)
566 pendingGlContexts = [[NSMutableArray alloc] init];
568 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
570 [glContexts addObject:context];
571 if (!clearedGlSurface)
573 context.shouldClearToBlack = TRUE;
574 clearedGlSurface = TRUE;
576 context.needsUpdate = TRUE;
580 [pendingGlContexts addObject:context];
581 [self setNeedsDisplay:YES];
584 _everHadGLContext = YES;
586 [self invalidateHasGLDescendant];
587 [(WineWindow*)[self window] updateForGLSubviews];
590 - (void) removeGLContext:(WineOpenGLContext*)context
592 [glContexts removeObjectIdenticalTo:context];
593 [pendingGlContexts removeObjectIdenticalTo:context];
594 [(WineWindow*)[self window] updateForGLSubviews];
597 - (void) updateGLContexts:(BOOL)reattach
599 for (WineOpenGLContext* context in glContexts)
601 context.needsUpdate = TRUE;
603 context.needsReattach = TRUE;
607 - (void) updateGLContexts
609 [self updateGLContexts:NO];
612 - (BOOL) _hasGLDescendant
616 if (_everHadGLContext)
618 for (WineContentView* view in [self subviews])
620 if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
626 - (BOOL) hasGLDescendant
628 if (!_cachedHasGLDescendantValid)
630 _cachedHasGLDescendant = [self _hasGLDescendant];
631 _cachedHasGLDescendantValid = YES;
633 return _cachedHasGLDescendant;
636 - (void) invalidateHasGLDescendant
638 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
639 _cachedHasGLDescendantValid = NO;
640 if (invalidateAncestors && self != [[self window] contentView])
642 WineContentView* superview = (WineContentView*)[self superview];
643 if ([superview isKindOfClass:[WineContentView class]])
644 [superview invalidateHasGLDescendant];
648 - (void) wine_getBackingSize:(int*)outBackingSize
650 @synchronized(self) {
651 memcpy(outBackingSize, backingSize, sizeof(backingSize));
654 - (void) wine_setBackingSize:(const int*)newBackingSize
656 @synchronized(self) {
657 memcpy(backingSize, newBackingSize, sizeof(backingSize));
661 #ifdef HAVE_METAL_METAL_H
662 - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
664 if (_metalView) return _metalView;
666 WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
667 [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
668 [self setAutoresizesSubviews:YES];
669 [self addSubview:view positioned:NSWindowBelow relativeTo:nil];
672 [(WineWindow*)self.window windowDidDrawContent];
678 - (void) setRetinaMode:(int)mode
680 double scale = mode ? 0.5 : 2.0;
681 NSRect frame = self.frame;
682 frame.origin.x *= scale;
683 frame.origin.y *= scale;
684 frame.size.width *= scale;
685 frame.size.height *= scale;
686 [self setFrame:frame];
687 [self updateGLContexts];
689 [super setRetinaMode:mode];
695 [self invalidateHasGLDescendant];
698 - (void) viewDidUnhide
700 [super viewDidUnhide];
701 [self updateGLContexts:YES];
702 [self invalidateHasGLDescendant];
705 - (void) clearMarkedText
707 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
708 markedTextSelection = NSMakeRange(0, 0);
709 [[self inputContext] discardMarkedText];
712 - (void) completeText:(NSString*)text
715 WineWindow* window = (WineWindow*)[self window];
717 event = macdrv_create_event(IM_SET_TEXT, window);
718 event->im_set_text.data = [window imeData];
719 event->im_set_text.text = (CFStringRef)[text copy];
720 event->im_set_text.complete = TRUE;
722 [[window queue] postEvent:event];
724 macdrv_release_event(event);
726 [self clearMarkedText];
729 - (void) didAddSubview:(NSView*)subview
731 if ([subview isKindOfClass:[WineContentView class]])
733 WineContentView* view = (WineContentView*)subview;
734 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
735 [self invalidateHasGLDescendant];
737 [super didAddSubview:subview];
740 - (void) willRemoveSubview:(NSView*)subview
742 if ([subview isKindOfClass:[WineContentView class]])
744 WineContentView* view = (WineContentView*)subview;
745 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
746 [self invalidateHasGLDescendant];
748 #ifdef HAVE_METAL_METAL_H
749 if (subview == _metalView)
752 [super willRemoveSubview:subview];
755 - (void) setLayer:(CALayer*)newLayer
757 [super setLayer:newLayer];
758 [self updateGLContexts];
762 * ---------- NSTextInputClient methods ----------
764 - (NSTextInputContext*) inputContext
767 markedText = [[NSMutableAttributedString alloc] init];
768 return [super inputContext];
771 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
773 if ([string isKindOfClass:[NSAttributedString class]])
774 string = [string string];
776 if ([string isKindOfClass:[NSString class]])
777 [self completeText:string];
780 - (void) doCommandBySelector:(SEL)aSelector
782 [(WineWindow*)[self window] setCommandDone:TRUE];
785 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
787 if ([string isKindOfClass:[NSAttributedString class]])
788 string = [string string];
790 if ([string isKindOfClass:[NSString class]])
793 WineWindow* window = (WineWindow*)[self window];
795 if (replacementRange.location == NSNotFound)
796 replacementRange = NSMakeRange(0, [markedText length]);
798 [markedText replaceCharactersInRange:replacementRange withString:string];
799 markedTextSelection = selectedRange;
800 markedTextSelection.location += replacementRange.location;
802 event = macdrv_create_event(IM_SET_TEXT, window);
803 event->im_set_text.data = [window imeData];
804 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
805 event->im_set_text.complete = FALSE;
806 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
808 [[window queue] postEvent:event];
810 macdrv_release_event(event);
812 [[self inputContext] invalidateCharacterCoordinates];
818 [self completeText:nil];
821 - (NSRange) selectedRange
823 return markedTextSelection;
826 - (NSRange) markedRange
828 NSRange range = NSMakeRange(0, [markedText length]);
830 range.location = NSNotFound;
834 - (BOOL) hasMarkedText
836 return [markedText length] > 0;
839 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
841 if (aRange.location >= [markedText length])
844 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
846 *actualRange = aRange;
847 return [markedText attributedSubstringFromRange:aRange];
850 - (NSArray*) validAttributesForMarkedText
852 return [NSArray array];
855 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
858 WineWindow* window = (WineWindow*)[self window];
861 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
863 query = macdrv_create_query();
864 query->type = QUERY_IME_CHAR_RECT;
865 query->window = (macdrv_window)[window retain];
866 query->ime_char_rect.data = [window imeData];
867 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
869 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
871 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
872 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
873 [[WineApplicationController sharedController] flipRect:&ret];
876 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
878 macdrv_release_query(query);
881 *actualRange = aRange;
885 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
890 - (NSInteger) windowLevel
892 return [[self window] level];
898 #ifdef HAVE_METAL_METAL_H
899 @implementation WineMetalView
901 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
903 self = [super initWithFrame:frame];
906 _device = [device retain];
907 self.wantsLayer = YES;
908 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
919 - (void) setRetinaMode:(int)mode
921 self.layer.contentsScale = mode ? 2.0 : 1.0;
922 [super setRetinaMode:mode];
925 - (CALayer*) makeBackingLayer
927 CAMetalLayer *layer = [CAMetalLayer layer];
928 layer.device = _device;
929 layer.framebufferOnly = YES;
930 layer.magnificationFilter = kCAFilterNearest;
931 layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
932 layer.contentsScale = retina_on ? 2.0 : 1.0;
945 @implementation WineWindow
947 static WineWindow* causing_becomeKeyWindow;
949 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
950 @synthesize drawnSinceShown;
951 @synthesize surface, surface_mutex;
952 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
953 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
954 @synthesize usePerPixelAlpha;
955 @synthesize imeData, commandDone;
957 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
958 windowFrame:(NSRect)window_frame
960 queue:(WineEventQueue*)queue
963 WineContentView* contentView;
964 NSTrackingArea* trackingArea;
965 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
967 [[WineApplicationController sharedController] flipRect:&window_frame];
969 window = [[[self alloc] initWithContentRect:window_frame
970 styleMask:style_mask_for_features(wf)
971 backing:NSBackingStoreBuffered
972 defer:YES] autorelease];
974 if (!window) return nil;
976 /* Standardize windows to eliminate differences between titled and
977 borderless windows and between NSWindow and NSPanel. */
978 [window setHidesOnDeactivate:NO];
979 [window setReleasedWhenClosed:NO];
981 [window setOneShot:YES];
982 [window disableCursorRects];
983 [window setShowsResizeIndicator:NO];
984 [window setHasShadow:wf->shadow];
985 [window setAcceptsMouseMovedEvents:YES];
986 [window setDelegate:window];
987 [window setBackgroundColor:[NSColor clearColor]];
988 [window setOpaque:NO];
990 window.queue = queue;
991 window->savedContentMinSize = NSZeroSize;
992 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
993 window->resizable = wf->resizable;
994 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
996 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
997 (NSString*)kUTTypeContent,
1000 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
1003 [contentView setAutoresizesSubviews:NO];
1005 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
1006 because they give us mouse moves in the background. */
1007 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
1008 options:(NSTrackingMouseMoved |
1009 NSTrackingActiveAlways |
1010 NSTrackingInVisibleRect)
1012 userInfo:nil] autorelease];
1015 [contentView addTrackingArea:trackingArea];
1017 [window setContentView:contentView];
1018 [window setInitialFirstResponder:contentView];
1020 [nc addObserver:window
1021 selector:@selector(updateFullscreen)
1022 name:NSApplicationDidChangeScreenParametersNotification
1024 [window updateFullscreen];
1026 [nc addObserver:window
1027 selector:@selector(applicationWillHide)
1028 name:NSApplicationWillHideNotification
1030 [nc addObserver:window
1031 selector:@selector(applicationDidUnhide)
1032 name:NSApplicationDidUnhideNotification
1035 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1036 selector:@selector(checkWineDisplayLink)
1037 name:NSWorkspaceActiveSpaceDidChangeNotification
1038 object:[NSWorkspace sharedWorkspace]];
1040 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1047 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1048 [[NSNotificationCenter defaultCenter] removeObserver:self];
1050 [latentChildWindows release];
1051 [latentParentWindow release];
1053 [shapeData release];
1057 - (BOOL) preventResizing
1059 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1060 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
1063 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1065 if (allow_immovable_windows && (disabled || inMaximized))
1067 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1073 - (void) adjustFeaturesForState
1075 NSUInteger style = [self styleMask];
1077 if (style & NSClosableWindowMask)
1078 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1079 if (style & NSMiniaturizableWindowMask)
1080 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1081 if (style & NSResizableWindowMask)
1082 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1083 if ([self respondsToSelector:@selector(toggleFullScreen:)])
1085 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1086 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1089 if ([self preventResizing])
1091 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1092 [self setContentMinSize:size];
1093 [self setContentMaxSize:size];
1097 [self setContentMaxSize:savedContentMaxSize];
1098 [self setContentMinSize:savedContentMinSize];
1101 if (allow_immovable_windows || cursor_clipping_locks_windows)
1102 [self setMovable:[self allowsMovingWithMaximized:maximized]];
1105 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1107 if ([self respondsToSelector:@selector(toggleFullScreen:)])
1109 NSUInteger style = [self styleMask];
1111 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1112 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
1113 !(self.parentWindow || self.latentParentWindow))
1115 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1116 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1120 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1121 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1122 if (style & NSFullScreenWindowMask)
1123 [super toggleFullScreen:nil];
1127 if (behavior != [self collectionBehavior])
1129 [self setCollectionBehavior:behavior];
1130 [self adjustFeaturesForState];
1134 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1136 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1137 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1138 NSUInteger currentStyle = [self styleMask];
1139 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1141 if (newStyle != currentStyle)
1143 NSString* title = [[[self title] copy] autorelease];
1144 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1145 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1146 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1148 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1149 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1150 // just NSTitledWindowMask, the window buttons should disappear rather
1151 // than just being disabled. But they don't. Similarly in reverse.
1152 // The workaround is to also toggle NSClosableWindowMask at the same time.
1153 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1155 [self setStyleMask:newStyle];
1157 // -setStyleMask: resets the firstResponder to the window. Set it
1158 // back to the content view.
1159 if ([[self contentView] acceptsFirstResponder])
1160 [self makeFirstResponder:[self contentView]];
1162 [self adjustFullScreenBehavior:[self collectionBehavior]];
1164 if ([[self title] length] == 0 && [title length] > 0)
1165 [self setTitle:title];
1168 resizable = wf->resizable;
1169 [self adjustFeaturesForState];
1170 [self setHasShadow:wf->shadow];
1173 // Indicates if the window would be visible if the app were not hidden.
1174 - (BOOL) wouldBeVisible
1176 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1179 - (BOOL) isOrderedIn
1181 return [self wouldBeVisible] || [self isMiniaturized];
1184 - (NSInteger) minimumLevelForActive:(BOOL)active
1188 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1189 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1190 level = NSFloatingWindowLevel;
1192 level = NSNormalWindowLevel;
1198 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1200 if (captured || fullscreen)
1203 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1205 level = NSStatusWindowLevel + 1;
1215 - (void) postDidUnminimizeEvent
1217 macdrv_event* event;
1219 /* Coalesce events by discarding any previous ones still in the queue. */
1220 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1223 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1224 [queue postEvent:event];
1225 macdrv_release_event(event);
1228 - (void) sendResizeStartQuery
1230 macdrv_query* query = macdrv_create_query();
1231 query->type = QUERY_RESIZE_START;
1232 query->window = (macdrv_window)[self retain];
1234 [self.queue query:query timeout:0.3];
1235 macdrv_release_query(query);
1238 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1240 NSWindowCollectionBehavior behavior;
1242 self.disabled = state->disabled;
1243 self.noActivate = state->no_activate;
1245 if (self.floating != state->floating)
1247 self.floating = state->floating;
1248 if (state->floating)
1250 // Became floating. If child of non-floating window, make that
1251 // relationship latent.
1252 WineWindow* parent = (WineWindow*)[self parentWindow];
1253 if (parent && !parent.floating)
1254 [self becameIneligibleChild];
1258 // Became non-floating. If parent of floating children, make that
1259 // relationship latent.
1261 for (child in [self childWineWindows])
1264 [child becameIneligibleChild];
1268 // Check our latent relationships. If floating status was the only
1269 // reason they were latent, then make them active.
1270 if ([self isVisible])
1271 [self becameEligibleParentOrChild];
1273 [[WineApplicationController sharedController] adjustWindowLevels];
1276 if (state->minimized_valid)
1278 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1280 pendingMinimize = FALSE;
1281 if (state->minimized && ![self isMiniaturized])
1283 if ([self wouldBeVisible])
1285 if ([self styleMask] & NSFullScreenWindowMask)
1287 [self postDidUnminimizeEvent];
1288 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1292 [self setStyleMask:([self styleMask] | NSMiniaturizableWindowMask)];
1293 [super miniaturize:nil];
1294 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1295 event_mask_for_type(WINDOW_GOT_FOCUS) |
1296 event_mask_for_type(WINDOW_LOST_FOCUS);
1300 pendingMinimize = TRUE;
1302 else if (!state->minimized && [self isMiniaturized])
1304 ignore_windowDeminiaturize = TRUE;
1305 [self deminiaturize:nil];
1306 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1310 [queue discardEventsMatchingMask:discard forWindow:self];
1313 if (state->maximized != maximized)
1315 maximized = state->maximized;
1316 [self adjustFeaturesForState];
1318 if (!maximized && [self inLiveResize])
1319 [self sendResizeStartQuery];
1322 behavior = NSWindowCollectionBehaviorDefault;
1323 if (state->excluded_by_expose)
1324 behavior |= NSWindowCollectionBehaviorTransient;
1326 behavior |= NSWindowCollectionBehaviorManaged;
1327 if (state->excluded_by_cycle)
1329 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1330 if ([self isOrderedIn])
1331 [NSApp removeWindowsItem:self];
1335 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1336 if ([self isOrderedIn])
1337 [NSApp addWindowsItem:self title:[self title] filename:NO];
1339 [self adjustFullScreenBehavior:behavior];
1342 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1344 BOOL reordered = FALSE;
1346 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1348 if ([self level] > [child level])
1349 [child setLevel:[self level]];
1350 if (![child isVisible])
1351 [child setAutodisplay:YES];
1352 [self addChildWindow:child ordered:NSWindowAbove];
1353 [child checkWineDisplayLink];
1354 [latentChildWindows removeObjectIdenticalTo:child];
1355 child.latentParentWindow = nil;
1360 if (!latentChildWindows)
1361 latentChildWindows = [[NSMutableArray alloc] init];
1362 if (![latentChildWindows containsObject:child])
1363 [latentChildWindows addObject:child];
1364 child.latentParentWindow = self;
1370 - (BOOL) addChildWineWindow:(WineWindow*)child
1372 return [self addChildWineWindow:child assumeVisible:FALSE];
1375 - (void) removeChildWineWindow:(WineWindow*)child
1377 [self removeChildWindow:child];
1378 if (child.latentParentWindow == self)
1379 child.latentParentWindow = nil;
1380 [latentChildWindows removeObjectIdenticalTo:child];
1383 - (void) setChildWineWindows:(NSArray*)childWindows
1385 NSArray* origChildren;
1386 NSUInteger count, start, limit, i;
1388 origChildren = self.childWineWindows;
1390 // If the current and desired children arrays match up to a point, leave
1391 // those matching children alone.
1392 count = childWindows.count;
1393 limit = MIN(origChildren.count, count);
1394 for (start = 0; start < limit; start++)
1396 if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1400 // Remove all of the child windows and re-add them back-to-front so they
1401 // are in the desired order.
1402 for (i = start; i < count; i++)
1404 WineWindow* child = [childWindows objectAtIndex:i];
1405 [self removeChildWindow:child];
1407 for (i = start; i < count; i++)
1409 WineWindow* child = [childWindows objectAtIndex:i];
1410 [self addChildWindow:child ordered:NSWindowAbove];
1414 static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1416 NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1417 NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1418 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1419 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1420 if (index1 == NSNotFound)
1422 if (index2 == NSNotFound)
1423 return NSOrderedSame;
1425 return NSOrderedAscending;
1427 else if (index2 == NSNotFound)
1428 return NSOrderedDescending;
1429 else if (index1 < index2)
1430 return NSOrderedDescending;
1431 else if (index2 < index1)
1432 return NSOrderedAscending;
1434 return NSOrderedSame;
1437 - (BOOL) becameEligibleParentOrChild
1439 BOOL reordered = FALSE;
1442 if (latentParentWindow.floating || !self.floating)
1444 // If we aren't visible currently, we assume that we should be and soon
1445 // will be. So, if the latent parent is visible that's enough to assume
1446 // we can establish the parent-child relationship in Cocoa. That will
1447 // actually make us visible, which is fine.
1448 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1452 // Here, though, we may not actually be visible yet and adding a child
1453 // won't make us visible. The caller will have to call this method
1454 // again after actually making us visible.
1455 if ([self isVisible] && (count = [latentChildWindows count]))
1457 NSMutableArray* windowNumbers;
1458 NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1459 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1462 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1464 for (i = 0; i < count; i++)
1466 WineWindow* child = [latentChildWindows objectAtIndex:i];
1467 if ([child isVisible] && (self.floating || !child.floating))
1469 if (child.latentParentWindow == self)
1471 if ([self level] > [child level])
1472 [child setLevel:[self level]];
1473 [childWindows addObject:child];
1474 child.latentParentWindow = nil;
1478 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1479 [indexesToRemove addIndex:i];
1483 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1485 [childWindows sortWithOptions:NSSortStable
1486 usingComparator:^NSComparisonResult(id obj1, id obj2){
1487 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1489 [self setChildWineWindows:childWindows];
1495 - (void) becameIneligibleChild
1497 WineWindow* parent = (WineWindow*)[self parentWindow];
1500 if (!parent->latentChildWindows)
1501 parent->latentChildWindows = [[NSMutableArray alloc] init];
1502 [parent->latentChildWindows insertObject:self atIndex:0];
1503 self.latentParentWindow = parent;
1504 [parent removeChildWindow:self];
1508 - (void) becameIneligibleParentOrChild
1510 NSArray* childWindows = [self childWineWindows];
1512 [self becameIneligibleChild];
1514 if ([childWindows count])
1518 for (child in childWindows)
1520 child.latentParentWindow = self;
1521 [self removeChildWindow:child];
1524 if (latentChildWindows)
1525 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1527 latentChildWindows = [childWindows mutableCopy];
1531 // Determine if, among Wine windows, this window is directly above or below
1532 // a given other Wine window with no other Wine window intervening.
1533 // Intervening non-Wine windows are ignored.
1534 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1536 NSNumber* windowNumber;
1537 NSNumber* otherWindowNumber;
1538 NSArray* windowNumbers;
1539 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1541 if (![self isVisible] || ![otherWindow isVisible])
1544 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1545 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1546 windowNumbers = [[self class] windowNumbersWithOptions:0];
1547 windowIndex = [windowNumbers indexOfObject:windowNumber];
1548 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1550 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1553 if (orderingMode == NSWindowAbove)
1555 lowIndex = windowIndex;
1556 highIndex = otherWindowIndex;
1558 else if (orderingMode == NSWindowBelow)
1560 lowIndex = otherWindowIndex;
1561 highIndex = windowIndex;
1566 if (highIndex <= lowIndex)
1569 for (i = lowIndex + 1; i < highIndex; i++)
1571 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1572 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1573 if ([interveningWindow isKindOfClass:[WineWindow class]])
1580 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1582 NSMutableArray* windowNumbers;
1583 NSNumber* childWindowNumber;
1584 NSUInteger otherIndex;
1585 NSArray* origChildren;
1586 NSMutableArray* children;
1588 // Get the z-order from the window server and modify it to reflect the
1589 // requested window ordering.
1590 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1591 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1592 [windowNumbers removeObject:childWindowNumber];
1595 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1596 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1598 else if (mode == NSWindowAbove)
1599 [windowNumbers insertObject:childWindowNumber atIndex:0];
1601 [windowNumbers addObject:childWindowNumber];
1603 // Get our child windows and sort them in the reverse of the desired
1604 // z-order (back-to-front).
1605 origChildren = [self childWineWindows];
1606 children = [[origChildren mutableCopy] autorelease];
1607 [children sortWithOptions:NSSortStable
1608 usingComparator:^NSComparisonResult(id obj1, id obj2){
1609 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1612 [self setChildWineWindows:children];
1615 // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1616 // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1617 // (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
1618 // if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1619 // otherwise, the two ancestors are each roots of disjoint window trees
1620 // (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
1621 // (non-nil, nil) self is a descendent of other and *ancestor is the direct child
1622 - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1624 NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1628 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1634 *ancestorOfOther = child;
1638 [otherAncestors addObject:parent];
1642 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1645 NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1646 if (index != NSNotFound)
1650 *ancestorOfOther = nil;
1652 *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1658 *ancestorOfOther = otherAncestors.lastObject;;
1661 /* Returns whether or not the window was ordered in, which depends on if
1662 its frame intersects any screen. */
1663 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1665 WineApplicationController* controller = [WineApplicationController sharedController];
1666 if (![self isMiniaturized])
1668 BOOL needAdjustWindowLevels = FALSE;
1673 [controller transformProcessToForeground];
1674 if ([NSApp isHidden])
1676 wasVisible = [self isVisible];
1679 [NSApp activateIgnoringOtherApps:YES];
1681 NSDisableScreenUpdates();
1683 if ([self becameEligibleParentOrChild])
1684 needAdjustWindowLevels = TRUE;
1688 WineWindow* other = [prev isVisible] ? prev : next;
1689 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1691 if (![self isOrdered:orderingMode relativeTo:other])
1693 WineWindow* ancestor;
1694 WineWindow* ancestorOfOther;
1696 [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1699 [self setAutodisplay:YES];
1700 if (ancestorOfOther)
1702 // This window level may not be right for this window based
1703 // on floating-ness, fullscreen-ness, etc. But we set it
1704 // temporarily to allow us to order the windows properly.
1705 // Then the levels get fixed by -adjustWindowLevels.
1706 if ([ancestor level] != [ancestorOfOther level])
1707 [ancestor setLevel:[ancestorOfOther level]];
1709 parent = (WineWindow*)ancestor.parentWindow;
1710 if ([parent isKindOfClass:[WineWindow class]])
1711 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1713 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1716 if (!ancestorOfOther || ancestor != self)
1719 (parent = (WineWindow*)child.parentWindow);
1722 if ([parent isKindOfClass:[WineWindow class]])
1723 [parent order:-orderingMode childWindow:child relativeTo:nil];
1724 if (parent == ancestor)
1729 [self checkWineDisplayLink];
1730 needAdjustWindowLevels = TRUE;
1737 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1740 [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1743 // Again, temporarily set level to make sure we can order to
1745 next = [controller frontWineWindow];
1746 if (next && [self level] < [next level])
1747 [self setLevel:[next level]];
1748 [self setAutodisplay:YES];
1749 [self orderFront:nil];
1750 [self checkWineDisplayLink];
1751 needAdjustWindowLevels = TRUE;
1753 pendingOrderOut = FALSE;
1755 if ([self becameEligibleParentOrChild])
1756 needAdjustWindowLevels = TRUE;
1758 if (needAdjustWindowLevels)
1760 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1761 [controller updateFullscreenWindows];
1762 [controller adjustWindowLevels];
1765 if (pendingMinimize)
1767 [self setStyleMask:([self styleMask] | NSMiniaturizableWindowMask)];
1768 [super miniaturize:nil];
1769 pendingMinimize = FALSE;
1772 NSEnableScreenUpdates();
1774 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1775 Generate a frame-changed event just in case. The back end will ignore
1776 it if nothing actually changed. */
1777 [self windowDidResize:nil];
1779 if (![self isExcludedFromWindowsMenu])
1780 [NSApp addWindowsItem:self title:[self title] filename:NO];
1786 WineApplicationController* controller = [WineApplicationController sharedController];
1787 BOOL wasVisible = [self isVisible];
1788 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1790 [self endWindowDragging];
1791 [controller windowWillOrderOut:self];
1793 if (enteringFullScreen || exitingFullScreen)
1795 pendingOrderOut = TRUE;
1796 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1797 event_mask_for_type(WINDOW_GOT_FOCUS) |
1798 event_mask_for_type(WINDOW_LOST_FOCUS) |
1799 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1800 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1801 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1806 pendingOrderOut = FALSE;
1808 if ([self isMiniaturized])
1809 pendingMinimize = TRUE;
1811 WineWindow* parent = (WineWindow*)self.parentWindow;
1812 if ([parent isKindOfClass:[WineWindow class]])
1813 [parent grabDockIconSnapshotFromWindow:self force:NO];
1815 [self becameIneligibleParentOrChild];
1816 if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1820 fakingClose = FALSE;
1823 [self orderOut:nil];
1824 [self checkWineDisplayLink];
1825 [self setBackgroundColor:[NSColor clearColor]];
1826 [self setOpaque:NO];
1827 drawnSinceShown = NO;
1828 savedVisibleState = FALSE;
1829 if (wasVisible && wasOnActiveSpace && fullscreen)
1830 [controller updateFullscreenWindows];
1831 [controller adjustWindowLevels];
1832 [NSApp removeWindowsItem:self];
1834 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1835 event_mask_for_type(WINDOW_GOT_FOCUS) |
1836 event_mask_for_type(WINDOW_LOST_FOCUS) |
1837 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1838 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1839 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1843 - (void) updateFullscreen
1845 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1846 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1848 if (nowFullscreen != fullscreen)
1850 WineApplicationController* controller = [WineApplicationController sharedController];
1852 fullscreen = nowFullscreen;
1853 if ([self isVisible] && [self isOnActiveSpace])
1854 [controller updateFullscreenWindows];
1856 [controller adjustWindowLevels];
1860 - (void) setFrameAndWineFrame:(NSRect)frame
1862 [self setFrame:frame display:YES];
1865 roundedWineFrame = self.frame;
1867 #if CGFLOAT_IS_DOUBLE
1868 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1869 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1870 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1871 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1872 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1873 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1874 roundedWineFrame = wineFrame;
1876 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1877 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1878 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1879 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1880 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1881 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1882 roundedWineFrame = wineFrame;
1886 - (void) setFrameFromWine:(NSRect)contentRect
1888 /* Origin is (left, top) in a top-down space. Need to convert it to
1889 (left, bottom) in a bottom-up space. */
1890 [[WineApplicationController sharedController] flipRect:&contentRect];
1892 /* The back end is establishing a new window size and position. It's
1893 not interested in any stale events regarding those that may be sitting
1895 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1898 if (!NSIsEmptyRect(contentRect))
1900 NSRect frame, oldFrame;
1902 oldFrame = self.wine_fractionalFrame;
1903 frame = [self frameRectForContentRect:contentRect];
1904 if (!NSEqualRects(frame, oldFrame))
1906 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1907 BOOL needEnableScreenUpdates = FALSE;
1909 if ([self preventResizing])
1911 // Allow the following calls to -setFrame:display: to work even
1912 // if they would violate the content size constraints. This
1913 // shouldn't be necessary since the content size constraints are
1914 // documented to not constrain that method, but it seems to be.
1915 [self setContentMinSize:NSZeroSize];
1916 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1919 if (equalSizes && [[self childWineWindows] count])
1921 // If we change the window frame such that the origin moves
1922 // but the size doesn't change, then Cocoa moves child
1923 // windows with the parent. We don't want that so we fake
1924 // a change of the size and then change it back.
1925 NSRect bogusFrame = frame;
1926 bogusFrame.size.width++;
1928 NSDisableScreenUpdates();
1929 needEnableScreenUpdates = TRUE;
1931 ignore_windowResize = TRUE;
1932 [self setFrame:bogusFrame display:NO];
1933 ignore_windowResize = FALSE;
1936 [self setFrameAndWineFrame:frame];
1937 if ([self preventResizing])
1939 [self setContentMinSize:contentRect.size];
1940 [self setContentMaxSize:contentRect.size];
1943 if (needEnableScreenUpdates)
1944 NSEnableScreenUpdates();
1946 if (!enteringFullScreen &&
1947 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1948 nonFullscreenFrame = frame;
1950 [self updateFullscreen];
1952 if ([self isOrderedIn])
1954 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1955 event. The back end will ignore it if nothing actually changed. */
1956 [self windowDidResize:nil];
1962 - (NSRect) wine_fractionalFrame
1964 NSRect frame = self.frame;
1965 if (NSEqualRects(frame, roundedWineFrame))
1970 - (void) setMacDrvParentWindow:(WineWindow*)parent
1972 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1973 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1975 [oldParent removeChildWineWindow:self];
1976 [latentParentWindow removeChildWineWindow:self];
1977 if ([parent addChildWineWindow:self])
1978 [[WineApplicationController sharedController] adjustWindowLevels];
1979 [self adjustFullScreenBehavior:[self collectionBehavior]];
1983 - (void) setDisabled:(BOOL)newValue
1985 if (disabled != newValue)
1987 disabled = newValue;
1988 [self adjustFeaturesForState];
1992 - (BOOL) needsTransparency
1994 return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1995 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1998 - (void) checkTransparency
2000 if (![self isOpaque] && !self.needsTransparency)
2002 self.shapeChangedSinceLastDraw = TRUE;
2003 [[self contentView] setNeedsDisplay:YES];
2004 [self setBackgroundColor:[NSColor windowBackgroundColor]];
2005 [self setOpaque:YES];
2007 else if ([self isOpaque] && self.needsTransparency)
2009 self.shapeChangedSinceLastDraw = TRUE;
2010 [[self contentView] setNeedsDisplay:YES];
2011 [self setBackgroundColor:[NSColor clearColor]];
2012 [self setOpaque:NO];
2016 - (void) setShape:(NSBezierPath*)newShape
2018 if (shape == newShape) return;
2022 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
2026 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
2028 shape = [newShape copy];
2029 self.shapeChangedSinceLastDraw = TRUE;
2031 [self checkTransparency];
2034 - (void) makeFocused:(BOOL)activate
2038 [[WineApplicationController sharedController] transformProcessToForeground];
2039 [NSApp activateIgnoringOtherApps:YES];
2042 causing_becomeKeyWindow = self;
2043 [self makeKeyWindow];
2044 causing_becomeKeyWindow = nil;
2046 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2047 event_mask_for_type(WINDOW_LOST_FOCUS)
2051 - (void) postKey:(uint16_t)keyCode
2052 pressed:(BOOL)pressed
2053 modifiers:(NSUInteger)modifiers
2054 event:(NSEvent*)theEvent
2056 macdrv_event* event;
2058 WineApplicationController* controller = [WineApplicationController sharedController];
2060 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2061 event->key.keycode = keyCode;
2062 event->key.modifiers = modifiers;
2063 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
2065 if ((cgevent = [theEvent CGEvent]))
2066 controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2068 [queue postEvent:event];
2070 macdrv_release_event(event);
2072 [controller noteKey:keyCode pressed:pressed];
2075 - (void) postKeyEvent:(NSEvent *)theEvent
2077 [self flagsChanged:theEvent];
2078 [self postKey:[theEvent keyCode]
2079 pressed:[theEvent type] == NSKeyDown
2080 modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2084 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2086 savedContentMinSize = minSize;
2087 savedContentMaxSize = maxSize;
2088 if (![self preventResizing])
2090 [self setContentMinSize:minSize];
2091 [self setContentMaxSize:maxSize];
2095 - (WineWindow*) ancestorWineWindow
2097 WineWindow* ancestor = self;
2100 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2101 if ([parent isKindOfClass:[WineWindow class]])
2109 - (void) postBroughtForwardEvent
2111 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2112 [queue postEvent:event];
2113 macdrv_release_event(event);
2116 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
2118 macdrv_event* event;
2119 NSUInteger style = self.styleMask;
2122 style |= NSFullScreenWindowMask;
2124 style &= ~NSFullScreenWindowMask;
2125 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2126 [[WineApplicationController sharedController] flipRect:&frame];
2128 /* Coalesce events by discarding any previous ones still in the queue. */
2129 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2132 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2133 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2134 event->window_frame_changed.fullscreen = isFullscreen;
2135 event->window_frame_changed.in_resize = resizing;
2136 [queue postEvent:event];
2137 macdrv_release_event(event);
2140 - (void) updateForCursorClipping
2142 [self adjustFeaturesForState];
2145 - (void) endWindowDragging
2149 if (draggingPhase == 3)
2151 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2152 [queue postEvent:event];
2153 macdrv_release_event(event);
2157 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2161 - (NSMutableDictionary*) displayIDToDisplayLinkMap
2163 static NSMutableDictionary* displayIDToDisplayLinkMap;
2164 if (!displayIDToDisplayLinkMap)
2166 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2168 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2171 usingBlock:^(NSNotification *note){
2172 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2173 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2174 [badDisplayIDs minusSet:validDisplayIDs];
2175 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2178 return displayIDToDisplayLinkMap;
2181 - (WineDisplayLink*) wineDisplayLink
2183 if (!_lastDisplayID)
2186 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2187 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2190 - (void) checkWineDisplayLink
2192 NSScreen* screen = self.screen;
2193 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2195 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2196 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2200 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2201 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2202 if (displayID == _lastDisplayID)
2205 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2209 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2210 [link removeWindow:self];
2214 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2217 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2218 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2220 [link addWindow:self];
2221 [self displayIfNeeded];
2223 _lastDisplayID = displayID;
2226 - (BOOL) isEmptyShaped
2228 return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2231 - (BOOL) canProvideSnapshot
2233 return (self.windowNumber > 0 && ![self isEmptyShaped]);
2236 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2238 if (![self isEmptyShaped])
2241 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2242 if (!force && now < lastDockIconSnapshot + 1)
2247 if (![window canProvideSnapshot])
2253 for (WineWindow* childWindow in self.childWindows)
2255 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2258 NSSize size = childWindow.frame.size;
2259 CGFloat area = size.width * size.height;
2260 if (!window || area > bestArea)
2262 window = childWindow;
2271 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2272 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2273 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2274 CFRelease(windowIDs);
2278 NSImage* appImage = [NSApp applicationIconImage];
2280 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2282 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2283 [dockIcon lockFocus];
2285 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2287 CGRect rect = CGRectMake(8, 8, 240, 240);
2288 size_t width = CGImageGetWidth(windowImage);
2289 size_t height = CGImageGetHeight(windowImage);
2292 rect.size.height *= height / (double)width;
2293 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2295 else if (width != height)
2297 rect.size.width *= width / (double)height;
2298 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2301 CGContextDrawImage(cgcontext, rect, windowImage);
2302 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2304 operation:NSCompositeSourceOver
2309 [dockIcon unlockFocus];
2311 CGImageRelease(windowImage);
2313 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2314 if (![imageView isKindOfClass:[NSImageView class]])
2316 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2317 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2318 self.dockTile.contentView = imageView;
2320 imageView.image = dockIcon;
2321 [self.dockTile display];
2322 lastDockIconSnapshot = now;
2325 - (void) checkEmptyShaped
2327 if (self.dockTile.contentView && ![self isEmptyShaped])
2329 self.dockTile.contentView = nil;
2330 lastDockIconSnapshot = 0;
2332 [self checkWineDisplayLink];
2337 * ---------- NSWindow method overrides ----------
2339 - (BOOL) canBecomeKeyWindow
2341 if (causing_becomeKeyWindow == self) return YES;
2342 if (self.disabled || self.noActivate) return NO;
2343 return [self isKeyWindow];
2346 - (BOOL) canBecomeMainWindow
2348 return [self canBecomeKeyWindow];
2351 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2353 // If a window is sized to completely cover a screen, then it's in
2354 // full-screen mode. In that case, we don't allow NSWindow to constrain
2356 NSArray* screens = [NSScreen screens];
2357 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2358 if (!screen_covered_by_rect(contentRect, screens) &&
2359 frame_intersects_screens(frameRect, screens))
2360 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2364 // This private method of NSWindow is called as Cocoa reacts to the display
2365 // configuration changing. Among other things, it adjusts the window's
2366 // frame based on how the screen(s) changed size. That tells Wine that the
2367 // window has been moved. We don't want that. Rather, we want to make
2368 // sure that the WinAPI notion of the window position is maintained/
2369 // restored, possibly undoing or overriding Cocoa's adjustment.
2371 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2372 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2373 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2374 // reassert its notion of the position. That call won't get processed
2375 // until after this method returns, so it will override whatever this
2376 // method does to the window position. It will also discard any pending
2377 // WINDOW_FRAME_CHANGED events.
2379 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2380 // adjust the window's position due to a display change is to hook into
2381 // this private method. This private method has remained stable from 10.6
2382 // through 10.11. If it does change, the most likely thing is that it
2383 // will be removed and no longer called and this fix will simply stop
2384 // working. The only real danger would be if Apple changed the return type
2385 // to a struct or floating-point type, which would change the calling
2387 - (id) _displayChanged
2389 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2390 [queue postEvent:event];
2391 macdrv_release_event(event);
2393 return [super _displayChanged];
2396 - (BOOL) isExcludedFromWindowsMenu
2398 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2401 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2403 BOOL ret = [super validateMenuItem:menuItem];
2405 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2406 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2407 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2413 /* We don't call this. It's the action method of the items in the Window menu. */
2414 - (void) makeKeyAndOrderFront:(id)sender
2416 if ([self isMiniaturized])
2417 [self deminiaturize:nil];
2418 [self orderBelow:nil orAbove:nil activate:NO];
2419 [[self ancestorWineWindow] postBroughtForwardEvent];
2421 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2422 [[WineApplicationController sharedController] windowGotFocus:self];
2425 - (void) sendEvent:(NSEvent*)event
2427 NSEventType type = event.type;
2429 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2430 interface control. For example, Control-Tab switches focus among
2431 views. We want to bypass that feature, so directly route key-down
2432 events to -keyDown:. */
2433 if (type == NSKeyDown)
2434 [[self firstResponder] keyDown:event];
2437 if (!draggingPhase && maximized && ![self isMovable] &&
2438 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2439 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2441 NSRect titleBar = self.frame;
2442 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2443 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2444 titleBar.origin.y = NSMaxY(contentRect);
2446 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2448 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2450 static const NSWindowButton buttons[] = {
2451 NSWindowCloseButton,
2452 NSWindowMiniaturizeButton,
2454 NSWindowFullScreenButton,
2456 BOOL hitButton = NO;
2459 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2463 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2466 button = [self standardWindowButton:buttons[i]];
2467 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2477 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2478 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2482 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2484 if ([self isMovable])
2486 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2487 NSPoint newTopLeft = dragWindowStartPosition;
2489 newTopLeft.x += point.x - dragStartPosition.x;
2490 newTopLeft.y += point.y - dragStartPosition.y;
2492 if (draggingPhase == 2)
2494 macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2495 mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2496 [queue postEvent:mevent];
2497 macdrv_release_event(mevent);
2502 [self setFrameTopLeftPoint:newTopLeft];
2504 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2506 macdrv_event* event;
2507 NSRect frame = [self contentRectForFrameRect:self.frame];
2509 [[WineApplicationController sharedController] flipRect:&frame];
2511 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2512 event->window_restore_requested.keep_frame = TRUE;
2513 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2514 [queue postEvent:event];
2515 macdrv_release_event(event);
2520 if (type == NSLeftMouseUp)
2521 [self endWindowDragging];
2524 [super sendEvent:event];
2528 - (void) miniaturize:(id)sender
2530 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2531 [queue postEvent:event];
2532 macdrv_release_event(event);
2534 WineWindow* parent = (WineWindow*)self.parentWindow;
2535 if ([parent isKindOfClass:[WineWindow class]])
2536 [parent grabDockIconSnapshotFromWindow:self force:YES];
2539 - (void) toggleFullScreen:(id)sender
2541 if (!self.disabled && !maximized)
2542 [super toggleFullScreen:sender];
2545 - (void) setViewsNeedDisplay:(BOOL)value
2547 if (value && ![self viewsNeedDisplay])
2549 WineDisplayLink* link = [self wineDisplayLink];
2552 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2553 if (_lastDisplayTime + [link refreshPeriod] < now)
2554 [self setAutodisplay:YES];
2558 _lastDisplayTime = now;
2562 [self setAutodisplay:YES];
2564 [super setViewsNeedDisplay:value];
2569 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2572 [self setAutodisplay:NO];
2575 - (void) displayIfNeeded
2577 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2578 [super displayIfNeeded];
2580 [self setAutodisplay:NO];
2583 - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2586 [self setAutodisplay:YES];
2587 [super setFrame:frameRect display:flag];
2590 - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2593 [self setAutodisplay:YES];
2594 [super setFrame:frameRect display:displayFlag animate:animateFlag];
2597 - (void) windowDidDrawContent
2599 if (!drawnSinceShown)
2601 drawnSinceShown = YES;
2602 dispatch_async(dispatch_get_main_queue(), ^{
2603 [self checkTransparency];
2608 - (NSArray*) childWineWindows
2610 NSArray* childWindows = self.childWindows;
2611 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2612 return [child isKindOfClass:[WineWindow class]];
2614 return [childWindows objectsAtIndexes:indexes];
2617 - (void) updateForGLSubviews
2619 if (gl_surface_mode == GL_SURFACE_BEHIND)
2620 [self checkTransparency];
2623 - (void) setRetinaMode:(int)mode
2626 double scale = mode ? 0.5 : 2.0;
2627 NSAffineTransform* transform = [NSAffineTransform transform];
2629 [transform scaleBy:scale];
2632 [shape transformUsingAffineTransform:transform];
2634 for (WineBaseView* subview in [self.contentView subviews])
2636 if ([subview isKindOfClass:[WineBaseView class]])
2637 [subview setRetinaMode:mode];
2640 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2641 frame.origin.x *= scale;
2642 frame.origin.y *= scale;
2643 frame.size.width *= scale;
2644 frame.size.height *= scale;
2645 frame = [self frameRectForContentRect:frame];
2647 savedContentMinSize = [transform transformSize:savedContentMinSize];
2648 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2649 savedContentMaxSize.width *= scale;
2650 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2651 savedContentMaxSize.height *= scale;
2653 self.contentMinSize = [transform transformSize:self.contentMinSize];
2654 NSSize temp = self.contentMaxSize;
2655 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2656 temp.width *= scale;
2657 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2658 temp.height *= scale;
2659 self.contentMaxSize = temp;
2661 ignore_windowResize = TRUE;
2662 [self setFrameAndWineFrame:frame];
2663 ignore_windowResize = FALSE;
2668 * ---------- NSResponder method overrides ----------
2670 - (void) keyDown:(NSEvent *)theEvent
2672 if ([theEvent isARepeat])
2674 if (!allowKeyRepeats)
2678 allowKeyRepeats = YES;
2680 [self postKeyEvent:theEvent];
2683 - (void) flagsChanged:(NSEvent *)theEvent
2685 static const struct {
2689 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2690 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2691 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2692 { NX_DEVICELCTLKEYMASK, kVK_Control },
2693 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2694 { NX_DEVICELALTKEYMASK, kVK_Option },
2695 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2696 { NX_DEVICELCMDKEYMASK, kVK_Command },
2697 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2700 NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2702 int i, last_changed;
2704 fix_device_modifiers_by_generic(&modifierFlags);
2705 changed = modifierFlags ^ lastModifierFlags;
2708 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2709 if (changed & modifiers[i].mask)
2712 for (i = 0; i <= last_changed; i++)
2714 if (changed & modifiers[i].mask)
2716 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2719 allowKeyRepeats = NO;
2721 if (i == last_changed)
2722 lastModifierFlags = modifierFlags;
2725 lastModifierFlags ^= modifiers[i].mask;
2726 fix_generic_modifiers_by_device(&lastModifierFlags);
2729 // Caps lock generates one event for each press-release action.
2730 // We need to simulate a pair of events for each actual event.
2731 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2733 [self postKey:modifiers[i].keycode
2735 modifiers:lastModifierFlags
2736 event:(NSEvent*)theEvent];
2740 [self postKey:modifiers[i].keycode
2742 modifiers:lastModifierFlags
2743 event:(NSEvent*)theEvent];
2748 - (void) applicationWillHide
2750 savedVisibleState = [self isVisible];
2753 - (void) applicationDidUnhide
2755 if ([self isVisible])
2756 [self becameEligibleParentOrChild];
2761 * ---------- NSWindowDelegate methods ----------
2763 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2765 macdrv_query* query;
2768 query = macdrv_create_query();
2769 query->type = QUERY_MIN_MAX_INFO;
2770 query->window = (macdrv_window)[self retain];
2771 [self.queue query:query timeout:0.5];
2772 macdrv_release_query(query);
2774 size = [self contentMaxSize];
2775 if (proposedSize.width < size.width)
2776 size.width = proposedSize.width;
2777 if (proposedSize.height < size.height)
2778 size.height = proposedSize.height;
2782 - (void)windowDidBecomeKey:(NSNotification *)notification
2784 WineApplicationController* controller = [WineApplicationController sharedController];
2785 NSEvent* event = [controller lastFlagsChanged];
2787 [self flagsChanged:event];
2789 if (causing_becomeKeyWindow == self) return;
2791 [controller windowGotFocus:self];
2794 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2796 [self checkWineDisplayLink];
2799 - (void) windowDidChangeScreen:(NSNotification*)notification
2801 [self checkWineDisplayLink];
2804 - (void)windowDidDeminiaturize:(NSNotification *)notification
2806 WineApplicationController* controller = [WineApplicationController sharedController];
2808 if (!ignore_windowDeminiaturize)
2809 [self postDidUnminimizeEvent];
2810 ignore_windowDeminiaturize = FALSE;
2812 [self becameEligibleParentOrChild];
2814 if (fullscreen && [self isOnActiveSpace])
2815 [controller updateFullscreenWindows];
2816 [controller adjustWindowLevels];
2818 if (![self parentWindow])
2819 [self postBroughtForwardEvent];
2821 if (!self.disabled && !self.noActivate)
2823 causing_becomeKeyWindow = self;
2824 [self makeKeyWindow];
2825 causing_becomeKeyWindow = nil;
2826 [controller windowGotFocus:self];
2829 [self windowDidResize:notification];
2830 [self checkWineDisplayLink];
2833 - (void) windowDidEndLiveResize:(NSNotification *)notification
2837 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2838 [queue postEvent:event];
2839 macdrv_release_event(event);
2843 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2845 enteringFullScreen = FALSE;
2846 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2847 if (pendingOrderOut)
2851 - (void) windowDidExitFullScreen:(NSNotification*)notification
2853 exitingFullScreen = FALSE;
2854 [self setFrameAndWineFrame:nonFullscreenFrame];
2855 [self windowDidResize:nil];
2856 if (pendingOrderOut)
2860 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2862 enteringFullScreen = FALSE;
2863 enteredFullScreenTime = 0;
2864 if (pendingOrderOut)
2868 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2870 exitingFullScreen = FALSE;
2871 [self windowDidResize:nil];
2872 if (pendingOrderOut)
2876 - (void)windowDidMiniaturize:(NSNotification *)notification
2878 if (fullscreen && [self isOnActiveSpace])
2879 [[WineApplicationController sharedController] updateFullscreenWindows];
2880 [self checkWineDisplayLink];
2883 - (void)windowDidMove:(NSNotification *)notification
2885 [self windowDidResize:notification];
2888 - (void)windowDidResignKey:(NSNotification *)notification
2890 macdrv_event* event;
2892 if (causing_becomeKeyWindow) return;
2894 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2895 [queue postEvent:event];
2896 macdrv_release_event(event);
2899 - (void)windowDidResize:(NSNotification *)notification
2901 NSRect frame = self.wine_fractionalFrame;
2903 if ([self inLiveResize])
2905 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2906 resizingFromLeft = TRUE;
2907 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2908 resizingFromTop = TRUE;
2911 if (ignore_windowResize || exitingFullScreen) return;
2913 if ([self preventResizing])
2915 NSRect contentRect = [self contentRectForFrameRect:frame];
2916 [self setContentMinSize:contentRect.size];
2917 [self setContentMaxSize:contentRect.size];
2920 [self postWindowFrameChanged:frame
2921 fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2922 resizing:[self inLiveResize]];
2924 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2925 [self updateFullscreen];
2928 - (BOOL)windowShouldClose:(id)sender
2930 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2931 [queue postEvent:event];
2932 macdrv_release_event(event);
2936 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2940 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2941 [queue postEvent:event];
2942 macdrv_release_event(event);
2945 else if (!resizable)
2947 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2948 [queue postEvent:event];
2949 macdrv_release_event(event);
2956 - (void) windowWillClose:(NSNotification*)notification
2960 if (fakingClose) return;
2961 if (latentParentWindow)
2963 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2964 self.latentParentWindow = nil;
2967 for (child in latentChildWindows)
2969 if (child.latentParentWindow == self)
2970 child.latentParentWindow = nil;
2972 [latentChildWindows removeAllObjects];
2975 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2977 enteringFullScreen = TRUE;
2978 nonFullscreenFrame = self.wine_fractionalFrame;
2981 - (void) windowWillExitFullScreen:(NSNotification*)notification
2983 exitingFullScreen = TRUE;
2984 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2987 - (void)windowWillMiniaturize:(NSNotification *)notification
2989 [self becameIneligibleParentOrChild];
2990 [self grabDockIconSnapshotFromWindow:nil force:NO];
2993 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2995 if ([self inLiveResize])
2998 return self.wine_fractionalFrame.size;
3001 macdrv_query* query;
3003 rect = [self frame];
3004 if (resizingFromLeft)
3005 rect.origin.x = NSMaxX(rect) - frameSize.width;
3006 if (!resizingFromTop)
3007 rect.origin.y = NSMaxY(rect) - frameSize.height;
3008 rect.size = frameSize;
3009 rect = [self contentRectForFrameRect:rect];
3010 [[WineApplicationController sharedController] flipRect:&rect];
3012 query = macdrv_create_query();
3013 query->type = QUERY_RESIZE_SIZE;
3014 query->window = (macdrv_window)[self retain];
3015 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3016 query->resize_size.from_left = resizingFromLeft;
3017 query->resize_size.from_top = resizingFromTop;
3019 if ([self.queue query:query timeout:0.1])
3021 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3022 rect = [self frameRectForContentRect:rect];
3023 frameSize = rect.size;
3026 macdrv_release_query(query);
3032 - (void) windowWillStartLiveResize:(NSNotification *)notification
3034 [self endWindowDragging];
3038 macdrv_event* event;
3039 NSRect frame = [self contentRectForFrameRect:self.frame];
3041 [[WineApplicationController sharedController] flipRect:&frame];
3043 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3044 event->window_restore_requested.keep_frame = TRUE;
3045 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3046 [queue postEvent:event];
3047 macdrv_release_event(event);
3050 [self sendResizeStartQuery];
3052 frameAtResizeStart = [self frame];
3053 resizingFromLeft = resizingFromTop = FALSE;
3056 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3058 macdrv_query* query;
3059 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3062 query = macdrv_create_query();
3063 query->type = QUERY_MIN_MAX_INFO;
3064 query->window = (macdrv_window)[self retain];
3065 [self.queue query:query timeout:0.5];
3066 macdrv_release_query(query);
3068 currentContentRect = [self contentRectForFrameRect:[self frame]];
3069 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3071 maxSize = [self contentMaxSize];
3072 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3073 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3075 // Try to keep the top-left corner where it is.
3076 newContentRect.origin.x = NSMinX(currentContentRect);
3077 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3079 // If that pushes the bottom or right off the screen, pull it up and to the left.
3080 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3081 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3082 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3083 if (NSMinY(newContentRect) < NSMinY(screenRect))
3084 newContentRect.origin.y = NSMinY(screenRect);
3086 // If that pushes the top or left off the screen, push it down and the right
3087 // again. Do this last because the top-left corner is more important than the
3089 if (NSMinX(newContentRect) < NSMinX(screenRect))
3090 newContentRect.origin.x = NSMinX(screenRect);
3091 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3092 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3094 return [self frameRectForContentRect:newContentRect];
3099 * ---------- NSPasteboardOwner methods ----------
3101 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3103 macdrv_query* query = macdrv_create_query();
3104 query->type = QUERY_PASTEBOARD_DATA;
3105 query->window = (macdrv_window)[self retain];
3106 query->pasteboard_data.type = (CFStringRef)[type copy];
3108 [self.queue query:query timeout:3];
3109 macdrv_release_query(query);
3112 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3114 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3115 [queue postEvent:event];
3116 macdrv_release_event(event);
3121 * ---------- NSDraggingDestination methods ----------
3123 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3125 return [self draggingUpdated:sender];
3128 - (void) draggingExited:(id <NSDraggingInfo>)sender
3130 // This isn't really a query. We don't need any response. However, it
3131 // has to be processed in a similar manner as the other drag-and-drop
3132 // queries in order to maintain the proper order of operations.
3133 macdrv_query* query = macdrv_create_query();
3134 query->type = QUERY_DRAG_EXITED;
3135 query->window = (macdrv_window)[self retain];
3137 [self.queue query:query timeout:0.1];
3138 macdrv_release_query(query);
3141 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3143 NSDragOperation ret;
3144 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3145 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3146 NSPasteboard* pb = [sender draggingPasteboard];
3148 macdrv_query* query = macdrv_create_query();
3149 query->type = QUERY_DRAG_OPERATION;
3150 query->window = (macdrv_window)[self retain];
3151 query->drag_operation.x = floor(cgpt.x);
3152 query->drag_operation.y = floor(cgpt.y);
3153 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3154 query->drag_operation.accepted_op = NSDragOperationNone;
3155 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3157 [self.queue query:query timeout:3];
3158 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3159 macdrv_release_query(query);
3164 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3167 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3168 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3169 NSPasteboard* pb = [sender draggingPasteboard];
3171 macdrv_query* query = macdrv_create_query();
3172 query->type = QUERY_DRAG_DROP;
3173 query->window = (macdrv_window)[self retain];
3174 query->drag_drop.x = floor(cgpt.x);
3175 query->drag_drop.y = floor(cgpt.y);
3176 query->drag_drop.op = [sender draggingSourceOperationMask];
3177 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3179 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3180 ret = query->status;
3181 macdrv_release_query(query);
3186 - (BOOL) wantsPeriodicDraggingUpdates
3194 /***********************************************************************
3195 * macdrv_create_cocoa_window
3197 * Create a Cocoa window with the given content frame and features (e.g.
3198 * title bar, close box, etc.).
3200 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3201 CGRect frame, void* hwnd, macdrv_event_queue queue)
3203 __block WineWindow* window;
3206 window = [[WineWindow createWindowWithFeatures:wf
3207 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3209 queue:(WineEventQueue*)queue] retain];
3212 return (macdrv_window)window;
3215 /***********************************************************************
3216 * macdrv_destroy_cocoa_window
3218 * Destroy a Cocoa window.
3220 void macdrv_destroy_cocoa_window(macdrv_window w)
3222 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3223 WineWindow* window = (WineWindow*)w;
3226 [window doOrderOut];
3229 [window.queue discardEventsMatchingMask:-1 forWindow:window];
3235 /***********************************************************************
3236 * macdrv_get_window_hwnd
3238 * Get the hwnd that was set for the window at creation.
3240 void* macdrv_get_window_hwnd(macdrv_window w)
3242 WineWindow* window = (WineWindow*)w;
3246 /***********************************************************************
3247 * macdrv_set_cocoa_window_features
3249 * Update a Cocoa window's features.
3251 void macdrv_set_cocoa_window_features(macdrv_window w,
3252 const struct macdrv_window_features* wf)
3254 WineWindow* window = (WineWindow*)w;
3257 [window setWindowFeatures:wf];
3261 /***********************************************************************
3262 * macdrv_set_cocoa_window_state
3264 * Update a Cocoa window's state.
3266 void macdrv_set_cocoa_window_state(macdrv_window w,
3267 const struct macdrv_window_state* state)
3269 WineWindow* window = (WineWindow*)w;
3272 [window setMacDrvState:state];
3276 /***********************************************************************
3277 * macdrv_set_cocoa_window_title
3279 * Set a Cocoa window's title.
3281 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3284 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3285 WineWindow* window = (WineWindow*)w;
3286 NSString* titleString;
3289 titleString = [NSString stringWithCharacters:title length:length];
3292 OnMainThreadAsync(^{
3293 [window setTitle:titleString];
3294 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3295 [NSApp changeWindowsItem:window title:titleString filename:NO];
3301 /***********************************************************************
3302 * macdrv_order_cocoa_window
3304 * Reorder a Cocoa window relative to other windows. If prev is
3305 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3306 * it is ordered above that window. Otherwise, it is ordered to the
3309 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3310 macdrv_window n, int activate)
3312 WineWindow* window = (WineWindow*)w;
3313 WineWindow* prev = (WineWindow*)p;
3314 WineWindow* next = (WineWindow*)n;
3316 OnMainThreadAsync(^{
3317 [window orderBelow:prev
3321 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3323 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3327 /***********************************************************************
3328 * macdrv_hide_cocoa_window
3330 * Hides a Cocoa window.
3332 void macdrv_hide_cocoa_window(macdrv_window w)
3334 WineWindow* window = (WineWindow*)w;
3337 [window doOrderOut];
3341 /***********************************************************************
3342 * macdrv_set_cocoa_window_frame
3344 * Move a Cocoa window.
3346 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3348 WineWindow* window = (WineWindow*)w;
3351 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3355 /***********************************************************************
3356 * macdrv_get_cocoa_window_frame
3358 * Gets the frame of a Cocoa window.
3360 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3362 WineWindow* window = (WineWindow*)w;
3367 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3368 [[WineApplicationController sharedController] flipRect:&frame];
3369 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3373 /***********************************************************************
3374 * macdrv_set_cocoa_parent_window
3376 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3377 * the parent window.
3379 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3381 WineWindow* window = (WineWindow*)w;
3384 [window setMacDrvParentWindow:(WineWindow*)parent];
3388 /***********************************************************************
3389 * macdrv_set_window_surface
3391 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3393 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3394 WineWindow* window = (WineWindow*)w;
3397 window.surface = surface;
3398 window.surface_mutex = mutex;
3404 /***********************************************************************
3405 * macdrv_window_needs_display
3407 * Mark a window as needing display in a specified rect (in non-client
3408 * area coordinates).
3410 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3412 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3413 WineWindow* window = (WineWindow*)w;
3415 OnMainThreadAsync(^{
3416 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3422 /***********************************************************************
3423 * macdrv_set_window_shape
3425 * Sets the shape of a Cocoa window from an array of rectangles. If
3426 * rects is NULL, resets the window's shape to its frame.
3428 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3430 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3431 WineWindow* window = (WineWindow*)w;
3434 if (!rects || !count)
3437 window.shapeData = nil;
3438 [window checkEmptyShaped];
3442 size_t length = sizeof(*rects) * count;
3443 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3448 path = [NSBezierPath bezierPath];
3449 for (i = 0; i < count; i++)
3450 [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3451 window.shape = path;
3452 window.shapeData = [NSData dataWithBytes:rects length:length];
3453 [window checkEmptyShaped];
3461 /***********************************************************************
3462 * macdrv_set_window_alpha
3464 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3466 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3467 WineWindow* window = (WineWindow*)w;
3469 [window setAlphaValue:alpha];
3474 /***********************************************************************
3475 * macdrv_set_window_color_key
3477 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3480 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3481 WineWindow* window = (WineWindow*)w;
3484 window.colorKeyed = TRUE;
3485 window.colorKeyRed = keyRed;
3486 window.colorKeyGreen = keyGreen;
3487 window.colorKeyBlue = keyBlue;
3488 [window checkTransparency];
3494 /***********************************************************************
3495 * macdrv_clear_window_color_key
3497 void macdrv_clear_window_color_key(macdrv_window w)
3499 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3500 WineWindow* window = (WineWindow*)w;
3503 window.colorKeyed = FALSE;
3504 [window checkTransparency];
3510 /***********************************************************************
3511 * macdrv_window_use_per_pixel_alpha
3513 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3515 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3516 WineWindow* window = (WineWindow*)w;
3519 window.usePerPixelAlpha = use_per_pixel_alpha;
3520 [window checkTransparency];
3526 /***********************************************************************
3527 * macdrv_give_cocoa_window_focus
3529 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3530 * orders it front and, if its frame was not within the desktop bounds,
3531 * Cocoa will typically move it on-screen.
3533 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3535 WineWindow* window = (WineWindow*)w;
3538 [window makeFocused:activate];
3542 /***********************************************************************
3543 * macdrv_set_window_min_max_sizes
3545 * Sets the window's minimum and maximum content sizes.
3547 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3549 WineWindow* window = (WineWindow*)w;
3552 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3556 /***********************************************************************
3557 * macdrv_create_view
3559 * Creates and returns a view with the specified frame rect. The
3560 * caller is responsible for calling macdrv_dispose_view() on the view
3561 * when it is done with it.
3563 macdrv_view macdrv_create_view(CGRect rect)
3565 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3566 __block WineContentView* view;
3568 if (CGRectIsNull(rect)) rect = CGRectZero;
3571 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3573 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3574 [view setAutoresizesSubviews:NO];
3575 [view setAutoresizingMask:NSViewNotSizable];
3576 [view setHidden:YES];
3577 [nc addObserver:view
3578 selector:@selector(updateGLContexts)
3579 name:NSViewGlobalFrameDidChangeNotification
3581 [nc addObserver:view
3582 selector:@selector(updateGLContexts)
3583 name:NSApplicationDidChangeScreenParametersNotification
3588 return (macdrv_view)view;
3591 /***********************************************************************
3592 * macdrv_dispose_view
3594 * Destroys a view previously returned by macdrv_create_view.
3596 void macdrv_dispose_view(macdrv_view v)
3598 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3599 WineContentView* view = (WineContentView*)v;
3602 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3603 WineWindow* window = (WineWindow*)[view window];
3605 [nc removeObserver:view
3606 name:NSViewGlobalFrameDidChangeNotification
3608 [nc removeObserver:view
3609 name:NSApplicationDidChangeScreenParametersNotification
3611 [view removeFromSuperview];
3613 [window updateForGLSubviews];
3619 /***********************************************************************
3620 * macdrv_set_view_frame
3622 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3624 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3625 WineContentView* view = (WineContentView*)v;
3627 if (CGRectIsNull(rect)) rect = CGRectZero;
3629 OnMainThreadAsync(^{
3630 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3631 NSRect oldFrame = [view frame];
3633 if (!NSEqualRects(oldFrame, newFrame))
3635 [[view superview] setNeedsDisplayInRect:oldFrame];
3636 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3637 [view setFrameSize:newFrame.size];
3638 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3639 [view setFrameOrigin:newFrame.origin];
3641 [view setFrame:newFrame];
3642 [view setNeedsDisplay:YES];
3646 int backing_size[2] = { 0 };
3647 [view wine_setBackingSize:backing_size];
3649 [(WineWindow*)[view window] updateForGLSubviews];
3656 /***********************************************************************
3657 * macdrv_set_view_superview
3659 * Move a view to a new superview and position it relative to its
3660 * siblings. If p is non-NULL, the view is ordered behind it.
3661 * Otherwise, the view is ordered above n. If s is NULL, use the
3662 * content view of w as the new superview.
3664 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3666 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3667 WineContentView* view = (WineContentView*)v;
3668 WineContentView* superview = (WineContentView*)s;
3669 WineWindow* window = (WineWindow*)w;
3670 WineContentView* prev = (WineContentView*)p;
3671 WineContentView* next = (WineContentView*)n;
3674 superview = [window contentView];
3676 OnMainThreadAsync(^{
3677 if (superview == [view superview])
3679 NSArray* subviews = [superview subviews];
3680 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3681 if (!prev && !next && index == [subviews count] - 1)
3683 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3685 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3689 WineWindow* oldWindow = (WineWindow*)[view window];
3690 WineWindow* newWindow = (WineWindow*)[superview window];
3692 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3693 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3694 [view removeFromSuperview];
3697 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3699 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3701 if (oldWindow != newWindow)
3703 [oldWindow updateForGLSubviews];
3704 [newWindow updateForGLSubviews];
3711 /***********************************************************************
3712 * macdrv_set_view_hidden
3714 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3716 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3717 WineContentView* view = (WineContentView*)v;
3719 OnMainThreadAsync(^{
3720 [view setHidden:hidden];
3721 [(WineWindow*)view.window updateForGLSubviews];
3727 /***********************************************************************
3728 * macdrv_add_view_opengl_context
3730 * Add an OpenGL context to the list being tracked for each view.
3732 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3734 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3735 WineContentView* view = (WineContentView*)v;
3736 WineOpenGLContext *context = (WineOpenGLContext*)c;
3739 [view addGLContext:context];
3745 /***********************************************************************
3746 * macdrv_remove_view_opengl_context
3748 * Add an OpenGL context to the list being tracked for each view.
3750 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3752 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3753 WineContentView* view = (WineContentView*)v;
3754 WineOpenGLContext *context = (WineOpenGLContext*)c;
3756 OnMainThreadAsync(^{
3757 [view removeGLContext:context];
3763 #ifdef HAVE_METAL_METAL_H
3764 macdrv_metal_device macdrv_create_metal_device(void)
3766 macdrv_metal_device ret;
3768 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3769 if (MTLCreateSystemDefaultDevice == NULL)
3773 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3774 ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3779 void macdrv_release_metal_device(macdrv_metal_device d)
3781 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3782 [(id<MTLDevice>)d release];
3786 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3788 id<MTLDevice> device = (id<MTLDevice>)d;
3789 WineContentView* view = (WineContentView*)v;
3790 __block WineMetalView *metalView;
3793 metalView = [view newMetalViewWithDevice:device];
3796 return (macdrv_metal_view)metalView;
3799 macdrv_metal_layer macdrv_view_get_metal_layer(macdrv_metal_view v)
3801 WineMetalView* view = (WineMetalView*)v;
3802 __block CAMetalLayer* layer;
3805 layer = (CAMetalLayer*)view.layer;
3808 return (macdrv_metal_layer)layer;
3811 void macdrv_view_release_metal_view(macdrv_metal_view v)
3813 WineMetalView* view = (WineMetalView*)v;
3815 [view removeFromSuperview];
3821 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3823 WineContentView* view = (WineContentView*)v;
3825 if (![view isKindOfClass:[WineContentView class]])
3828 [view wine_getBackingSize:backing_size];
3832 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3834 WineContentView* view = (WineContentView*)v;
3836 if ([view isKindOfClass:[WineContentView class]])
3837 [view wine_setBackingSize:backing_size];
3840 /***********************************************************************
3841 * macdrv_window_background_color
3843 * Returns the standard Mac window background color as a 32-bit value of
3844 * the form 0x00rrggbb.
3846 uint32_t macdrv_window_background_color(void)
3848 static uint32_t result;
3849 static dispatch_once_t once;
3851 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3852 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3853 // of it is to draw with it.
3854 dispatch_once(&once, ^{
3856 unsigned char rgbx[4];
3857 unsigned char *planes = rgbx;
3858 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3865 colorSpaceName:NSCalibratedRGBColorSpace
3869 [NSGraphicsContext saveGraphicsState];
3870 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3871 [[NSColor windowBackgroundColor] set];
3872 NSRectFill(NSMakeRect(0, 0, 1, 1));
3873 [NSGraphicsContext restoreGraphicsState];
3875 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3882 /***********************************************************************
3883 * macdrv_send_text_input_event
3885 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3887 OnMainThreadAsync(^{
3889 macdrv_event* event;
3890 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3891 if (![window isKindOfClass:[WineWindow class]])
3893 window = (WineWindow*)[NSApp mainWindow];
3894 if (![window isKindOfClass:[WineWindow class]])
3895 window = [[WineApplicationController sharedController] frontWineWindow];
3900 NSUInteger localFlags = flags;
3904 window.imeData = data;
3905 fix_device_modifiers_by_generic(&localFlags);
3907 // An NSEvent created with +keyEventWithType:... is internally marked
3908 // as synthetic and doesn't get sent through input methods. But one
3909 // created from a CGEvent doesn't have that problem.
3910 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3911 CGEventSetFlags(c, localFlags);
3912 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3913 event = [NSEvent eventWithCGEvent:c];
3916 window.commandDone = FALSE;
3917 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3922 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3923 event->sent_text_input.handled = ret;
3924 event->sent_text_input.done = done;
3925 [[window queue] postEvent:event];
3926 macdrv_release_event(event);
3930 void macdrv_clear_ime_text(void)
3932 OnMainThreadAsync(^{
3933 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3934 if (![window isKindOfClass:[WineWindow class]])
3936 window = (WineWindow*)[NSApp mainWindow];
3937 if (![window isKindOfClass:[WineWindow class]])
3938 window = [[WineApplicationController sharedController] frontWineWindow];
3941 [[window contentView] clearMarkedText];