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) completeText:(NSString*)text
708 WineWindow* window = (WineWindow*)[self window];
710 event = macdrv_create_event(IM_SET_TEXT, window);
711 event->im_set_text.data = [window imeData];
712 event->im_set_text.text = (CFStringRef)[text copy];
713 event->im_set_text.complete = TRUE;
715 [[window queue] postEvent:event];
717 macdrv_release_event(event);
719 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
720 markedTextSelection = NSMakeRange(0, 0);
721 [[self inputContext] discardMarkedText];
724 - (void) didAddSubview:(NSView*)subview
726 if ([subview isKindOfClass:[WineContentView class]])
728 WineContentView* view = (WineContentView*)subview;
729 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
730 [self invalidateHasGLDescendant];
732 [super didAddSubview:subview];
735 - (void) willRemoveSubview:(NSView*)subview
737 if ([subview isKindOfClass:[WineContentView class]])
739 WineContentView* view = (WineContentView*)subview;
740 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
741 [self invalidateHasGLDescendant];
743 #ifdef HAVE_METAL_METAL_H
744 if (subview == _metalView)
747 [super willRemoveSubview:subview];
751 * ---------- NSTextInputClient methods ----------
753 - (NSTextInputContext*) inputContext
756 markedText = [[NSMutableAttributedString alloc] init];
757 return [super inputContext];
760 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
762 if ([string isKindOfClass:[NSAttributedString class]])
763 string = [string string];
765 if ([string isKindOfClass:[NSString class]])
766 [self completeText:string];
769 - (void) doCommandBySelector:(SEL)aSelector
771 [(WineWindow*)[self window] setCommandDone:TRUE];
774 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
776 if ([string isKindOfClass:[NSAttributedString class]])
777 string = [string string];
779 if ([string isKindOfClass:[NSString class]])
782 WineWindow* window = (WineWindow*)[self window];
784 if (replacementRange.location == NSNotFound)
785 replacementRange = NSMakeRange(0, [markedText length]);
787 [markedText replaceCharactersInRange:replacementRange withString:string];
788 markedTextSelection = selectedRange;
789 markedTextSelection.location += replacementRange.location;
791 event = macdrv_create_event(IM_SET_TEXT, window);
792 event->im_set_text.data = [window imeData];
793 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
794 event->im_set_text.complete = FALSE;
795 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
797 [[window queue] postEvent:event];
799 macdrv_release_event(event);
801 [[self inputContext] invalidateCharacterCoordinates];
807 [self completeText:nil];
810 - (NSRange) selectedRange
812 return markedTextSelection;
815 - (NSRange) markedRange
817 NSRange range = NSMakeRange(0, [markedText length]);
819 range.location = NSNotFound;
823 - (BOOL) hasMarkedText
825 return [markedText length] > 0;
828 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
830 if (aRange.location >= [markedText length])
833 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
835 *actualRange = aRange;
836 return [markedText attributedSubstringFromRange:aRange];
839 - (NSArray*) validAttributesForMarkedText
841 return [NSArray array];
844 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
847 WineWindow* window = (WineWindow*)[self window];
850 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
852 query = macdrv_create_query();
853 query->type = QUERY_IME_CHAR_RECT;
854 query->window = (macdrv_window)[window retain];
855 query->ime_char_rect.data = [window imeData];
856 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
858 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
860 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
861 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
862 [[WineApplicationController sharedController] flipRect:&ret];
865 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
867 macdrv_release_query(query);
870 *actualRange = aRange;
874 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
879 - (NSInteger) windowLevel
881 return [[self window] level];
887 #ifdef HAVE_METAL_METAL_H
888 @implementation WineMetalView
890 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
892 self = [super initWithFrame:frame];
895 _device = [device retain];
896 self.wantsLayer = YES;
897 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
908 - (void) setRetinaMode:(int)mode
910 self.layer.contentsScale = mode ? 2.0 : 1.0;
911 [super setRetinaMode:mode];
914 - (CALayer*) makeBackingLayer
916 CAMetalLayer *layer = [CAMetalLayer layer];
917 layer.device = _device;
918 layer.framebufferOnly = YES;
919 layer.magnificationFilter = kCAFilterNearest;
920 layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
921 layer.contentsScale = retina_on ? 2.0 : 1.0;
934 @implementation WineWindow
936 static WineWindow* causing_becomeKeyWindow;
938 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
939 @synthesize drawnSinceShown;
940 @synthesize surface, surface_mutex;
941 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
942 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
943 @synthesize usePerPixelAlpha;
944 @synthesize imeData, commandDone;
946 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
947 windowFrame:(NSRect)window_frame
949 queue:(WineEventQueue*)queue
952 WineContentView* contentView;
953 NSTrackingArea* trackingArea;
954 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
956 [[WineApplicationController sharedController] flipRect:&window_frame];
958 window = [[[self alloc] initWithContentRect:window_frame
959 styleMask:style_mask_for_features(wf)
960 backing:NSBackingStoreBuffered
961 defer:YES] autorelease];
963 if (!window) return nil;
965 /* Standardize windows to eliminate differences between titled and
966 borderless windows and between NSWindow and NSPanel. */
967 [window setHidesOnDeactivate:NO];
968 [window setReleasedWhenClosed:NO];
970 [window setOneShot:YES];
971 [window disableCursorRects];
972 [window setShowsResizeIndicator:NO];
973 [window setHasShadow:wf->shadow];
974 [window setAcceptsMouseMovedEvents:YES];
975 [window setDelegate:window];
976 [window setBackgroundColor:[NSColor clearColor]];
977 [window setOpaque:NO];
979 window.queue = queue;
980 window->savedContentMinSize = NSZeroSize;
981 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
982 window->resizable = wf->resizable;
983 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
985 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
986 (NSString*)kUTTypeContent,
989 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
992 [contentView setAutoresizesSubviews:NO];
994 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
995 because they give us mouse moves in the background. */
996 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
997 options:(NSTrackingMouseMoved |
998 NSTrackingActiveAlways |
999 NSTrackingInVisibleRect)
1001 userInfo:nil] autorelease];
1004 [contentView addTrackingArea:trackingArea];
1006 [window setContentView:contentView];
1007 [window setInitialFirstResponder:contentView];
1009 [nc addObserver:window
1010 selector:@selector(updateFullscreen)
1011 name:NSApplicationDidChangeScreenParametersNotification
1013 [window updateFullscreen];
1015 [nc addObserver:window
1016 selector:@selector(applicationWillHide)
1017 name:NSApplicationWillHideNotification
1019 [nc addObserver:window
1020 selector:@selector(applicationDidUnhide)
1021 name:NSApplicationDidUnhideNotification
1024 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1025 selector:@selector(checkWineDisplayLink)
1026 name:NSWorkspaceActiveSpaceDidChangeNotification
1027 object:[NSWorkspace sharedWorkspace]];
1029 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1036 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1037 [[NSNotificationCenter defaultCenter] removeObserver:self];
1039 [latentChildWindows release];
1040 [latentParentWindow release];
1042 [shapeData release];
1046 - (BOOL) preventResizing
1048 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1049 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
1052 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1054 if (allow_immovable_windows && (disabled || inMaximized))
1056 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1062 - (void) adjustFeaturesForState
1064 NSUInteger style = [self styleMask];
1066 if (style & NSClosableWindowMask)
1067 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1068 if (style & NSMiniaturizableWindowMask)
1069 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1070 if (style & NSResizableWindowMask)
1071 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1072 if ([self respondsToSelector:@selector(toggleFullScreen:)])
1074 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1075 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1078 if ([self preventResizing])
1080 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1081 [self setContentMinSize:size];
1082 [self setContentMaxSize:size];
1086 [self setContentMaxSize:savedContentMaxSize];
1087 [self setContentMinSize:savedContentMinSize];
1090 if (allow_immovable_windows || cursor_clipping_locks_windows)
1091 [self setMovable:[self allowsMovingWithMaximized:maximized]];
1094 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1096 if ([self respondsToSelector:@selector(toggleFullScreen:)])
1098 NSUInteger style = [self styleMask];
1100 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1101 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized &&
1102 !(self.parentWindow || self.latentParentWindow))
1104 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1105 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1109 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1110 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1111 if (style & NSFullScreenWindowMask)
1112 [super toggleFullScreen:nil];
1116 if (behavior != [self collectionBehavior])
1118 [self setCollectionBehavior:behavior];
1119 [self adjustFeaturesForState];
1123 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1125 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
1126 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
1127 NSUInteger currentStyle = [self styleMask];
1128 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1130 if (newStyle != currentStyle)
1132 NSString* title = [[[self title] copy] autorelease];
1133 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1134 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
1135 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
1137 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
1138 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
1139 // just NSTitledWindowMask, the window buttons should disappear rather
1140 // than just being disabled. But they don't. Similarly in reverse.
1141 // The workaround is to also toggle NSClosableWindowMask at the same time.
1142 [self setStyleMask:newStyle ^ NSClosableWindowMask];
1144 [self setStyleMask:newStyle];
1146 // -setStyleMask: resets the firstResponder to the window. Set it
1147 // back to the content view.
1148 if ([[self contentView] acceptsFirstResponder])
1149 [self makeFirstResponder:[self contentView]];
1151 [self adjustFullScreenBehavior:[self collectionBehavior]];
1153 if ([[self title] length] == 0 && [title length] > 0)
1154 [self setTitle:title];
1157 resizable = wf->resizable;
1158 [self adjustFeaturesForState];
1159 [self setHasShadow:wf->shadow];
1162 // Indicates if the window would be visible if the app were not hidden.
1163 - (BOOL) wouldBeVisible
1165 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1168 - (BOOL) isOrderedIn
1170 return [self wouldBeVisible] || [self isMiniaturized];
1173 - (NSInteger) minimumLevelForActive:(BOOL)active
1177 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1178 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1179 level = NSFloatingWindowLevel;
1181 level = NSNormalWindowLevel;
1187 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1189 if (captured || fullscreen)
1192 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1194 level = NSStatusWindowLevel + 1;
1204 - (void) postDidUnminimizeEvent
1206 macdrv_event* event;
1208 /* Coalesce events by discarding any previous ones still in the queue. */
1209 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1212 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1213 [queue postEvent:event];
1214 macdrv_release_event(event);
1217 - (void) sendResizeStartQuery
1219 macdrv_query* query = macdrv_create_query();
1220 query->type = QUERY_RESIZE_START;
1221 query->window = (macdrv_window)[self retain];
1223 [self.queue query:query timeout:0.3];
1224 macdrv_release_query(query);
1227 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1229 NSWindowCollectionBehavior behavior;
1231 self.disabled = state->disabled;
1232 self.noActivate = state->no_activate;
1234 if (self.floating != state->floating)
1236 self.floating = state->floating;
1237 if (state->floating)
1239 // Became floating. If child of non-floating window, make that
1240 // relationship latent.
1241 WineWindow* parent = (WineWindow*)[self parentWindow];
1242 if (parent && !parent.floating)
1243 [self becameIneligibleChild];
1247 // Became non-floating. If parent of floating children, make that
1248 // relationship latent.
1250 for (child in [self childWineWindows])
1253 [child becameIneligibleChild];
1257 // Check our latent relationships. If floating status was the only
1258 // reason they were latent, then make them active.
1259 if ([self isVisible])
1260 [self becameEligibleParentOrChild];
1262 [[WineApplicationController sharedController] adjustWindowLevels];
1265 if (state->minimized_valid)
1267 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1269 pendingMinimize = FALSE;
1270 if (state->minimized && ![self isMiniaturized])
1272 if ([self wouldBeVisible])
1274 if ([self styleMask] & NSFullScreenWindowMask)
1276 [self postDidUnminimizeEvent];
1277 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1281 [super miniaturize:nil];
1282 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1283 event_mask_for_type(WINDOW_GOT_FOCUS) |
1284 event_mask_for_type(WINDOW_LOST_FOCUS);
1288 pendingMinimize = TRUE;
1290 else if (!state->minimized && [self isMiniaturized])
1292 ignore_windowDeminiaturize = TRUE;
1293 [self deminiaturize:nil];
1294 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1298 [queue discardEventsMatchingMask:discard forWindow:self];
1301 if (state->maximized != maximized)
1303 maximized = state->maximized;
1304 [self adjustFeaturesForState];
1306 if (!maximized && [self inLiveResize])
1307 [self sendResizeStartQuery];
1310 behavior = NSWindowCollectionBehaviorDefault;
1311 if (state->excluded_by_expose)
1312 behavior |= NSWindowCollectionBehaviorTransient;
1314 behavior |= NSWindowCollectionBehaviorManaged;
1315 if (state->excluded_by_cycle)
1317 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1318 if ([self isOrderedIn])
1319 [NSApp removeWindowsItem:self];
1323 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1324 if ([self isOrderedIn])
1325 [NSApp addWindowsItem:self title:[self title] filename:NO];
1327 [self adjustFullScreenBehavior:behavior];
1330 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1332 BOOL reordered = FALSE;
1334 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1336 if ([self level] > [child level])
1337 [child setLevel:[self level]];
1338 if (![child isVisible])
1339 [child setAutodisplay:YES];
1340 [self addChildWindow:child ordered:NSWindowAbove];
1341 [child checkWineDisplayLink];
1342 [latentChildWindows removeObjectIdenticalTo:child];
1343 child.latentParentWindow = nil;
1348 if (!latentChildWindows)
1349 latentChildWindows = [[NSMutableArray alloc] init];
1350 if (![latentChildWindows containsObject:child])
1351 [latentChildWindows addObject:child];
1352 child.latentParentWindow = self;
1358 - (BOOL) addChildWineWindow:(WineWindow*)child
1360 return [self addChildWineWindow:child assumeVisible:FALSE];
1363 - (void) removeChildWineWindow:(WineWindow*)child
1365 [self removeChildWindow:child];
1366 if (child.latentParentWindow == self)
1367 child.latentParentWindow = nil;
1368 [latentChildWindows removeObjectIdenticalTo:child];
1371 - (void) setChildWineWindows:(NSArray*)childWindows
1373 NSArray* origChildren;
1374 NSUInteger count, start, limit, i;
1376 origChildren = self.childWineWindows;
1378 // If the current and desired children arrays match up to a point, leave
1379 // those matching children alone.
1380 count = childWindows.count;
1381 limit = MIN(origChildren.count, count);
1382 for (start = 0; start < limit; start++)
1384 if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1388 // Remove all of the child windows and re-add them back-to-front so they
1389 // are in the desired order.
1390 for (i = start; i < count; i++)
1392 WineWindow* child = [childWindows objectAtIndex:i];
1393 [self removeChildWindow:child];
1395 for (i = start; i < count; i++)
1397 WineWindow* child = [childWindows objectAtIndex:i];
1398 [self addChildWindow:child ordered:NSWindowAbove];
1402 static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1404 NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1405 NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1406 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1407 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1408 if (index1 == NSNotFound)
1410 if (index2 == NSNotFound)
1411 return NSOrderedSame;
1413 return NSOrderedAscending;
1415 else if (index2 == NSNotFound)
1416 return NSOrderedDescending;
1417 else if (index1 < index2)
1418 return NSOrderedDescending;
1419 else if (index2 < index1)
1420 return NSOrderedAscending;
1422 return NSOrderedSame;
1425 - (BOOL) becameEligibleParentOrChild
1427 BOOL reordered = FALSE;
1430 if (latentParentWindow.floating || !self.floating)
1432 // If we aren't visible currently, we assume that we should be and soon
1433 // will be. So, if the latent parent is visible that's enough to assume
1434 // we can establish the parent-child relationship in Cocoa. That will
1435 // actually make us visible, which is fine.
1436 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1440 // Here, though, we may not actually be visible yet and adding a child
1441 // won't make us visible. The caller will have to call this method
1442 // again after actually making us visible.
1443 if ([self isVisible] && (count = [latentChildWindows count]))
1445 NSMutableArray* windowNumbers;
1446 NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1447 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1450 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1452 for (i = 0; i < count; i++)
1454 WineWindow* child = [latentChildWindows objectAtIndex:i];
1455 if ([child isVisible] && (self.floating || !child.floating))
1457 if (child.latentParentWindow == self)
1459 if ([self level] > [child level])
1460 [child setLevel:[self level]];
1461 [childWindows addObject:child];
1462 child.latentParentWindow = nil;
1466 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1467 [indexesToRemove addIndex:i];
1471 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1473 [childWindows sortWithOptions:NSSortStable
1474 usingComparator:^NSComparisonResult(id obj1, id obj2){
1475 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1477 [self setChildWineWindows:childWindows];
1483 - (void) becameIneligibleChild
1485 WineWindow* parent = (WineWindow*)[self parentWindow];
1488 if (!parent->latentChildWindows)
1489 parent->latentChildWindows = [[NSMutableArray alloc] init];
1490 [parent->latentChildWindows insertObject:self atIndex:0];
1491 self.latentParentWindow = parent;
1492 [parent removeChildWindow:self];
1496 - (void) becameIneligibleParentOrChild
1498 NSArray* childWindows = [self childWineWindows];
1500 [self becameIneligibleChild];
1502 if ([childWindows count])
1506 for (child in childWindows)
1508 child.latentParentWindow = self;
1509 [self removeChildWindow:child];
1512 if (latentChildWindows)
1513 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1515 latentChildWindows = [childWindows mutableCopy];
1519 // Determine if, among Wine windows, this window is directly above or below
1520 // a given other Wine window with no other Wine window intervening.
1521 // Intervening non-Wine windows are ignored.
1522 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1524 NSNumber* windowNumber;
1525 NSNumber* otherWindowNumber;
1526 NSArray* windowNumbers;
1527 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1529 if (![self isVisible] || ![otherWindow isVisible])
1532 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1533 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1534 windowNumbers = [[self class] windowNumbersWithOptions:0];
1535 windowIndex = [windowNumbers indexOfObject:windowNumber];
1536 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1538 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1541 if (orderingMode == NSWindowAbove)
1543 lowIndex = windowIndex;
1544 highIndex = otherWindowIndex;
1546 else if (orderingMode == NSWindowBelow)
1548 lowIndex = otherWindowIndex;
1549 highIndex = windowIndex;
1554 if (highIndex <= lowIndex)
1557 for (i = lowIndex + 1; i < highIndex; i++)
1559 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1560 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1561 if ([interveningWindow isKindOfClass:[WineWindow class]])
1568 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1570 NSMutableArray* windowNumbers;
1571 NSNumber* childWindowNumber;
1572 NSUInteger otherIndex;
1573 NSArray* origChildren;
1574 NSMutableArray* children;
1576 // Get the z-order from the window server and modify it to reflect the
1577 // requested window ordering.
1578 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1579 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1580 [windowNumbers removeObject:childWindowNumber];
1583 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1584 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1586 else if (mode == NSWindowAbove)
1587 [windowNumbers insertObject:childWindowNumber atIndex:0];
1589 [windowNumbers addObject:childWindowNumber];
1591 // Get our child windows and sort them in the reverse of the desired
1592 // z-order (back-to-front).
1593 origChildren = [self childWineWindows];
1594 children = [[origChildren mutableCopy] autorelease];
1595 [children sortWithOptions:NSSortStable
1596 usingComparator:^NSComparisonResult(id obj1, id obj2){
1597 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1600 [self setChildWineWindows:children];
1603 // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1604 // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1605 // (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
1606 // if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1607 // otherwise, the two ancestors are each roots of disjoint window trees
1608 // (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
1609 // (non-nil, nil) self is a descendent of other and *ancestor is the direct child
1610 - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1612 NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1616 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1622 *ancestorOfOther = child;
1626 [otherAncestors addObject:parent];
1630 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1633 NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1634 if (index != NSNotFound)
1638 *ancestorOfOther = nil;
1640 *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1646 *ancestorOfOther = otherAncestors.lastObject;;
1649 /* Returns whether or not the window was ordered in, which depends on if
1650 its frame intersects any screen. */
1651 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1653 WineApplicationController* controller = [WineApplicationController sharedController];
1654 if (![self isMiniaturized])
1656 BOOL needAdjustWindowLevels = FALSE;
1661 [controller transformProcessToForeground];
1662 if ([NSApp isHidden])
1664 wasVisible = [self isVisible];
1667 [NSApp activateIgnoringOtherApps:YES];
1669 NSDisableScreenUpdates();
1671 if ([self becameEligibleParentOrChild])
1672 needAdjustWindowLevels = TRUE;
1676 WineWindow* other = [prev isVisible] ? prev : next;
1677 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1679 if (![self isOrdered:orderingMode relativeTo:other])
1681 WineWindow* ancestor;
1682 WineWindow* ancestorOfOther;
1684 [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1687 [self setAutodisplay:YES];
1688 if (ancestorOfOther)
1690 // This window level may not be right for this window based
1691 // on floating-ness, fullscreen-ness, etc. But we set it
1692 // temporarily to allow us to order the windows properly.
1693 // Then the levels get fixed by -adjustWindowLevels.
1694 if ([ancestor level] != [ancestorOfOther level])
1695 [ancestor setLevel:[ancestorOfOther level]];
1697 parent = (WineWindow*)ancestor.parentWindow;
1698 if ([parent isKindOfClass:[WineWindow class]])
1699 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1701 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1704 if (!ancestorOfOther || ancestor != self)
1707 (parent = (WineWindow*)child.parentWindow);
1710 if ([parent isKindOfClass:[WineWindow class]])
1711 [parent order:-orderingMode childWindow:child relativeTo:nil];
1712 if (parent == ancestor)
1717 [self checkWineDisplayLink];
1718 needAdjustWindowLevels = TRUE;
1725 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1728 [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1731 // Again, temporarily set level to make sure we can order to
1733 next = [controller frontWineWindow];
1734 if (next && [self level] < [next level])
1735 [self setLevel:[next level]];
1736 [self setAutodisplay:YES];
1737 [self orderFront:nil];
1738 [self checkWineDisplayLink];
1739 needAdjustWindowLevels = TRUE;
1741 pendingOrderOut = FALSE;
1743 if ([self becameEligibleParentOrChild])
1744 needAdjustWindowLevels = TRUE;
1746 if (needAdjustWindowLevels)
1748 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1749 [controller updateFullscreenWindows];
1750 [controller adjustWindowLevels];
1753 if (pendingMinimize)
1755 [super miniaturize:nil];
1756 pendingMinimize = FALSE;
1759 NSEnableScreenUpdates();
1761 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1762 Generate a frame-changed event just in case. The back end will ignore
1763 it if nothing actually changed. */
1764 [self windowDidResize:nil];
1766 if (![self isExcludedFromWindowsMenu])
1767 [NSApp addWindowsItem:self title:[self title] filename:NO];
1773 WineApplicationController* controller = [WineApplicationController sharedController];
1774 BOOL wasVisible = [self isVisible];
1775 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1777 [self endWindowDragging];
1778 [controller windowWillOrderOut:self];
1780 if (enteringFullScreen || exitingFullScreen)
1782 pendingOrderOut = TRUE;
1783 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1784 event_mask_for_type(WINDOW_GOT_FOCUS) |
1785 event_mask_for_type(WINDOW_LOST_FOCUS) |
1786 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1787 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1788 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1793 pendingOrderOut = FALSE;
1795 if ([self isMiniaturized])
1796 pendingMinimize = TRUE;
1798 WineWindow* parent = (WineWindow*)self.parentWindow;
1799 if ([parent isKindOfClass:[WineWindow class]])
1800 [parent grabDockIconSnapshotFromWindow:self force:NO];
1802 [self becameIneligibleParentOrChild];
1803 if ([self isMiniaturized] || [self styleMask] & NSFullScreenWindowMask)
1807 fakingClose = FALSE;
1810 [self orderOut:nil];
1811 [self checkWineDisplayLink];
1812 [self setBackgroundColor:[NSColor clearColor]];
1813 [self setOpaque:NO];
1814 drawnSinceShown = NO;
1815 savedVisibleState = FALSE;
1816 if (wasVisible && wasOnActiveSpace && fullscreen)
1817 [controller updateFullscreenWindows];
1818 [controller adjustWindowLevels];
1819 [NSApp removeWindowsItem:self];
1821 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1822 event_mask_for_type(WINDOW_GOT_FOCUS) |
1823 event_mask_for_type(WINDOW_LOST_FOCUS) |
1824 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1825 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1826 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1830 - (void) updateFullscreen
1832 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1833 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1835 if (nowFullscreen != fullscreen)
1837 WineApplicationController* controller = [WineApplicationController sharedController];
1839 fullscreen = nowFullscreen;
1840 if ([self isVisible] && [self isOnActiveSpace])
1841 [controller updateFullscreenWindows];
1843 [controller adjustWindowLevels];
1847 - (void) setFrameAndWineFrame:(NSRect)frame
1849 [self setFrame:frame display:YES];
1852 roundedWineFrame = self.frame;
1854 #if CGFLOAT_IS_DOUBLE
1855 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1856 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1857 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1858 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1859 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1860 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1861 roundedWineFrame = wineFrame;
1863 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1864 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1865 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1866 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1867 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1868 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1869 roundedWineFrame = wineFrame;
1873 - (void) setFrameFromWine:(NSRect)contentRect
1875 /* Origin is (left, top) in a top-down space. Need to convert it to
1876 (left, bottom) in a bottom-up space. */
1877 [[WineApplicationController sharedController] flipRect:&contentRect];
1879 /* The back end is establishing a new window size and position. It's
1880 not interested in any stale events regarding those that may be sitting
1882 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1885 if (!NSIsEmptyRect(contentRect))
1887 NSRect frame, oldFrame;
1889 oldFrame = self.wine_fractionalFrame;
1890 frame = [self frameRectForContentRect:contentRect];
1891 if (!NSEqualRects(frame, oldFrame))
1893 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1894 BOOL needEnableScreenUpdates = FALSE;
1896 if ([self preventResizing])
1898 // Allow the following calls to -setFrame:display: to work even
1899 // if they would violate the content size constraints. This
1900 // shouldn't be necessary since the content size constraints are
1901 // documented to not constrain that method, but it seems to be.
1902 [self setContentMinSize:NSZeroSize];
1903 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1906 if (equalSizes && [[self childWineWindows] count])
1908 // If we change the window frame such that the origin moves
1909 // but the size doesn't change, then Cocoa moves child
1910 // windows with the parent. We don't want that so we fake
1911 // a change of the size and then change it back.
1912 NSRect bogusFrame = frame;
1913 bogusFrame.size.width++;
1915 NSDisableScreenUpdates();
1916 needEnableScreenUpdates = TRUE;
1918 ignore_windowResize = TRUE;
1919 [self setFrame:bogusFrame display:NO];
1920 ignore_windowResize = FALSE;
1923 [self setFrameAndWineFrame:frame];
1924 if ([self preventResizing])
1926 [self setContentMinSize:contentRect.size];
1927 [self setContentMaxSize:contentRect.size];
1930 if (needEnableScreenUpdates)
1931 NSEnableScreenUpdates();
1933 if (!enteringFullScreen &&
1934 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1935 nonFullscreenFrame = frame;
1937 [self updateFullscreen];
1939 if ([self isOrderedIn])
1941 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1942 event. The back end will ignore it if nothing actually changed. */
1943 [self windowDidResize:nil];
1949 - (NSRect) wine_fractionalFrame
1951 NSRect frame = self.frame;
1952 if (NSEqualRects(frame, roundedWineFrame))
1957 - (void) setMacDrvParentWindow:(WineWindow*)parent
1959 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1960 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1962 [oldParent removeChildWineWindow:self];
1963 [latentParentWindow removeChildWineWindow:self];
1964 if ([parent addChildWineWindow:self])
1965 [[WineApplicationController sharedController] adjustWindowLevels];
1966 [self adjustFullScreenBehavior:[self collectionBehavior]];
1970 - (void) setDisabled:(BOOL)newValue
1972 if (disabled != newValue)
1974 disabled = newValue;
1975 [self adjustFeaturesForState];
1979 - (BOOL) needsTransparency
1981 return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1982 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1985 - (void) checkTransparency
1987 if (![self isOpaque] && !self.needsTransparency)
1989 self.shapeChangedSinceLastDraw = TRUE;
1990 [[self contentView] setNeedsDisplay:YES];
1991 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1992 [self setOpaque:YES];
1994 else if ([self isOpaque] && self.needsTransparency)
1996 self.shapeChangedSinceLastDraw = TRUE;
1997 [[self contentView] setNeedsDisplay:YES];
1998 [self setBackgroundColor:[NSColor clearColor]];
1999 [self setOpaque:NO];
2003 - (void) setShape:(NSBezierPath*)newShape
2005 if (shape == newShape) return;
2009 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
2013 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
2015 shape = [newShape copy];
2016 self.shapeChangedSinceLastDraw = TRUE;
2018 [self checkTransparency];
2021 - (void) makeFocused:(BOOL)activate
2025 [[WineApplicationController sharedController] transformProcessToForeground];
2026 [NSApp activateIgnoringOtherApps:YES];
2029 causing_becomeKeyWindow = self;
2030 [self makeKeyWindow];
2031 causing_becomeKeyWindow = nil;
2033 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2034 event_mask_for_type(WINDOW_LOST_FOCUS)
2038 - (void) postKey:(uint16_t)keyCode
2039 pressed:(BOOL)pressed
2040 modifiers:(NSUInteger)modifiers
2041 event:(NSEvent*)theEvent
2043 macdrv_event* event;
2045 WineApplicationController* controller = [WineApplicationController sharedController];
2047 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2048 event->key.keycode = keyCode;
2049 event->key.modifiers = modifiers;
2050 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
2052 if ((cgevent = [theEvent CGEvent]))
2053 controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2055 [queue postEvent:event];
2057 macdrv_release_event(event);
2059 [controller noteKey:keyCode pressed:pressed];
2062 - (void) postKeyEvent:(NSEvent *)theEvent
2064 [self flagsChanged:theEvent];
2065 [self postKey:[theEvent keyCode]
2066 pressed:[theEvent type] == NSKeyDown
2067 modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2071 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2073 savedContentMinSize = minSize;
2074 savedContentMaxSize = maxSize;
2075 if (![self preventResizing])
2077 [self setContentMinSize:minSize];
2078 [self setContentMaxSize:maxSize];
2082 - (WineWindow*) ancestorWineWindow
2084 WineWindow* ancestor = self;
2087 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2088 if ([parent isKindOfClass:[WineWindow class]])
2096 - (void) postBroughtForwardEvent
2098 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2099 [queue postEvent:event];
2100 macdrv_release_event(event);
2103 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing
2105 macdrv_event* event;
2106 NSUInteger style = self.styleMask;
2109 style |= NSFullScreenWindowMask;
2111 style &= ~NSFullScreenWindowMask;
2112 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2113 [[WineApplicationController sharedController] flipRect:&frame];
2115 /* Coalesce events by discarding any previous ones still in the queue. */
2116 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2119 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2120 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2121 event->window_frame_changed.fullscreen = isFullscreen;
2122 event->window_frame_changed.in_resize = resizing;
2123 [queue postEvent:event];
2124 macdrv_release_event(event);
2127 - (void) updateForCursorClipping
2129 [self adjustFeaturesForState];
2132 - (void) endWindowDragging
2136 if (draggingPhase == 3)
2138 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2139 [queue postEvent:event];
2140 macdrv_release_event(event);
2144 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2148 - (NSMutableDictionary*) displayIDToDisplayLinkMap
2150 static NSMutableDictionary* displayIDToDisplayLinkMap;
2151 if (!displayIDToDisplayLinkMap)
2153 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2155 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2158 usingBlock:^(NSNotification *note){
2159 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2160 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2161 [badDisplayIDs minusSet:validDisplayIDs];
2162 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2165 return displayIDToDisplayLinkMap;
2168 - (WineDisplayLink*) wineDisplayLink
2170 if (!_lastDisplayID)
2173 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2174 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2177 - (void) checkWineDisplayLink
2179 NSScreen* screen = self.screen;
2180 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2182 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2183 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2187 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2188 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2189 if (displayID == _lastDisplayID)
2192 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2196 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2197 [link removeWindow:self];
2201 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2204 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2205 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2207 [link addWindow:self];
2208 [self displayIfNeeded];
2210 _lastDisplayID = displayID;
2213 - (BOOL) isEmptyShaped
2215 return (self.shapeData.length == sizeof(CGRectZero) && !memcmp(self.shapeData.bytes, &CGRectZero, sizeof(CGRectZero)));
2218 - (BOOL) canProvideSnapshot
2220 return (self.windowNumber > 0 && ![self isEmptyShaped]);
2223 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2225 if (![self isEmptyShaped])
2228 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2229 if (!force && now < lastDockIconSnapshot + 1)
2234 if (![window canProvideSnapshot])
2240 for (WineWindow* childWindow in self.childWindows)
2242 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2245 NSSize size = childWindow.frame.size;
2246 CGFloat area = size.width * size.height;
2247 if (!window || area > bestArea)
2249 window = childWindow;
2258 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2259 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2260 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2261 CFRelease(windowIDs);
2265 NSImage* appImage = [NSApp applicationIconImage];
2267 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2269 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2270 [dockIcon lockFocus];
2272 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2274 CGRect rect = CGRectMake(8, 8, 240, 240);
2275 size_t width = CGImageGetWidth(windowImage);
2276 size_t height = CGImageGetHeight(windowImage);
2279 rect.size.height *= height / (double)width;
2280 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2282 else if (width != height)
2284 rect.size.width *= width / (double)height;
2285 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2288 CGContextDrawImage(cgcontext, rect, windowImage);
2289 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2291 operation:NSCompositeSourceOver
2296 [dockIcon unlockFocus];
2298 CGImageRelease(windowImage);
2300 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2301 if (![imageView isKindOfClass:[NSImageView class]])
2303 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2304 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2305 self.dockTile.contentView = imageView;
2307 imageView.image = dockIcon;
2308 [self.dockTile display];
2309 lastDockIconSnapshot = now;
2312 - (void) checkEmptyShaped
2314 if (self.dockTile.contentView && ![self isEmptyShaped])
2316 self.dockTile.contentView = nil;
2317 lastDockIconSnapshot = 0;
2319 [self checkWineDisplayLink];
2324 * ---------- NSWindow method overrides ----------
2326 - (BOOL) canBecomeKeyWindow
2328 if (causing_becomeKeyWindow == self) return YES;
2329 if (self.disabled || self.noActivate) return NO;
2330 return [self isKeyWindow];
2333 - (BOOL) canBecomeMainWindow
2335 return [self canBecomeKeyWindow];
2338 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2340 // If a window is sized to completely cover a screen, then it's in
2341 // full-screen mode. In that case, we don't allow NSWindow to constrain
2343 NSArray* screens = [NSScreen screens];
2344 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2345 if (!screen_covered_by_rect(contentRect, screens) &&
2346 frame_intersects_screens(frameRect, screens))
2347 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2351 // This private method of NSWindow is called as Cocoa reacts to the display
2352 // configuration changing. Among other things, it adjusts the window's
2353 // frame based on how the screen(s) changed size. That tells Wine that the
2354 // window has been moved. We don't want that. Rather, we want to make
2355 // sure that the WinAPI notion of the window position is maintained/
2356 // restored, possibly undoing or overriding Cocoa's adjustment.
2358 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2359 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2360 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2361 // reassert its notion of the position. That call won't get processed
2362 // until after this method returns, so it will override whatever this
2363 // method does to the window position. It will also discard any pending
2364 // WINDOW_FRAME_CHANGED events.
2366 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2367 // adjust the window's position due to a display change is to hook into
2368 // this private method. This private method has remained stable from 10.6
2369 // through 10.11. If it does change, the most likely thing is that it
2370 // will be removed and no longer called and this fix will simply stop
2371 // working. The only real danger would be if Apple changed the return type
2372 // to a struct or floating-point type, which would change the calling
2374 - (id) _displayChanged
2376 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2377 [queue postEvent:event];
2378 macdrv_release_event(event);
2380 return [super _displayChanged];
2383 - (BOOL) isExcludedFromWindowsMenu
2385 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2388 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2390 BOOL ret = [super validateMenuItem:menuItem];
2392 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2393 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2394 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2400 /* We don't call this. It's the action method of the items in the Window menu. */
2401 - (void) makeKeyAndOrderFront:(id)sender
2403 if ([self isMiniaturized])
2404 [self deminiaturize:nil];
2405 [self orderBelow:nil orAbove:nil activate:NO];
2406 [[self ancestorWineWindow] postBroughtForwardEvent];
2408 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2409 [[WineApplicationController sharedController] windowGotFocus:self];
2412 - (void) sendEvent:(NSEvent*)event
2414 NSEventType type = event.type;
2416 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2417 interface control. For example, Control-Tab switches focus among
2418 views. We want to bypass that feature, so directly route key-down
2419 events to -keyDown:. */
2420 if (type == NSKeyDown)
2421 [[self firstResponder] keyDown:event];
2424 if (!draggingPhase && maximized && ![self isMovable] &&
2425 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2426 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
2428 NSRect titleBar = self.frame;
2429 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2430 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2431 titleBar.origin.y = NSMaxY(contentRect);
2433 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2435 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2437 static const NSWindowButton buttons[] = {
2438 NSWindowCloseButton,
2439 NSWindowMiniaturizeButton,
2441 NSWindowFullScreenButton,
2443 BOOL hitButton = NO;
2446 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2450 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
2453 button = [self standardWindowButton:buttons[i]];
2454 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2464 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2465 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2469 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
2471 if ([self isMovable])
2473 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2474 NSPoint newTopLeft = dragWindowStartPosition;
2476 newTopLeft.x += point.x - dragStartPosition.x;
2477 newTopLeft.y += point.y - dragStartPosition.y;
2479 if (draggingPhase == 2)
2481 macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2482 mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2483 [queue postEvent:mevent];
2484 macdrv_release_event(mevent);
2489 [self setFrameTopLeftPoint:newTopLeft];
2491 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
2493 macdrv_event* event;
2494 NSRect frame = [self contentRectForFrameRect:self.frame];
2496 [[WineApplicationController sharedController] flipRect:&frame];
2498 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2499 event->window_restore_requested.keep_frame = TRUE;
2500 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2501 [queue postEvent:event];
2502 macdrv_release_event(event);
2507 if (type == NSLeftMouseUp)
2508 [self endWindowDragging];
2511 [super sendEvent:event];
2515 - (void) miniaturize:(id)sender
2517 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2518 [queue postEvent:event];
2519 macdrv_release_event(event);
2521 WineWindow* parent = (WineWindow*)self.parentWindow;
2522 if ([parent isKindOfClass:[WineWindow class]])
2523 [parent grabDockIconSnapshotFromWindow:self force:YES];
2526 - (void) toggleFullScreen:(id)sender
2528 if (!self.disabled && !maximized)
2529 [super toggleFullScreen:sender];
2532 - (void) setViewsNeedDisplay:(BOOL)value
2534 if (value && ![self viewsNeedDisplay])
2536 WineDisplayLink* link = [self wineDisplayLink];
2539 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2540 if (_lastDisplayTime + [link refreshPeriod] < now)
2541 [self setAutodisplay:YES];
2545 _lastDisplayTime = now;
2549 [self setAutodisplay:YES];
2551 [super setViewsNeedDisplay:value];
2556 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2559 [self setAutodisplay:NO];
2562 - (void) displayIfNeeded
2564 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2565 [super displayIfNeeded];
2567 [self setAutodisplay:NO];
2570 - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2573 [self setAutodisplay:YES];
2574 [super setFrame:frameRect display:flag];
2577 - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2580 [self setAutodisplay:YES];
2581 [super setFrame:frameRect display:displayFlag animate:animateFlag];
2584 - (void) windowDidDrawContent
2586 if (!drawnSinceShown)
2588 drawnSinceShown = YES;
2589 dispatch_async(dispatch_get_main_queue(), ^{
2590 [self checkTransparency];
2595 - (NSArray*) childWineWindows
2597 NSArray* childWindows = self.childWindows;
2598 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2599 return [child isKindOfClass:[WineWindow class]];
2601 return [childWindows objectsAtIndexes:indexes];
2604 - (void) updateForGLSubviews
2606 if (gl_surface_mode == GL_SURFACE_BEHIND)
2607 [self checkTransparency];
2610 - (void) setRetinaMode:(int)mode
2613 double scale = mode ? 0.5 : 2.0;
2614 NSAffineTransform* transform = [NSAffineTransform transform];
2616 [transform scaleBy:scale];
2619 [shape transformUsingAffineTransform:transform];
2621 for (WineBaseView* subview in [self.contentView subviews])
2623 if ([subview isKindOfClass:[WineBaseView class]])
2624 [subview setRetinaMode:mode];
2627 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2628 frame.origin.x *= scale;
2629 frame.origin.y *= scale;
2630 frame.size.width *= scale;
2631 frame.size.height *= scale;
2632 frame = [self frameRectForContentRect:frame];
2634 savedContentMinSize = [transform transformSize:savedContentMinSize];
2635 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2636 savedContentMaxSize.width *= scale;
2637 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2638 savedContentMaxSize.height *= scale;
2640 self.contentMinSize = [transform transformSize:self.contentMinSize];
2641 NSSize temp = self.contentMaxSize;
2642 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2643 temp.width *= scale;
2644 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2645 temp.height *= scale;
2646 self.contentMaxSize = temp;
2648 ignore_windowResize = TRUE;
2649 [self setFrameAndWineFrame:frame];
2650 ignore_windowResize = FALSE;
2655 * ---------- NSResponder method overrides ----------
2657 - (void) keyDown:(NSEvent *)theEvent
2659 if ([theEvent isARepeat])
2661 if (!allowKeyRepeats)
2665 allowKeyRepeats = YES;
2667 [self postKeyEvent:theEvent];
2670 - (void) flagsChanged:(NSEvent *)theEvent
2672 static const struct {
2676 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2677 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2678 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2679 { NX_DEVICELCTLKEYMASK, kVK_Control },
2680 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2681 { NX_DEVICELALTKEYMASK, kVK_Option },
2682 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2683 { NX_DEVICELCMDKEYMASK, kVK_Command },
2684 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2687 NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2689 int i, last_changed;
2691 fix_device_modifiers_by_generic(&modifierFlags);
2692 changed = modifierFlags ^ lastModifierFlags;
2695 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2696 if (changed & modifiers[i].mask)
2699 for (i = 0; i <= last_changed; i++)
2701 if (changed & modifiers[i].mask)
2703 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2706 allowKeyRepeats = NO;
2708 if (i == last_changed)
2709 lastModifierFlags = modifierFlags;
2712 lastModifierFlags ^= modifiers[i].mask;
2713 fix_generic_modifiers_by_device(&lastModifierFlags);
2716 // Caps lock generates one event for each press-release action.
2717 // We need to simulate a pair of events for each actual event.
2718 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2720 [self postKey:modifiers[i].keycode
2722 modifiers:lastModifierFlags
2723 event:(NSEvent*)theEvent];
2727 [self postKey:modifiers[i].keycode
2729 modifiers:lastModifierFlags
2730 event:(NSEvent*)theEvent];
2735 - (void) applicationWillHide
2737 savedVisibleState = [self isVisible];
2740 - (void) applicationDidUnhide
2742 if ([self isVisible])
2743 [self becameEligibleParentOrChild];
2748 * ---------- NSWindowDelegate methods ----------
2750 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2752 macdrv_query* query;
2755 query = macdrv_create_query();
2756 query->type = QUERY_MIN_MAX_INFO;
2757 query->window = (macdrv_window)[self retain];
2758 [self.queue query:query timeout:0.5];
2759 macdrv_release_query(query);
2761 size = [self contentMaxSize];
2762 if (proposedSize.width < size.width)
2763 size.width = proposedSize.width;
2764 if (proposedSize.height < size.height)
2765 size.height = proposedSize.height;
2769 - (void)windowDidBecomeKey:(NSNotification *)notification
2771 WineApplicationController* controller = [WineApplicationController sharedController];
2772 NSEvent* event = [controller lastFlagsChanged];
2774 [self flagsChanged:event];
2776 if (causing_becomeKeyWindow == self) return;
2778 [controller windowGotFocus:self];
2781 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2783 [self checkWineDisplayLink];
2786 - (void) windowDidChangeScreen:(NSNotification*)notification
2788 [self checkWineDisplayLink];
2791 - (void)windowDidDeminiaturize:(NSNotification *)notification
2793 WineApplicationController* controller = [WineApplicationController sharedController];
2795 if (!ignore_windowDeminiaturize)
2796 [self postDidUnminimizeEvent];
2797 ignore_windowDeminiaturize = FALSE;
2799 [self becameEligibleParentOrChild];
2801 if (fullscreen && [self isOnActiveSpace])
2802 [controller updateFullscreenWindows];
2803 [controller adjustWindowLevels];
2805 if (![self parentWindow])
2806 [self postBroughtForwardEvent];
2808 if (!self.disabled && !self.noActivate)
2810 causing_becomeKeyWindow = self;
2811 [self makeKeyWindow];
2812 causing_becomeKeyWindow = nil;
2813 [controller windowGotFocus:self];
2816 [self windowDidResize:notification];
2817 [self checkWineDisplayLink];
2820 - (void) windowDidEndLiveResize:(NSNotification *)notification
2824 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2825 [queue postEvent:event];
2826 macdrv_release_event(event);
2830 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2832 enteringFullScreen = FALSE;
2833 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2834 if (pendingOrderOut)
2838 - (void) windowDidExitFullScreen:(NSNotification*)notification
2840 exitingFullScreen = FALSE;
2841 [self setFrameAndWineFrame:nonFullscreenFrame];
2842 [self windowDidResize:nil];
2843 if (pendingOrderOut)
2847 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2849 enteringFullScreen = FALSE;
2850 enteredFullScreenTime = 0;
2851 if (pendingOrderOut)
2855 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2857 exitingFullScreen = FALSE;
2858 [self windowDidResize:nil];
2859 if (pendingOrderOut)
2863 - (void)windowDidMiniaturize:(NSNotification *)notification
2865 if (fullscreen && [self isOnActiveSpace])
2866 [[WineApplicationController sharedController] updateFullscreenWindows];
2867 [self checkWineDisplayLink];
2870 - (void)windowDidMove:(NSNotification *)notification
2872 [self windowDidResize:notification];
2875 - (void)windowDidResignKey:(NSNotification *)notification
2877 macdrv_event* event;
2879 if (causing_becomeKeyWindow) return;
2881 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2882 [queue postEvent:event];
2883 macdrv_release_event(event);
2886 - (void)windowDidResize:(NSNotification *)notification
2888 NSRect frame = self.wine_fractionalFrame;
2890 if ([self inLiveResize])
2892 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2893 resizingFromLeft = TRUE;
2894 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2895 resizingFromTop = TRUE;
2898 if (ignore_windowResize || exitingFullScreen) return;
2900 if ([self preventResizing])
2902 NSRect contentRect = [self contentRectForFrameRect:frame];
2903 [self setContentMinSize:contentRect.size];
2904 [self setContentMaxSize:contentRect.size];
2907 [self postWindowFrameChanged:frame
2908 fullscreen:([self styleMask] & NSFullScreenWindowMask) != 0
2909 resizing:[self inLiveResize]];
2911 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2912 [self updateFullscreen];
2915 - (BOOL)windowShouldClose:(id)sender
2917 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2918 [queue postEvent:event];
2919 macdrv_release_event(event);
2923 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2927 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2928 [queue postEvent:event];
2929 macdrv_release_event(event);
2932 else if (!resizable)
2934 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2935 [queue postEvent:event];
2936 macdrv_release_event(event);
2943 - (void) windowWillClose:(NSNotification*)notification
2947 if (fakingClose) return;
2948 if (latentParentWindow)
2950 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2951 self.latentParentWindow = nil;
2954 for (child in latentChildWindows)
2956 if (child.latentParentWindow == self)
2957 child.latentParentWindow = nil;
2959 [latentChildWindows removeAllObjects];
2962 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2964 enteringFullScreen = TRUE;
2965 nonFullscreenFrame = self.wine_fractionalFrame;
2968 - (void) windowWillExitFullScreen:(NSNotification*)notification
2970 exitingFullScreen = TRUE;
2971 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE];
2974 - (void)windowWillMiniaturize:(NSNotification *)notification
2976 [self becameIneligibleParentOrChild];
2977 [self grabDockIconSnapshotFromWindow:nil force:NO];
2980 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2982 if ([self inLiveResize])
2985 return self.wine_fractionalFrame.size;
2988 macdrv_query* query;
2990 rect = [self frame];
2991 if (resizingFromLeft)
2992 rect.origin.x = NSMaxX(rect) - frameSize.width;
2993 if (!resizingFromTop)
2994 rect.origin.y = NSMaxY(rect) - frameSize.height;
2995 rect.size = frameSize;
2996 rect = [self contentRectForFrameRect:rect];
2997 [[WineApplicationController sharedController] flipRect:&rect];
2999 query = macdrv_create_query();
3000 query->type = QUERY_RESIZE_SIZE;
3001 query->window = (macdrv_window)[self retain];
3002 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3003 query->resize_size.from_left = resizingFromLeft;
3004 query->resize_size.from_top = resizingFromTop;
3006 if ([self.queue query:query timeout:0.1])
3008 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3009 rect = [self frameRectForContentRect:rect];
3010 frameSize = rect.size;
3013 macdrv_release_query(query);
3019 - (void) windowWillStartLiveResize:(NSNotification *)notification
3021 [self endWindowDragging];
3025 macdrv_event* event;
3026 NSRect frame = [self contentRectForFrameRect:self.frame];
3028 [[WineApplicationController sharedController] flipRect:&frame];
3030 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3031 event->window_restore_requested.keep_frame = TRUE;
3032 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3033 [queue postEvent:event];
3034 macdrv_release_event(event);
3037 [self sendResizeStartQuery];
3039 frameAtResizeStart = [self frame];
3040 resizingFromLeft = resizingFromTop = FALSE;
3043 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3045 macdrv_query* query;
3046 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3049 query = macdrv_create_query();
3050 query->type = QUERY_MIN_MAX_INFO;
3051 query->window = (macdrv_window)[self retain];
3052 [self.queue query:query timeout:0.5];
3053 macdrv_release_query(query);
3055 currentContentRect = [self contentRectForFrameRect:[self frame]];
3056 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3058 maxSize = [self contentMaxSize];
3059 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3060 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3062 // Try to keep the top-left corner where it is.
3063 newContentRect.origin.x = NSMinX(currentContentRect);
3064 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3066 // If that pushes the bottom or right off the screen, pull it up and to the left.
3067 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3068 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3069 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3070 if (NSMinY(newContentRect) < NSMinY(screenRect))
3071 newContentRect.origin.y = NSMinY(screenRect);
3073 // If that pushes the top or left off the screen, push it down and the right
3074 // again. Do this last because the top-left corner is more important than the
3076 if (NSMinX(newContentRect) < NSMinX(screenRect))
3077 newContentRect.origin.x = NSMinX(screenRect);
3078 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3079 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3081 return [self frameRectForContentRect:newContentRect];
3086 * ---------- NSPasteboardOwner methods ----------
3088 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3090 macdrv_query* query = macdrv_create_query();
3091 query->type = QUERY_PASTEBOARD_DATA;
3092 query->window = (macdrv_window)[self retain];
3093 query->pasteboard_data.type = (CFStringRef)[type copy];
3095 [self.queue query:query timeout:3];
3096 macdrv_release_query(query);
3099 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3101 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3102 [queue postEvent:event];
3103 macdrv_release_event(event);
3108 * ---------- NSDraggingDestination methods ----------
3110 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3112 return [self draggingUpdated:sender];
3115 - (void) draggingExited:(id <NSDraggingInfo>)sender
3117 // This isn't really a query. We don't need any response. However, it
3118 // has to be processed in a similar manner as the other drag-and-drop
3119 // queries in order to maintain the proper order of operations.
3120 macdrv_query* query = macdrv_create_query();
3121 query->type = QUERY_DRAG_EXITED;
3122 query->window = (macdrv_window)[self retain];
3124 [self.queue query:query timeout:0.1];
3125 macdrv_release_query(query);
3128 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3130 NSDragOperation ret;
3131 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3132 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3133 NSPasteboard* pb = [sender draggingPasteboard];
3135 macdrv_query* query = macdrv_create_query();
3136 query->type = QUERY_DRAG_OPERATION;
3137 query->window = (macdrv_window)[self retain];
3138 query->drag_operation.x = floor(cgpt.x);
3139 query->drag_operation.y = floor(cgpt.y);
3140 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3141 query->drag_operation.accepted_op = NSDragOperationNone;
3142 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3144 [self.queue query:query timeout:3];
3145 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3146 macdrv_release_query(query);
3151 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3154 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3155 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3156 NSPasteboard* pb = [sender draggingPasteboard];
3158 macdrv_query* query = macdrv_create_query();
3159 query->type = QUERY_DRAG_DROP;
3160 query->window = (macdrv_window)[self retain];
3161 query->drag_drop.x = floor(cgpt.x);
3162 query->drag_drop.y = floor(cgpt.y);
3163 query->drag_drop.op = [sender draggingSourceOperationMask];
3164 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3166 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3167 ret = query->status;
3168 macdrv_release_query(query);
3173 - (BOOL) wantsPeriodicDraggingUpdates
3181 /***********************************************************************
3182 * macdrv_create_cocoa_window
3184 * Create a Cocoa window with the given content frame and features (e.g.
3185 * title bar, close box, etc.).
3187 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3188 CGRect frame, void* hwnd, macdrv_event_queue queue)
3190 __block WineWindow* window;
3193 window = [[WineWindow createWindowWithFeatures:wf
3194 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3196 queue:(WineEventQueue*)queue] retain];
3199 return (macdrv_window)window;
3202 /***********************************************************************
3203 * macdrv_destroy_cocoa_window
3205 * Destroy a Cocoa window.
3207 void macdrv_destroy_cocoa_window(macdrv_window w)
3209 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3210 WineWindow* window = (WineWindow*)w;
3213 [window doOrderOut];
3216 [window.queue discardEventsMatchingMask:-1 forWindow:window];
3222 /***********************************************************************
3223 * macdrv_get_window_hwnd
3225 * Get the hwnd that was set for the window at creation.
3227 void* macdrv_get_window_hwnd(macdrv_window w)
3229 WineWindow* window = (WineWindow*)w;
3233 /***********************************************************************
3234 * macdrv_set_cocoa_window_features
3236 * Update a Cocoa window's features.
3238 void macdrv_set_cocoa_window_features(macdrv_window w,
3239 const struct macdrv_window_features* wf)
3241 WineWindow* window = (WineWindow*)w;
3244 [window setWindowFeatures:wf];
3248 /***********************************************************************
3249 * macdrv_set_cocoa_window_state
3251 * Update a Cocoa window's state.
3253 void macdrv_set_cocoa_window_state(macdrv_window w,
3254 const struct macdrv_window_state* state)
3256 WineWindow* window = (WineWindow*)w;
3259 [window setMacDrvState:state];
3263 /***********************************************************************
3264 * macdrv_set_cocoa_window_title
3266 * Set a Cocoa window's title.
3268 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3271 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3272 WineWindow* window = (WineWindow*)w;
3273 NSString* titleString;
3276 titleString = [NSString stringWithCharacters:title length:length];
3279 OnMainThreadAsync(^{
3280 [window setTitle:titleString];
3281 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3282 [NSApp changeWindowsItem:window title:titleString filename:NO];
3288 /***********************************************************************
3289 * macdrv_order_cocoa_window
3291 * Reorder a Cocoa window relative to other windows. If prev is
3292 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3293 * it is ordered above that window. Otherwise, it is ordered to the
3296 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3297 macdrv_window n, int activate)
3299 WineWindow* window = (WineWindow*)w;
3300 WineWindow* prev = (WineWindow*)p;
3301 WineWindow* next = (WineWindow*)n;
3303 OnMainThreadAsync(^{
3304 [window orderBelow:prev
3308 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3310 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3314 /***********************************************************************
3315 * macdrv_hide_cocoa_window
3317 * Hides a Cocoa window.
3319 void macdrv_hide_cocoa_window(macdrv_window w)
3321 WineWindow* window = (WineWindow*)w;
3324 [window doOrderOut];
3328 /***********************************************************************
3329 * macdrv_set_cocoa_window_frame
3331 * Move a Cocoa window.
3333 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3335 WineWindow* window = (WineWindow*)w;
3338 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3342 /***********************************************************************
3343 * macdrv_get_cocoa_window_frame
3345 * Gets the frame of a Cocoa window.
3347 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3349 WineWindow* window = (WineWindow*)w;
3354 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3355 [[WineApplicationController sharedController] flipRect:&frame];
3356 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3360 /***********************************************************************
3361 * macdrv_set_cocoa_parent_window
3363 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3364 * the parent window.
3366 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3368 WineWindow* window = (WineWindow*)w;
3371 [window setMacDrvParentWindow:(WineWindow*)parent];
3375 /***********************************************************************
3376 * macdrv_set_window_surface
3378 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3380 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3381 WineWindow* window = (WineWindow*)w;
3384 window.surface = surface;
3385 window.surface_mutex = mutex;
3391 /***********************************************************************
3392 * macdrv_window_needs_display
3394 * Mark a window as needing display in a specified rect (in non-client
3395 * area coordinates).
3397 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3399 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3400 WineWindow* window = (WineWindow*)w;
3402 OnMainThreadAsync(^{
3403 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3409 /***********************************************************************
3410 * macdrv_set_window_shape
3412 * Sets the shape of a Cocoa window from an array of rectangles. If
3413 * rects is NULL, resets the window's shape to its frame.
3415 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3417 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3418 WineWindow* window = (WineWindow*)w;
3421 if (!rects || !count)
3424 window.shapeData = nil;
3425 [window checkEmptyShaped];
3429 size_t length = sizeof(*rects) * count;
3430 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
3435 path = [NSBezierPath bezierPath];
3436 for (i = 0; i < count; i++)
3437 [path appendBezierPathWithRect:NSRectFromCGRect(cgrect_mac_from_win(rects[i]))];
3438 window.shape = path;
3439 window.shapeData = [NSData dataWithBytes:rects length:length];
3440 [window checkEmptyShaped];
3448 /***********************************************************************
3449 * macdrv_set_window_alpha
3451 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3453 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3454 WineWindow* window = (WineWindow*)w;
3456 [window setAlphaValue:alpha];
3461 /***********************************************************************
3462 * macdrv_set_window_color_key
3464 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3467 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3468 WineWindow* window = (WineWindow*)w;
3471 window.colorKeyed = TRUE;
3472 window.colorKeyRed = keyRed;
3473 window.colorKeyGreen = keyGreen;
3474 window.colorKeyBlue = keyBlue;
3475 [window checkTransparency];
3481 /***********************************************************************
3482 * macdrv_clear_window_color_key
3484 void macdrv_clear_window_color_key(macdrv_window w)
3486 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3487 WineWindow* window = (WineWindow*)w;
3490 window.colorKeyed = FALSE;
3491 [window checkTransparency];
3497 /***********************************************************************
3498 * macdrv_window_use_per_pixel_alpha
3500 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3502 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3503 WineWindow* window = (WineWindow*)w;
3506 window.usePerPixelAlpha = use_per_pixel_alpha;
3507 [window checkTransparency];
3513 /***********************************************************************
3514 * macdrv_give_cocoa_window_focus
3516 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3517 * orders it front and, if its frame was not within the desktop bounds,
3518 * Cocoa will typically move it on-screen.
3520 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3522 WineWindow* window = (WineWindow*)w;
3525 [window makeFocused:activate];
3529 /***********************************************************************
3530 * macdrv_set_window_min_max_sizes
3532 * Sets the window's minimum and maximum content sizes.
3534 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3536 WineWindow* window = (WineWindow*)w;
3539 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3543 /***********************************************************************
3544 * macdrv_create_view
3546 * Creates and returns a view with the specified frame rect. The
3547 * caller is responsible for calling macdrv_dispose_view() on the view
3548 * when it is done with it.
3550 macdrv_view macdrv_create_view(CGRect rect)
3552 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3553 __block WineContentView* view;
3555 if (CGRectIsNull(rect)) rect = CGRectZero;
3558 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3560 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3561 [view setAutoresizesSubviews:NO];
3562 [view setAutoresizingMask:NSViewNotSizable];
3563 [view setHidden:YES];
3564 [nc addObserver:view
3565 selector:@selector(updateGLContexts)
3566 name:NSViewGlobalFrameDidChangeNotification
3568 [nc addObserver:view
3569 selector:@selector(updateGLContexts)
3570 name:NSApplicationDidChangeScreenParametersNotification
3575 return (macdrv_view)view;
3578 /***********************************************************************
3579 * macdrv_dispose_view
3581 * Destroys a view previously returned by macdrv_create_view.
3583 void macdrv_dispose_view(macdrv_view v)
3585 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3586 WineContentView* view = (WineContentView*)v;
3589 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3590 WineWindow* window = (WineWindow*)[view window];
3592 [nc removeObserver:view
3593 name:NSViewGlobalFrameDidChangeNotification
3595 [nc removeObserver:view
3596 name:NSApplicationDidChangeScreenParametersNotification
3598 [view removeFromSuperview];
3600 [window updateForGLSubviews];
3606 /***********************************************************************
3607 * macdrv_set_view_frame
3609 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3611 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3612 WineContentView* view = (WineContentView*)v;
3614 if (CGRectIsNull(rect)) rect = CGRectZero;
3616 OnMainThreadAsync(^{
3617 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3618 NSRect oldFrame = [view frame];
3620 if (!NSEqualRects(oldFrame, newFrame))
3622 [[view superview] setNeedsDisplayInRect:oldFrame];
3623 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3624 [view setFrameSize:newFrame.size];
3625 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3626 [view setFrameOrigin:newFrame.origin];
3628 [view setFrame:newFrame];
3629 [view setNeedsDisplay:YES];
3633 int backing_size[2] = { 0 };
3634 [view wine_setBackingSize:backing_size];
3636 [(WineWindow*)[view window] updateForGLSubviews];
3643 /***********************************************************************
3644 * macdrv_set_view_superview
3646 * Move a view to a new superview and position it relative to its
3647 * siblings. If p is non-NULL, the view is ordered behind it.
3648 * Otherwise, the view is ordered above n. If s is NULL, use the
3649 * content view of w as the new superview.
3651 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3653 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3654 WineContentView* view = (WineContentView*)v;
3655 WineContentView* superview = (WineContentView*)s;
3656 WineWindow* window = (WineWindow*)w;
3657 WineContentView* prev = (WineContentView*)p;
3658 WineContentView* next = (WineContentView*)n;
3661 superview = [window contentView];
3663 OnMainThreadAsync(^{
3664 if (superview == [view superview])
3666 NSArray* subviews = [superview subviews];
3667 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3668 if (!prev && !next && index == [subviews count] - 1)
3670 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3672 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3676 WineWindow* oldWindow = (WineWindow*)[view window];
3677 WineWindow* newWindow = (WineWindow*)[superview window];
3679 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3680 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3681 [view removeFromSuperview];
3684 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3686 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3688 if (oldWindow != newWindow)
3690 [oldWindow updateForGLSubviews];
3691 [newWindow updateForGLSubviews];
3698 /***********************************************************************
3699 * macdrv_set_view_hidden
3701 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3703 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3704 WineContentView* view = (WineContentView*)v;
3706 OnMainThreadAsync(^{
3707 [view setHidden:hidden];
3708 [(WineWindow*)view.window updateForGLSubviews];
3714 /***********************************************************************
3715 * macdrv_add_view_opengl_context
3717 * Add an OpenGL context to the list being tracked for each view.
3719 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3721 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3722 WineContentView* view = (WineContentView*)v;
3723 WineOpenGLContext *context = (WineOpenGLContext*)c;
3726 [view addGLContext:context];
3732 /***********************************************************************
3733 * macdrv_remove_view_opengl_context
3735 * Add an OpenGL context to the list being tracked for each view.
3737 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3739 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3740 WineContentView* view = (WineContentView*)v;
3741 WineOpenGLContext *context = (WineOpenGLContext*)c;
3743 OnMainThreadAsync(^{
3744 [view removeGLContext:context];
3750 #ifdef HAVE_METAL_METAL_H
3751 macdrv_metal_device macdrv_create_metal_device(void)
3753 macdrv_metal_device ret;
3755 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3756 if (MTLCreateSystemDefaultDevice == NULL)
3760 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3761 ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3766 void macdrv_release_metal_device(macdrv_metal_device d)
3768 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3769 [(id<MTLDevice>)d release];
3773 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3775 id<MTLDevice> device = (id<MTLDevice>)d;
3776 WineContentView* view = (WineContentView*)v;
3777 __block WineMetalView *metalView;
3780 metalView = [view newMetalViewWithDevice:device];
3783 return (macdrv_metal_view)metalView;
3786 void macdrv_view_release_metal_view(macdrv_metal_view v)
3788 WineMetalView* view = (WineMetalView*)v;
3790 [view removeFromSuperview];
3796 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3798 WineContentView* view = (WineContentView*)v;
3800 if (![view isKindOfClass:[WineContentView class]])
3803 [view wine_getBackingSize:backing_size];
3807 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3809 WineContentView* view = (WineContentView*)v;
3811 if ([view isKindOfClass:[WineContentView class]])
3812 [view wine_setBackingSize:backing_size];
3815 /***********************************************************************
3816 * macdrv_window_background_color
3818 * Returns the standard Mac window background color as a 32-bit value of
3819 * the form 0x00rrggbb.
3821 uint32_t macdrv_window_background_color(void)
3823 static uint32_t result;
3824 static dispatch_once_t once;
3826 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3827 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3828 // of it is to draw with it.
3829 dispatch_once(&once, ^{
3831 unsigned char rgbx[4];
3832 unsigned char *planes = rgbx;
3833 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3840 colorSpaceName:NSCalibratedRGBColorSpace
3844 [NSGraphicsContext saveGraphicsState];
3845 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3846 [[NSColor windowBackgroundColor] set];
3847 NSRectFill(NSMakeRect(0, 0, 1, 1));
3848 [NSGraphicsContext restoreGraphicsState];
3850 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3857 /***********************************************************************
3858 * macdrv_send_text_input_event
3860 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3862 OnMainThreadAsync(^{
3864 macdrv_event* event;
3865 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3866 if (![window isKindOfClass:[WineWindow class]])
3868 window = (WineWindow*)[NSApp mainWindow];
3869 if (![window isKindOfClass:[WineWindow class]])
3870 window = [[WineApplicationController sharedController] frontWineWindow];
3875 NSUInteger localFlags = flags;
3879 window.imeData = data;
3880 fix_device_modifiers_by_generic(&localFlags);
3882 // An NSEvent created with +keyEventWithType:... is internally marked
3883 // as synthetic and doesn't get sent through input methods. But one
3884 // created from a CGEvent doesn't have that problem.
3885 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3886 CGEventSetFlags(c, localFlags);
3887 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3888 event = [NSEvent eventWithCGEvent:c];
3891 window.commandDone = FALSE;
3892 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3897 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3898 event->sent_text_input.handled = ret;
3899 event->sent_text_input.done = done;
3900 [[window queue] postEvent:event];
3901 macdrv_release_event(event);