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 #define GL_SILENCE_DEPRECATION
24 #import <Carbon/Carbon.h>
25 #import <CoreVideo/CoreVideo.h>
26 #import <Metal/Metal.h>
27 #import <QuartzCore/QuartzCore.h>
29 #import "cocoa_window.h"
31 #include "macdrv_cocoa.h"
33 #import "cocoa_event.h"
34 #import "cocoa_opengl.h"
36 #pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
38 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
39 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
41 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
46 @interface NSWindow (PrivatePreventsActivation)
48 /* Needed to ensure proper behavior after adding or removing
49 * NSWindowStyleMaskNonactivatingPanel.
50 * Available since at least macOS 10.6. */
51 - (void)_setPreventsActivation:(BOOL)flag;
56 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
58 NSUInteger style_mask;
62 style_mask = NSWindowStyleMaskTitled;
63 if (wf->close_button) style_mask |= NSWindowStyleMaskClosable;
64 if (wf->minimize_button) style_mask |= NSWindowStyleMaskMiniaturizable;
65 if (wf->resizable || wf->maximize_button) style_mask |= NSWindowStyleMaskResizable;
66 if (wf->utility) style_mask |= NSWindowStyleMaskUtilityWindow;
68 else style_mask = NSWindowStyleMaskBorderless;
70 if (wf->prevents_app_activation) style_mask |= NSWindowStyleMaskNonactivatingPanel;
76 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
79 for (screen in screens)
81 if (NSIntersectsRect(frame, [screen frame]))
88 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
90 for (NSScreen* screen in screens)
92 if (NSContainsRect(rect, [screen frame]))
99 /* We rely on the supposedly device-dependent modifier flags to distinguish the
100 keys on the left side of the keyboard from those on the right. Some event
101 sources don't set those device-depdendent flags. If we see a device-independent
102 flag for a modifier without either corresponding device-dependent flag, assume
104 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
106 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
107 *modifiers |= NX_DEVICELCMDKEYMASK;
108 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
109 *modifiers |= NX_DEVICELSHIFTKEYMASK;
110 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
111 *modifiers |= NX_DEVICELCTLKEYMASK;
112 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
113 *modifiers |= NX_DEVICELALTKEYMASK;
116 /* As we manipulate individual bits of a modifier mask, we can end up with
117 inconsistent sets of flags. In particular, we might set or clear one of the
118 left/right-specific bits, but not the corresponding non-side-specific bit.
119 Fix that. If either side-specific bit is set, set the non-side-specific bit,
120 otherwise clear it. */
121 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
123 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
124 *modifiers |= NX_COMMANDMASK;
126 *modifiers &= ~NX_COMMANDMASK;
127 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
128 *modifiers |= NX_SHIFTMASK;
130 *modifiers &= ~NX_SHIFTMASK;
131 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
132 *modifiers |= NX_CONTROLMASK;
134 *modifiers &= ~NX_CONTROLMASK;
135 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
136 *modifiers |= NX_ALTERNATEMASK;
138 *modifiers &= ~NX_ALTERNATEMASK;
141 static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
143 fix_device_modifiers_by_generic(&modifiers);
144 NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
145 NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
147 // The MACDRV keyboard driver translates Command keys to Alt. If the
148 // Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
149 // Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
150 if (modifiers & NX_DEVICELALTKEYMASK)
151 new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
152 if (modifiers & NX_DEVICERALTKEYMASK)
153 new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
155 if (modifiers & NX_DEVICELCMDKEYMASK)
156 new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
157 if (modifiers & NX_DEVICERCMDKEYMASK)
158 new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
160 fix_generic_modifiers_by_device(&new_modifiers);
161 return new_modifiers;
164 static inline BOOL stage_manager_enabled(void)
166 /* There is no documented way to determine if Stage Manager is enabled,
167 * but this seems like the best option.
169 if (floor(NSAppKitVersionNumber) >= 2299 /* NSAppKitVersionNumber13_0 */)
171 NSUserDefaults *defs = [[NSUserDefaults alloc] initWithSuiteName:@"com.apple.WindowManager.plist"];
172 BOOL enabled = [defs boolForKey:@"GloballyEnabled"];
180 @interface NSWindow (WineAccessPrivateMethods)
181 - (id) _displayChanged;
185 @interface WineDisplayLink : NSObject
187 CGDirectDisplayID _displayID;
188 CVDisplayLinkRef _link;
189 NSMutableSet* _windows;
191 NSTimeInterval _actualRefreshPeriod;
192 NSTimeInterval _nominalRefreshPeriod;
194 NSTimeInterval _lastDisplayTime;
197 - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
199 - (void) addWindow:(WineWindow*)window;
200 - (void) removeWindow:(WineWindow*)window;
202 - (NSTimeInterval) refreshPeriod;
208 @implementation WineDisplayLink
210 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
212 - (id) initWithDisplayID:(CGDirectDisplayID)displayID
217 CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
218 if (status == kCVReturnSuccess && !_link)
219 status = kCVReturnError;
220 if (status == kCVReturnSuccess)
221 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
222 if (status != kCVReturnSuccess)
228 _displayID = displayID;
229 _windows = [[NSMutableSet alloc] init];
238 CVDisplayLinkStop(_link);
239 CVDisplayLinkRelease(_link);
245 - (void) addWindow:(WineWindow*)window
248 @synchronized(self) {
249 firstWindow = !_windows.count;
250 [_windows addObject:window];
252 if (firstWindow || !CVDisplayLinkIsRunning(_link))
256 - (void) removeWindow:(WineWindow*)window
258 BOOL lastWindow = FALSE;
259 @synchronized(self) {
260 BOOL hadWindows = _windows.count > 0;
261 [_windows removeObject:window];
262 if (hadWindows && !_windows.count)
265 if (lastWindow && CVDisplayLinkIsRunning(_link))
266 CVDisplayLinkStop(_link);
272 @synchronized(self) {
273 windows = [_windows copy];
275 dispatch_async(dispatch_get_main_queue(), ^{
276 BOOL anyDisplayed = FALSE;
277 for (WineWindow* window in windows)
279 if ([window viewsNeedDisplay])
281 [window displayIfNeeded];
286 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
288 _lastDisplayTime = now;
289 else if (_lastDisplayTime + 2.0 < now)
290 CVDisplayLinkStop(_link);
295 - (NSTimeInterval) refreshPeriod
297 if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
298 return _actualRefreshPeriod;
300 if (_nominalRefreshPeriod)
301 return _nominalRefreshPeriod;
303 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
304 if (time.flags & kCVTimeIsIndefinite)
306 _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
307 return _nominalRefreshPeriod;
312 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
313 CVDisplayLinkStart(_link);
316 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
318 WineDisplayLink* link = displayLinkContext;
320 return kCVReturnSuccess;
326 #ifndef MAC_OS_X_VERSION_10_14
327 @protocol NSViewLayerContentScaleDelegate <NSObject>
330 - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window;
336 @interface CAShapeLayer (WineShapeMaskExtensions)
338 @property(readonly, nonatomic, getter=isEmptyShaped) BOOL emptyShaped;
342 @implementation CAShapeLayer (WineShapeMaskExtensions)
344 - (BOOL) isEmptyShaped
346 return CGRectEqualToRect(CGPathGetBoundingBox(self.path), CGRectZero);
352 @interface WineBaseView : NSView
356 @interface WineMetalView : WineBaseView
358 id<MTLDevice> _device;
361 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
366 @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContentScaleDelegate>
368 NSMutableArray* glContexts;
369 NSMutableArray* pendingGlContexts;
370 BOOL _everHadGLContext;
371 BOOL _cachedHasGLDescendant;
372 BOOL _cachedHasGLDescendantValid;
373 BOOL clearedGlSurface;
375 NSMutableAttributedString* markedText;
376 NSRange markedTextSelection;
380 WineMetalView *_metalView;
383 @property (readonly, nonatomic) BOOL everHadGLContext;
385 - (void) addGLContext:(WineOpenGLContext*)context;
386 - (void) removeGLContext:(WineOpenGLContext*)context;
387 - (void) updateGLContexts;
389 - (void) wine_getBackingSize:(int*)outBackingSize;
390 - (void) wine_setBackingSize:(const int*)newBackingSize;
392 - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
397 @interface WineWindow ()
399 @property (readwrite, nonatomic) BOOL disabled;
400 @property (readwrite, nonatomic) BOOL noForeground;
401 @property (readwrite, nonatomic) BOOL preventsAppActivation;
402 @property (readwrite, nonatomic) BOOL floating;
403 @property (readwrite, nonatomic) BOOL drawnSinceShown;
404 @property (readwrite, nonatomic) BOOL closing;
405 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
406 @property (retain, nonatomic) NSWindow* latentParentWindow;
408 @property (nonatomic) void* hwnd;
409 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
411 @property (nonatomic) void* surface;
412 @property (nonatomic) pthread_mutex_t* surface_mutex;
414 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
415 @property (readonly, nonatomic) BOOL needsTransparency;
417 @property (nonatomic) BOOL colorKeyed;
418 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
419 @property (nonatomic) BOOL usePerPixelAlpha;
421 @property (assign, nonatomic) void* himc;
422 @property (nonatomic) BOOL commandDone;
424 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
426 - (void) setShape:(CGPathRef)newShape;
428 - (void) updateForGLSubviews;
430 - (BOOL) becameEligibleParentOrChild;
431 - (void) becameIneligibleChild;
433 - (void) windowDidDrawContent;
438 @implementation WineBaseView
440 - (void) setRetinaMode:(int)mode
442 for (WineBaseView* subview in [self subviews])
444 if ([subview isKindOfClass:[WineBaseView class]])
445 [subview setRetinaMode:mode];
449 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
454 - (BOOL) preservesContentDuringLiveResize
456 // Returning YES from this tells Cocoa to keep our view's content during
457 // a Cocoa-driven resize. In theory, we're also supposed to override
458 // -setFrameSize: to mark exposed sections as needing redisplay, but
459 // user32 will take care of that in a roundabout way. This way, we don't
460 // redraw until the window surface is flushed.
462 // This doesn't do anything when we resize the window ourselves.
466 - (BOOL)acceptsFirstResponder
468 return [[self window] contentView] == self;
471 - (BOOL) mouseDownCanMoveWindow
476 - (NSFocusRingType) focusRingType
478 return NSFocusRingTypeNone;
484 @implementation WineContentView
486 @synthesize everHadGLContext = _everHadGLContext;
488 - (instancetype) initWithFrame:(NSRect)frame
490 self = [super initWithFrame:frame];
493 [self setWantsLayer:YES];
494 [self setLayerRetinaProperties:retina_on];
495 [self setAutoresizesSubviews:NO];
502 [markedText release];
503 [glContexts release];
504 [pendingGlContexts release];
513 - (BOOL) wantsUpdateLayer
515 return YES /*!_everHadGLContext*/;
520 WineWindow* window = (WineWindow*)[self window];
521 CGImageRef image = NULL;
523 CALayer* layer = [self layer];
525 if ([window contentView] != self)
528 if (window.closing || !window.surface || !window.surface_mutex)
531 pthread_mutex_lock(window.surface_mutex);
532 if (get_surface_blit_rects(window.surface, NULL, NULL))
534 imageRect = layer.bounds;
535 imageRect.origin.x *= layer.contentsScale;
536 imageRect.origin.y *= layer.contentsScale;
537 imageRect.size.width *= layer.contentsScale;
538 imageRect.size.height *= layer.contentsScale;
539 image = create_surface_image(window.surface, &imageRect, FALSE, window.colorKeyed,
540 window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue);
542 pthread_mutex_unlock(window.surface_mutex);
546 layer.contents = (id)image;
548 [window windowDidDrawContent];
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.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
555 window.shapeChangedSinceLastDraw = FALSE;
556 [window invalidateShadow];
561 - (void) viewWillDraw
563 [super viewWillDraw];
565 for (WineOpenGLContext* context in pendingGlContexts)
567 if (!clearedGlSurface)
569 context.shouldClearToBlack = TRUE;
570 clearedGlSurface = TRUE;
572 context.needsUpdate = TRUE;
574 [glContexts addObjectsFromArray:pendingGlContexts];
575 [pendingGlContexts removeAllObjects];
578 - (void) addGLContext:(WineOpenGLContext*)context
580 BOOL hadContext = _everHadGLContext;
582 glContexts = [[NSMutableArray alloc] init];
583 if (!pendingGlContexts)
584 pendingGlContexts = [[NSMutableArray alloc] init];
586 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
588 [glContexts addObject:context];
589 if (!clearedGlSurface)
591 context.shouldClearToBlack = TRUE;
592 clearedGlSurface = TRUE;
594 context.needsUpdate = TRUE;
598 [pendingGlContexts addObject:context];
599 [self setNeedsDisplay:YES];
602 _everHadGLContext = YES;
604 [self invalidateHasGLDescendant];
605 [(WineWindow*)[self window] updateForGLSubviews];
608 - (void) removeGLContext:(WineOpenGLContext*)context
610 [glContexts removeObjectIdenticalTo:context];
611 [pendingGlContexts removeObjectIdenticalTo:context];
612 [(WineWindow*)[self window] updateForGLSubviews];
615 - (void) updateGLContexts:(BOOL)reattach
617 for (WineOpenGLContext* context in glContexts)
619 context.needsUpdate = TRUE;
621 context.needsReattach = TRUE;
625 - (void) updateGLContexts
627 [self updateGLContexts:NO];
630 - (BOOL) _hasGLDescendant
634 if (_everHadGLContext)
636 for (WineContentView* view in [self subviews])
638 if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
644 - (BOOL) hasGLDescendant
646 if (!_cachedHasGLDescendantValid)
648 _cachedHasGLDescendant = [self _hasGLDescendant];
649 _cachedHasGLDescendantValid = YES;
651 return _cachedHasGLDescendant;
654 - (void) invalidateHasGLDescendant
656 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
657 _cachedHasGLDescendantValid = NO;
658 if (invalidateAncestors && self != [[self window] contentView])
660 WineContentView* superview = (WineContentView*)[self superview];
661 if ([superview isKindOfClass:[WineContentView class]])
662 [superview invalidateHasGLDescendant];
666 - (void) wine_getBackingSize:(int*)outBackingSize
668 @synchronized(self) {
669 memcpy(outBackingSize, backingSize, sizeof(backingSize));
672 - (void) wine_setBackingSize:(const int*)newBackingSize
674 @synchronized(self) {
675 memcpy(backingSize, newBackingSize, sizeof(backingSize));
679 - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
681 if (_metalView) return _metalView;
683 WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
684 [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
685 [self setAutoresizesSubviews:YES];
686 [self addSubview:view positioned:NSWindowBelow relativeTo:nil];
689 [(WineWindow*)self.window windowDidDrawContent];
694 - (void) setLayerRetinaProperties:(int)mode
696 [self layer].contentsScale = mode ? 2.0 : 1.0;
697 [self layer].minificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
698 [self layer].magnificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
700 /* On macOS 10.13 and earlier, the desired minificationFilter seems to be
701 * ignored and "nearest" filtering is used, which looks terrible.
702 * Enabling rasterization seems to work around this, only enable
703 * it when there may be down-scaling (retina mode enabled).
705 if (floor(NSAppKitVersionNumber) < 1671 /*NSAppKitVersionNumber10_14*/)
709 [self layer].shouldRasterize = YES;
710 [self layer].rasterizationScale = 2.0;
713 [self layer].shouldRasterize = NO;
717 - (void) setRetinaMode:(int)mode
719 double scale = mode ? 0.5 : 2.0;
720 NSRect frame = self.frame;
721 frame.origin.x *= scale;
722 frame.origin.y *= scale;
723 frame.size.width *= scale;
724 frame.size.height *= scale;
725 [self setFrame:frame];
726 [self setWantsBestResolutionOpenGLSurface:mode];
727 [self updateGLContexts];
728 [self setLayerRetinaProperties:mode];
730 [super setRetinaMode:mode];
733 - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window
735 /* This method is invoked when the contentsScale of the layer is not
736 * equal to the contentsScale of the window.
737 * (Initially when the layer is first created, and later if the window
738 * contentsScale changes, i.e. moved between retina/non-retina monitors).
740 * We usually want to return YES, so the "moving windows between
741 * retina/non-retina monitors" case works right.
742 * But return NO when we need an intentional mismatch between the
743 * window and layer contentsScale
744 * (non-retina mode with a retina monitor, and vice-versa).
746 if (layer.contentsScale != window.backingScaleFactor)
754 [self invalidateHasGLDescendant];
757 - (void) viewDidUnhide
759 [super viewDidUnhide];
760 [self updateGLContexts:YES];
761 [self invalidateHasGLDescendant];
764 - (void) clearMarkedText
766 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
767 markedTextSelection = NSMakeRange(0, 0);
768 [[self inputContext] discardMarkedText];
771 - (void) completeText:(NSString*)text
774 WineWindow* window = (WineWindow*)[self window];
776 event = macdrv_create_event(IM_SET_TEXT, window);
777 event->im_set_text.himc = [window himc];
778 event->im_set_text.text = (CFStringRef)[text copy];
779 event->im_set_text.complete = TRUE;
781 [[window queue] postEvent:event];
783 macdrv_release_event(event);
785 [self clearMarkedText];
788 - (void) didAddSubview:(NSView*)subview
790 if ([subview isKindOfClass:[WineContentView class]])
792 WineContentView* view = (WineContentView*)subview;
793 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
794 [self invalidateHasGLDescendant];
796 [super didAddSubview:subview];
799 - (void) willRemoveSubview:(NSView*)subview
801 if ([subview isKindOfClass:[WineContentView class]])
803 WineContentView* view = (WineContentView*)subview;
804 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
805 [self invalidateHasGLDescendant];
807 if (subview == _metalView)
809 [super willRemoveSubview:subview];
812 - (void) setLayer:(CALayer*)newLayer
814 [super setLayer:newLayer];
815 [self updateGLContexts];
819 * ---------- NSTextInputClient methods ----------
821 - (NSTextInputContext*) inputContext
824 markedText = [[NSMutableAttributedString alloc] init];
825 return [super inputContext];
828 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
830 if ([string isKindOfClass:[NSAttributedString class]])
831 string = [string string];
833 if ([string isKindOfClass:[NSString class]])
834 [self completeText:string];
837 - (void) doCommandBySelector:(SEL)aSelector
839 [(WineWindow*)[self window] setCommandDone:TRUE];
842 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
844 if ([string isKindOfClass:[NSAttributedString class]])
845 string = [string string];
847 if ([string isKindOfClass:[NSString class]])
850 WineWindow* window = (WineWindow*)[self window];
852 if (replacementRange.location == NSNotFound)
853 replacementRange = NSMakeRange(0, [markedText length]);
855 [markedText replaceCharactersInRange:replacementRange withString:string];
856 markedTextSelection = selectedRange;
857 markedTextSelection.location += replacementRange.location;
859 event = macdrv_create_event(IM_SET_TEXT, window);
860 event->im_set_text.himc = [window himc];
861 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
862 event->im_set_text.complete = FALSE;
863 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
865 [[window queue] postEvent:event];
867 macdrv_release_event(event);
869 [[self inputContext] invalidateCharacterCoordinates];
875 [self completeText:nil];
878 - (NSRange) selectedRange
880 return markedTextSelection;
883 - (NSRange) markedRange
885 NSRange range = NSMakeRange(0, [markedText length]);
887 range.location = NSNotFound;
891 - (BOOL) hasMarkedText
893 return [markedText length] > 0;
896 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
898 if (aRange.location >= [markedText length])
901 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
903 *actualRange = aRange;
904 return [markedText attributedSubstringFromRange:aRange];
907 - (NSArray*) validAttributesForMarkedText
909 return [NSArray array];
912 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
915 WineWindow* window = (WineWindow*)[self window];
918 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
920 query = macdrv_create_query();
921 query->type = QUERY_IME_CHAR_RECT;
922 query->window = (macdrv_window)[window retain];
923 query->ime_char_rect.himc = [window himc];
924 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
926 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
928 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
929 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
930 [[WineApplicationController sharedController] flipRect:&ret];
933 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
935 macdrv_release_query(query);
938 *actualRange = aRange;
942 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
947 - (NSInteger) windowLevel
949 return [[self window] level];
955 @implementation WineMetalView
957 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
959 self = [super initWithFrame:frame];
962 _device = [device retain];
963 self.wantsLayer = YES;
964 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
975 - (void) setRetinaMode:(int)mode
977 self.layer.contentsScale = mode ? 2.0 : 1.0;
978 [super setRetinaMode:mode];
981 - (CALayer*) makeBackingLayer
983 CAMetalLayer *layer = [CAMetalLayer layer];
984 layer.device = _device;
985 layer.framebufferOnly = YES;
986 layer.magnificationFilter = kCAFilterNearest;
987 layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
988 layer.contentsScale = retina_on ? 2.0 : 1.0;
1000 @implementation WineWindow
1002 static WineWindow* causing_becomeKeyWindow;
1004 @synthesize disabled, noForeground, preventsAppActivation, floating, fullscreen, fakingClose, closing, latentParentWindow, hwnd, queue;
1005 @synthesize drawnSinceShown;
1006 @synthesize surface, surface_mutex;
1007 @synthesize shapeChangedSinceLastDraw;
1008 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
1009 @synthesize usePerPixelAlpha;
1010 @synthesize himc, commandDone;
1012 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
1013 windowFrame:(NSRect)window_frame
1015 queue:(WineEventQueue*)queue
1018 WineContentView* contentView;
1019 NSTrackingArea* trackingArea;
1020 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1022 [[WineApplicationController sharedController] flipRect:&window_frame];
1024 window = [[[self alloc] initWithContentRect:window_frame
1025 styleMask:style_mask_for_features(wf)
1026 backing:NSBackingStoreBuffered
1027 defer:YES] autorelease];
1029 if (!window) return nil;
1031 /* Standardize windows to eliminate differences between titled and
1032 borderless windows and between NSWindow and NSPanel. */
1033 [window setHidesOnDeactivate:NO];
1034 [window setReleasedWhenClosed:NO];
1036 [window setOneShot:YES];
1037 [window disableCursorRects];
1038 [window setShowsResizeIndicator:NO];
1039 [window setHasShadow:wf->shadow];
1040 [window setAcceptsMouseMovedEvents:YES];
1041 [window setDelegate:window];
1042 [window setBackgroundColor:[NSColor clearColor]];
1043 [window setOpaque:NO];
1045 window.queue = queue;
1046 window->savedContentMinSize = NSZeroSize;
1047 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
1048 window->resizable = wf->resizable;
1049 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
1051 [window registerForDraggedTypes:@[(NSString*)kUTTypeData, (NSString*)kUTTypeContent]];
1053 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
1057 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
1058 because they give us mouse moves in the background. */
1059 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
1060 options:(NSTrackingMouseMoved |
1061 NSTrackingActiveAlways |
1062 NSTrackingInVisibleRect)
1064 userInfo:nil] autorelease];
1067 [contentView addTrackingArea:trackingArea];
1069 [window setContentView:contentView];
1070 [window setInitialFirstResponder:contentView];
1072 [nc addObserver:window
1073 selector:@selector(updateFullscreen)
1074 name:NSApplicationDidChangeScreenParametersNotification
1076 [window updateFullscreen];
1078 [nc addObserver:window
1079 selector:@selector(applicationWillHide)
1080 name:NSApplicationWillHideNotification
1082 [nc addObserver:window
1083 selector:@selector(applicationDidUnhide)
1084 name:NSApplicationDidUnhideNotification
1087 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1088 selector:@selector(checkWineDisplayLink)
1089 name:NSWorkspaceActiveSpaceDidChangeNotification
1090 object:[NSWorkspace sharedWorkspace]];
1092 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1099 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1100 [[NSNotificationCenter defaultCenter] removeObserver:self];
1102 [latentChildWindows release];
1103 [latentParentWindow release];
1107 - (BOOL) preventResizing
1109 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1110 return ([self styleMask] & NSWindowStyleMaskResizable) && (disabled || !resizable || preventForClipping);
1113 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1115 if (allow_immovable_windows && (disabled || inMaximized))
1117 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1123 - (void) adjustFeaturesForState
1125 NSUInteger style = [self styleMask];
1127 if (style & NSWindowStyleMaskClosable)
1128 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1129 if (style & NSWindowStyleMaskMiniaturizable)
1130 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1131 if (style & NSWindowStyleMaskResizable)
1132 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1133 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1134 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1136 if ([self preventResizing])
1138 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1139 [self setContentMinSize:size];
1140 [self setContentMaxSize:size];
1144 [self setContentMaxSize:savedContentMaxSize];
1145 [self setContentMinSize:savedContentMinSize];
1148 if (allow_immovable_windows || cursor_clipping_locks_windows)
1149 [self setMovable:[self allowsMovingWithMaximized:maximized]];
1152 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1154 NSUInteger style = [self styleMask];
1156 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1157 style & NSWindowStyleMaskResizable && !(style & NSWindowStyleMaskUtilityWindow) && !maximized &&
1158 !(self.parentWindow || self.latentParentWindow))
1160 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1161 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1165 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1166 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1167 if (style & NSWindowStyleMaskFullScreen)
1168 [super toggleFullScreen:nil];
1171 if (behavior != [self collectionBehavior])
1173 [self setCollectionBehavior:behavior];
1174 [self adjustFeaturesForState];
1178 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1180 static const NSUInteger usedStyles = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable |
1181 NSWindowStyleMaskResizable | NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskBorderless |
1182 NSWindowStyleMaskNonactivatingPanel;
1183 NSUInteger currentStyle = [self styleMask];
1184 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1186 self.preventsAppActivation = wf->prevents_app_activation;
1188 if (newStyle != currentStyle)
1190 NSString* title = [[[self title] copy] autorelease];
1191 BOOL showingButtons = (currentStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1192 BOOL shouldShowButtons = (newStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1193 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSWindowStyleMaskClosable))
1195 // -setStyleMask: is buggy on 10.7+ with respect to NSWindowStyleMaskResizable.
1196 // If transitioning from NSWindowStyleMaskTitled | NSWindowStyleMaskResizable to
1197 // just NSWindowStyleMaskTitled, the window buttons should disappear rather
1198 // than just being disabled. But they don't. Similarly in reverse.
1199 // The workaround is to also toggle NSWindowStyleMaskClosable at the same time.
1200 [self setStyleMask:newStyle ^ NSWindowStyleMaskClosable];
1202 [self setStyleMask:newStyle];
1204 BOOL isNonActivating = (currentStyle & NSWindowStyleMaskNonactivatingPanel) != 0;
1205 BOOL shouldBeNonActivating = (newStyle & NSWindowStyleMaskNonactivatingPanel) != 0;
1206 if (isNonActivating != shouldBeNonActivating) {
1207 // Changing NSWindowStyleMaskNonactivatingPanel with -setStyleMask is also
1208 // buggy. If it's added, clicking the title bar will still activate the
1209 // app. If it's removed, nothing changes at all.
1210 // This private method ensures the correct behavior.
1211 if ([self respondsToSelector:@selector(_setPreventsActivation:)])
1212 [self _setPreventsActivation:shouldBeNonActivating];
1215 // -setStyleMask: resets the firstResponder to the window. Set it
1216 // back to the content view.
1217 if ([[self contentView] acceptsFirstResponder])
1218 [self makeFirstResponder:[self contentView]];
1220 [self adjustFullScreenBehavior:[self collectionBehavior]];
1222 if ([[self title] length] == 0 && [title length] > 0)
1223 [self setTitle:title];
1226 resizable = wf->resizable;
1227 [self adjustFeaturesForState];
1228 [self setHasShadow:wf->shadow];
1231 // Indicates if the window would be visible if the app were not hidden.
1232 - (BOOL) wouldBeVisible
1234 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1237 - (BOOL) isOrderedIn
1239 return [self wouldBeVisible] || [self isMiniaturized];
1242 - (NSInteger) minimumLevelForActive:(BOOL)active
1246 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1247 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1248 level = NSFloatingWindowLevel;
1250 level = NSNormalWindowLevel;
1256 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1258 if (captured || fullscreen)
1261 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1263 level = NSStatusWindowLevel + 1;
1273 - (void) postDidUnminimizeEvent
1275 macdrv_event* event;
1277 /* Coalesce events by discarding any previous ones still in the queue. */
1278 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1281 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1282 [queue postEvent:event];
1283 macdrv_release_event(event);
1286 - (void) sendResizeStartQuery
1288 macdrv_query* query = macdrv_create_query();
1289 query->type = QUERY_RESIZE_START;
1290 query->window = (macdrv_window)[self retain];
1292 [self.queue query:query timeout:0.3];
1293 macdrv_release_query(query);
1296 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1298 NSWindowCollectionBehavior behavior;
1300 self.disabled = state->disabled;
1301 self.noForeground = state->no_foreground;
1303 if (self.floating != state->floating)
1305 self.floating = state->floating;
1306 if (state->floating)
1308 // Became floating. If child of non-floating window, make that
1309 // relationship latent.
1310 WineWindow* parent = (WineWindow*)[self parentWindow];
1311 if (parent && !parent.floating)
1312 [self becameIneligibleChild];
1316 // Became non-floating. If parent of floating children, make that
1317 // relationship latent.
1319 for (child in [self childWineWindows])
1322 [child becameIneligibleChild];
1326 // Check our latent relationships. If floating status was the only
1327 // reason they were latent, then make them active.
1328 if ([self isVisible])
1329 [self becameEligibleParentOrChild];
1331 [[WineApplicationController sharedController] adjustWindowLevels];
1334 if (state->minimized_valid)
1336 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1338 pendingMinimize = FALSE;
1339 if (state->minimized && ![self isMiniaturized])
1341 if ([self wouldBeVisible])
1343 if (([self styleMask] & NSWindowStyleMaskFullScreen) || stage_manager_enabled())
1345 [self postDidUnminimizeEvent];
1346 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1348 /* When Stage Manager is enabled, it's not possible to minimize the window
1349 * (miniaturize: just moves the window to the background).
1350 * Post an unminimize event, then miniaturize:.
1352 if (stage_manager_enabled())
1354 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1355 [super miniaturize:nil];
1360 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1361 [super miniaturize:nil];
1362 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1363 event_mask_for_type(WINDOW_GOT_FOCUS) |
1364 event_mask_for_type(WINDOW_LOST_FOCUS);
1368 pendingMinimize = TRUE;
1370 else if (!state->minimized && [self isMiniaturized])
1372 ignore_windowDeminiaturize = TRUE;
1373 [self deminiaturize:nil];
1374 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1378 [queue discardEventsMatchingMask:discard forWindow:self];
1381 if (state->maximized != maximized)
1383 maximized = state->maximized;
1384 [self adjustFeaturesForState];
1386 if (!maximized && [self inLiveResize])
1387 [self sendResizeStartQuery];
1390 behavior = NSWindowCollectionBehaviorDefault;
1391 if (state->excluded_by_expose)
1392 behavior |= NSWindowCollectionBehaviorTransient;
1394 behavior |= NSWindowCollectionBehaviorManaged;
1395 if (state->excluded_by_cycle)
1397 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1398 if ([self isOrderedIn])
1399 [NSApp removeWindowsItem:self];
1403 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1404 if ([self isOrderedIn])
1405 [NSApp addWindowsItem:self title:[self title] filename:NO];
1407 [self adjustFullScreenBehavior:behavior];
1410 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1412 BOOL reordered = FALSE;
1414 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1416 if ([self level] > [child level])
1417 [child setLevel:[self level]];
1418 if (![child isVisible])
1419 [child setAutodisplay:YES];
1420 [self addChildWindow:child ordered:NSWindowAbove];
1421 [child checkWineDisplayLink];
1422 [latentChildWindows removeObjectIdenticalTo:child];
1423 child.latentParentWindow = nil;
1428 if (!latentChildWindows)
1429 latentChildWindows = [[NSMutableArray alloc] init];
1430 if (![latentChildWindows containsObject:child])
1431 [latentChildWindows addObject:child];
1432 child.latentParentWindow = self;
1438 - (BOOL) addChildWineWindow:(WineWindow*)child
1440 return [self addChildWineWindow:child assumeVisible:FALSE];
1443 - (void) removeChildWineWindow:(WineWindow*)child
1445 [self removeChildWindow:child];
1446 if (child.latentParentWindow == self)
1447 child.latentParentWindow = nil;
1448 [latentChildWindows removeObjectIdenticalTo:child];
1451 - (void) setChildWineWindows:(NSArray*)childWindows
1453 NSArray* origChildren;
1454 NSUInteger count, start, limit, i;
1456 origChildren = self.childWineWindows;
1458 // If the current and desired children arrays match up to a point, leave
1459 // those matching children alone.
1460 count = childWindows.count;
1461 limit = MIN(origChildren.count, count);
1462 for (start = 0; start < limit; start++)
1464 if (origChildren[start] != childWindows[start])
1468 // Remove all of the child windows and re-add them back-to-front so they
1469 // are in the desired order.
1470 for (i = start; i < count; i++)
1472 WineWindow* child = childWindows[i];
1473 [self removeChildWindow:child];
1475 for (i = start; i < count; i++)
1477 WineWindow* child = childWindows[i];
1478 [self addChildWindow:child ordered:NSWindowAbove];
1482 static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1484 NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1485 NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1486 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1487 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1488 if (index1 == NSNotFound)
1490 if (index2 == NSNotFound)
1491 return NSOrderedSame;
1493 return NSOrderedAscending;
1495 else if (index2 == NSNotFound)
1496 return NSOrderedDescending;
1497 else if (index1 < index2)
1498 return NSOrderedDescending;
1499 else if (index2 < index1)
1500 return NSOrderedAscending;
1502 return NSOrderedSame;
1505 - (BOOL) becameEligibleParentOrChild
1507 BOOL reordered = FALSE;
1510 if (latentParentWindow.floating || !self.floating)
1512 // If we aren't visible currently, we assume that we should be and soon
1513 // will be. So, if the latent parent is visible that's enough to assume
1514 // we can establish the parent-child relationship in Cocoa. That will
1515 // actually make us visible, which is fine.
1516 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1520 // Here, though, we may not actually be visible yet and adding a child
1521 // won't make us visible. The caller will have to call this method
1522 // again after actually making us visible.
1523 if ([self isVisible] && (count = [latentChildWindows count]))
1525 NSMutableArray* windowNumbers;
1526 NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1527 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1530 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1532 for (i = 0; i < count; i++)
1534 WineWindow* child = latentChildWindows[i];
1535 if ([child isVisible] && (self.floating || !child.floating))
1537 if (child.latentParentWindow == self)
1539 if ([self level] > [child level])
1540 [child setLevel:[self level]];
1541 [childWindows addObject:child];
1542 child.latentParentWindow = nil;
1546 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1547 [indexesToRemove addIndex:i];
1551 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1553 [childWindows sortWithOptions:NSSortStable
1554 usingComparator:^NSComparisonResult(id obj1, id obj2){
1555 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1557 [self setChildWineWindows:childWindows];
1563 - (void) becameIneligibleChild
1565 WineWindow* parent = (WineWindow*)[self parentWindow];
1568 if (!parent->latentChildWindows)
1569 parent->latentChildWindows = [[NSMutableArray alloc] init];
1570 [parent->latentChildWindows insertObject:self atIndex:0];
1571 self.latentParentWindow = parent;
1572 [parent removeChildWindow:self];
1576 - (void) becameIneligibleParentOrChild
1578 NSArray* childWindows = [self childWineWindows];
1580 [self becameIneligibleChild];
1582 if ([childWindows count])
1586 for (child in childWindows)
1588 child.latentParentWindow = self;
1589 [self removeChildWindow:child];
1592 if (latentChildWindows)
1593 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1595 latentChildWindows = [childWindows mutableCopy];
1599 // Determine if, among Wine windows, this window is directly above or below
1600 // a given other Wine window with no other Wine window intervening.
1601 // Intervening non-Wine windows are ignored.
1602 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1604 NSNumber* windowNumber;
1605 NSNumber* otherWindowNumber;
1606 NSArray* windowNumbers;
1607 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1609 if (![self isVisible] || ![otherWindow isVisible])
1612 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1613 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1614 windowNumbers = [[self class] windowNumbersWithOptions:0];
1615 windowIndex = [windowNumbers indexOfObject:windowNumber];
1616 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1618 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1621 if (orderingMode == NSWindowAbove)
1623 lowIndex = windowIndex;
1624 highIndex = otherWindowIndex;
1626 else if (orderingMode == NSWindowBelow)
1628 lowIndex = otherWindowIndex;
1629 highIndex = windowIndex;
1634 if (highIndex <= lowIndex)
1637 for (i = lowIndex + 1; i < highIndex; i++)
1639 NSInteger interveningWindowNumber = [windowNumbers[i] integerValue];
1640 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1641 if ([interveningWindow isKindOfClass:[WineWindow class]])
1648 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1650 NSMutableArray* windowNumbers;
1651 NSNumber* childWindowNumber;
1652 NSUInteger otherIndex;
1653 NSArray* origChildren;
1654 NSMutableArray* children;
1656 // Get the z-order from the window server and modify it to reflect the
1657 // requested window ordering.
1658 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1659 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1660 [windowNumbers removeObject:childWindowNumber];
1663 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1664 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1666 else if (mode == NSWindowAbove)
1667 [windowNumbers insertObject:childWindowNumber atIndex:0];
1669 [windowNumbers addObject:childWindowNumber];
1671 // Get our child windows and sort them in the reverse of the desired
1672 // z-order (back-to-front).
1673 origChildren = [self childWineWindows];
1674 children = [[origChildren mutableCopy] autorelease];
1675 [children sortWithOptions:NSSortStable
1676 usingComparator:^NSComparisonResult(id obj1, id obj2){
1677 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1680 [self setChildWineWindows:children];
1683 // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1684 // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1685 // (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
1686 // if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1687 // otherwise, the two ancestors are each roots of disjoint window trees
1688 // (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
1689 // (non-nil, nil) self is a descendent of other and *ancestor is the direct child
1690 - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1692 NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1696 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1702 *ancestorOfOther = child;
1706 [otherAncestors addObject:parent];
1710 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1713 NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1714 if (index != NSNotFound)
1718 *ancestorOfOther = nil;
1720 *ancestorOfOther = otherAncestors[index - 1];
1726 *ancestorOfOther = otherAncestors.lastObject;;
1729 /* Returns whether or not the window was ordered in, which depends on if
1730 its frame intersects any screen. */
1731 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1733 WineApplicationController* controller = [WineApplicationController sharedController];
1734 if (![self isMiniaturized])
1736 BOOL needAdjustWindowLevels = FALSE;
1741 [controller transformProcessToForeground:!self.preventsAppActivation];
1742 if ([NSApp isHidden])
1744 wasVisible = [self isVisible];
1747 [controller tryToActivateIgnoringOtherApps:YES];
1749 NSDisableScreenUpdates();
1751 if ([self becameEligibleParentOrChild])
1752 needAdjustWindowLevels = TRUE;
1756 WineWindow* other = [prev isVisible] ? prev : next;
1757 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1759 if (![self isOrdered:orderingMode relativeTo:other])
1761 WineWindow* ancestor;
1762 WineWindow* ancestorOfOther;
1764 [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1767 [self setAutodisplay:YES];
1768 if (ancestorOfOther)
1770 // This window level may not be right for this window based
1771 // on floating-ness, fullscreen-ness, etc. But we set it
1772 // temporarily to allow us to order the windows properly.
1773 // Then the levels get fixed by -adjustWindowLevels.
1774 if ([ancestor level] != [ancestorOfOther level])
1775 [ancestor setLevel:[ancestorOfOther level]];
1777 parent = (WineWindow*)ancestor.parentWindow;
1778 if ([parent isKindOfClass:[WineWindow class]])
1779 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1781 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1784 if (!ancestorOfOther || ancestor != self)
1787 (parent = (WineWindow*)child.parentWindow);
1790 if ([parent isKindOfClass:[WineWindow class]])
1791 [parent order:-orderingMode childWindow:child relativeTo:nil];
1792 if (parent == ancestor)
1797 [self checkWineDisplayLink];
1798 needAdjustWindowLevels = TRUE;
1805 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1808 [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1811 // Again, temporarily set level to make sure we can order to
1813 next = [controller frontWineWindow];
1814 if (next && [self level] < [next level])
1815 [self setLevel:[next level]];
1816 [self setAutodisplay:YES];
1817 [self orderFront:nil];
1818 [self checkWineDisplayLink];
1819 needAdjustWindowLevels = TRUE;
1821 pendingOrderOut = FALSE;
1823 if ([self becameEligibleParentOrChild])
1824 needAdjustWindowLevels = TRUE;
1826 if (needAdjustWindowLevels)
1828 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1829 [controller updateFullscreenWindows];
1830 [controller adjustWindowLevels];
1833 if (pendingMinimize)
1835 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1836 [super miniaturize:nil];
1837 pendingMinimize = FALSE;
1840 NSEnableScreenUpdates();
1842 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1843 Generate a frame-changed event just in case. The back end will ignore
1844 it if nothing actually changed. */
1845 [self windowDidResize:nil skipSizeMove:TRUE];
1847 if (![self isExcludedFromWindowsMenu])
1848 [NSApp addWindowsItem:self title:[self title] filename:NO];
1854 WineApplicationController* controller = [WineApplicationController sharedController];
1855 BOOL wasVisible = [self isVisible];
1856 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1858 [self endWindowDragging];
1859 [controller windowWillOrderOut:self];
1861 if (enteringFullScreen || exitingFullScreen)
1863 pendingOrderOut = TRUE;
1864 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1865 event_mask_for_type(WINDOW_GOT_FOCUS) |
1866 event_mask_for_type(WINDOW_LOST_FOCUS) |
1867 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1868 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1869 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1874 pendingOrderOut = FALSE;
1876 if ([self isMiniaturized])
1877 pendingMinimize = TRUE;
1879 WineWindow* parent = (WineWindow*)self.parentWindow;
1880 if ([parent isKindOfClass:[WineWindow class]])
1881 [parent grabDockIconSnapshotFromWindow:self force:NO];
1883 [self becameIneligibleParentOrChild];
1884 if ([self isMiniaturized] || [self styleMask] & NSWindowStyleMaskFullScreen)
1888 fakingClose = FALSE;
1891 [self orderOut:nil];
1892 [self checkWineDisplayLink];
1893 [self setBackgroundColor:[NSColor clearColor]];
1894 [self setOpaque:NO];
1895 drawnSinceShown = NO;
1896 savedVisibleState = FALSE;
1897 if (wasVisible && wasOnActiveSpace && fullscreen)
1898 [controller updateFullscreenWindows];
1899 [controller adjustWindowLevels];
1900 [NSApp removeWindowsItem:self];
1902 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1903 event_mask_for_type(WINDOW_GOT_FOCUS) |
1904 event_mask_for_type(WINDOW_LOST_FOCUS) |
1905 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1906 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1907 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1911 - (void) updateFullscreen
1913 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1914 BOOL nowFullscreen = !([self styleMask] & NSWindowStyleMaskFullScreen) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1916 if (nowFullscreen != fullscreen)
1918 WineApplicationController* controller = [WineApplicationController sharedController];
1920 fullscreen = nowFullscreen;
1921 if ([self isVisible] && [self isOnActiveSpace])
1922 [controller updateFullscreenWindows];
1924 [controller adjustWindowLevels];
1928 - (void) setFrameAndWineFrame:(NSRect)frame
1930 [self setFrame:frame display:YES];
1933 roundedWineFrame = self.frame;
1935 #if CGFLOAT_IS_DOUBLE
1936 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1937 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1938 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1939 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1940 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1941 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1942 roundedWineFrame = wineFrame;
1944 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1945 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1946 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1947 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1948 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1949 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1950 roundedWineFrame = wineFrame;
1954 - (void) setFrameFromWine:(NSRect)contentRect
1956 /* Origin is (left, top) in a top-down space. Need to convert it to
1957 (left, bottom) in a bottom-up space. */
1958 [[WineApplicationController sharedController] flipRect:&contentRect];
1960 /* The back end is establishing a new window size and position. It's
1961 not interested in any stale events regarding those that may be sitting
1963 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1966 if (!NSIsEmptyRect(contentRect))
1968 NSRect frame, oldFrame;
1970 oldFrame = self.wine_fractionalFrame;
1971 frame = [self frameRectForContentRect:contentRect];
1972 if (!NSEqualRects(frame, oldFrame))
1974 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1975 BOOL needEnableScreenUpdates = FALSE;
1977 if ([self preventResizing])
1979 // Allow the following calls to -setFrame:display: to work even
1980 // if they would violate the content size constraints. This
1981 // shouldn't be necessary since the content size constraints are
1982 // documented to not constrain that method, but it seems to be.
1983 [self setContentMinSize:NSZeroSize];
1984 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1987 if (equalSizes && [[self childWineWindows] count])
1989 // If we change the window frame such that the origin moves
1990 // but the size doesn't change, then Cocoa moves child
1991 // windows with the parent. We don't want that so we fake
1992 // a change of the size and then change it back.
1993 NSRect bogusFrame = frame;
1994 bogusFrame.size.width++;
1996 NSDisableScreenUpdates();
1997 needEnableScreenUpdates = TRUE;
1999 ignore_windowResize = TRUE;
2000 [self setFrame:bogusFrame display:NO];
2001 ignore_windowResize = FALSE;
2004 [self setFrameAndWineFrame:frame];
2005 if ([self preventResizing])
2007 [self setContentMinSize:contentRect.size];
2008 [self setContentMaxSize:contentRect.size];
2011 if (needEnableScreenUpdates)
2012 NSEnableScreenUpdates();
2014 if (!enteringFullScreen &&
2015 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
2016 nonFullscreenFrame = frame;
2018 [self updateFullscreen];
2020 if ([self isOrderedIn])
2022 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
2023 event. The back end will ignore it if nothing actually changed. */
2024 [self windowDidResize:nil skipSizeMove:TRUE];
2030 - (NSRect) wine_fractionalFrame
2032 NSRect frame = self.frame;
2033 if (NSEqualRects(frame, roundedWineFrame))
2038 - (void) setMacDrvParentWindow:(WineWindow*)parent
2040 WineWindow* oldParent = (WineWindow*)[self parentWindow];
2041 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
2043 [oldParent removeChildWineWindow:self];
2044 [latentParentWindow removeChildWineWindow:self];
2045 if ([parent addChildWineWindow:self])
2046 [[WineApplicationController sharedController] adjustWindowLevels];
2047 [self adjustFullScreenBehavior:[self collectionBehavior]];
2051 - (void) setDisabled:(BOOL)newValue
2053 if (disabled != newValue)
2055 disabled = newValue;
2056 [self adjustFeaturesForState];
2060 - (BOOL) needsTransparency
2062 return self.contentView.layer.mask || self.colorKeyed || self.usePerPixelAlpha ||
2063 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
2066 - (void) checkTransparency
2068 if (![self isOpaque] && !self.needsTransparency)
2070 self.shapeChangedSinceLastDraw = TRUE;
2071 [[self contentView] setNeedsDisplay:YES];
2072 [self setBackgroundColor:[NSColor windowBackgroundColor]];
2073 [self setOpaque:YES];
2075 else if ([self isOpaque] && self.needsTransparency)
2077 self.shapeChangedSinceLastDraw = TRUE;
2078 [[self contentView] setNeedsDisplay:YES];
2079 [self setBackgroundColor:[NSColor clearColor]];
2080 [self setOpaque:NO];
2084 - (void) setShape:(CGPathRef)newShape
2086 CALayer* layer = [[self contentView] layer];
2087 CAShapeLayer* mask = (CAShapeLayer*)layer.mask;
2088 if (CGPathEqualToPath(newShape, mask.path)) return;
2090 if (newShape && !layer.mask)
2091 layer.mask = mask = [CAShapeLayer layer];
2093 layer.mask = mask = nil;
2096 [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(mask.path))];
2098 [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(newShape))];
2100 mask.path = newShape;
2101 self.shapeChangedSinceLastDraw = TRUE;
2103 [self checkTransparency];
2104 [self checkEmptyShaped];
2107 - (void) makeFocused:(BOOL)activate
2111 WineApplicationController *controller = [WineApplicationController sharedController];
2112 [controller transformProcessToForeground:YES];
2113 [controller tryToActivateIgnoringOtherApps:YES];
2116 causing_becomeKeyWindow = self;
2117 [self makeKeyWindow];
2118 causing_becomeKeyWindow = nil;
2120 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2121 event_mask_for_type(WINDOW_LOST_FOCUS)
2125 - (void) postKey:(uint16_t)keyCode
2126 pressed:(BOOL)pressed
2127 modifiers:(NSUInteger)modifiers
2128 event:(NSEvent*)theEvent
2130 macdrv_event* event;
2132 WineApplicationController* controller = [WineApplicationController sharedController];
2134 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2135 event->key.keycode = keyCode;
2136 event->key.modifiers = modifiers;
2137 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
2139 if ((cgevent = [theEvent CGEvent]))
2140 controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2142 [queue postEvent:event];
2144 macdrv_release_event(event);
2146 [controller noteKey:keyCode pressed:pressed];
2149 - (void) postKeyEvent:(NSEvent *)theEvent
2151 [self flagsChanged:theEvent];
2152 [self postKey:[theEvent keyCode]
2153 pressed:[theEvent type] == NSEventTypeKeyDown
2154 modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2158 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2160 savedContentMinSize = minSize;
2161 savedContentMaxSize = maxSize;
2162 if (![self preventResizing])
2164 [self setContentMinSize:minSize];
2165 [self setContentMaxSize:maxSize];
2169 - (WineWindow*) ancestorWineWindow
2171 WineWindow* ancestor = self;
2174 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2175 if ([parent isKindOfClass:[WineWindow class]])
2183 - (void) postBroughtForwardEvent
2185 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2186 [queue postEvent:event];
2187 macdrv_release_event(event);
2190 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing skipSizeMove:(BOOL)skipSizeMove
2192 macdrv_event* event;
2193 NSUInteger style = self.styleMask;
2196 style |= NSWindowStyleMaskFullScreen;
2198 style &= ~NSWindowStyleMaskFullScreen;
2199 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2200 [[WineApplicationController sharedController] flipRect:&frame];
2202 /* Coalesce events by discarding any previous ones still in the queue. */
2203 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2206 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2207 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2208 event->window_frame_changed.fullscreen = isFullscreen;
2209 event->window_frame_changed.in_resize = resizing;
2210 event->window_frame_changed.skip_size_move_loop = skipSizeMove;
2211 [queue postEvent:event];
2212 macdrv_release_event(event);
2215 - (void) updateForCursorClipping
2217 [self adjustFeaturesForState];
2220 - (void) endWindowDragging
2224 if (draggingPhase == 3)
2226 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2227 [queue postEvent:event];
2228 macdrv_release_event(event);
2232 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2236 - (NSMutableDictionary*) displayIDToDisplayLinkMap
2238 static NSMutableDictionary* displayIDToDisplayLinkMap;
2239 if (!displayIDToDisplayLinkMap)
2241 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2243 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2246 usingBlock:^(NSNotification *note){
2247 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2248 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2249 [badDisplayIDs minusSet:validDisplayIDs];
2250 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2253 return displayIDToDisplayLinkMap;
2256 - (WineDisplayLink*) wineDisplayLink
2258 if (!_lastDisplayID)
2261 return [self displayIDToDisplayLinkMap][@(_lastDisplayID)];
2264 - (void) checkWineDisplayLink
2266 NSScreen* screen = self.screen;
2267 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2269 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2270 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2274 NSNumber* displayIDNumber = screen.deviceDescription[@"NSScreenNumber"];
2275 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2276 if (displayID == _lastDisplayID)
2279 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2283 WineDisplayLink* link = displayIDToDisplayLinkMap[@(_lastDisplayID)];
2284 [link removeWindow:self];
2288 WineDisplayLink* link = displayIDToDisplayLinkMap[displayIDNumber];
2291 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2292 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2294 [link addWindow:self];
2295 [self displayIfNeeded];
2297 _lastDisplayID = displayID;
2300 - (BOOL) isEmptyShaped
2302 CAShapeLayer* mask = (CAShapeLayer*)[[self contentView] layer].mask;
2303 return ([mask isEmptyShaped]);
2306 - (BOOL) canProvideSnapshot
2308 return (self.windowNumber > 0 && ![self isEmptyShaped]);
2311 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2313 if (![self isEmptyShaped])
2316 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2317 if (!force && now < lastDockIconSnapshot + 1)
2322 if (![window canProvideSnapshot])
2328 for (WineWindow* childWindow in self.childWindows)
2330 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2333 NSSize size = childWindow.frame.size;
2334 CGFloat area = size.width * size.height;
2335 if (!window || area > bestArea)
2337 window = childWindow;
2346 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2347 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2348 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2349 CFRelease(windowIDs);
2353 NSImage* appImage = [NSApp applicationIconImage];
2355 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2357 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2358 [dockIcon lockFocus];
2360 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2362 CGRect rect = CGRectMake(8, 8, 240, 240);
2363 size_t width = CGImageGetWidth(windowImage);
2364 size_t height = CGImageGetHeight(windowImage);
2367 rect.size.height *= height / (double)width;
2368 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2370 else if (width != height)
2372 rect.size.width *= width / (double)height;
2373 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2376 CGContextDrawImage(cgcontext, rect, windowImage);
2377 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2379 operation:NSCompositingOperationSourceOver
2384 [dockIcon unlockFocus];
2386 CGImageRelease(windowImage);
2388 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2389 if (![imageView isKindOfClass:[NSImageView class]])
2391 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2392 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2393 self.dockTile.contentView = imageView;
2395 imageView.image = dockIcon;
2396 [self.dockTile display];
2397 lastDockIconSnapshot = now;
2400 - (void) checkEmptyShaped
2402 if (self.dockTile.contentView && ![self isEmptyShaped])
2404 self.dockTile.contentView = nil;
2405 lastDockIconSnapshot = 0;
2407 [self checkWineDisplayLink];
2412 * ---------- NSWindow method overrides ----------
2414 - (BOOL) canBecomeKeyWindow
2416 if (causing_becomeKeyWindow == self) return YES;
2417 if (self.disabled || self.noForeground) return NO;
2418 if ([self isKeyWindow]) return YES;
2420 // If a window's collectionBehavior says it participates in cycling,
2421 // it must return YES from this method to actually be eligible.
2422 return ![self isExcludedFromWindowsMenu];
2425 - (BOOL) canBecomeMainWindow
2427 return [self canBecomeKeyWindow];
2430 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2432 // If a window is sized to completely cover a screen, then it's in
2433 // full-screen mode. In that case, we don't allow NSWindow to constrain
2435 NSArray* screens = [NSScreen screens];
2436 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2437 if (!screen_covered_by_rect(contentRect, screens) &&
2438 frame_intersects_screens(frameRect, screens))
2439 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2443 // This private method of NSWindow is called as Cocoa reacts to the display
2444 // configuration changing. Among other things, it adjusts the window's
2445 // frame based on how the screen(s) changed size. That tells Wine that the
2446 // window has been moved. We don't want that. Rather, we want to make
2447 // sure that the WinAPI notion of the window position is maintained/
2448 // restored, possibly undoing or overriding Cocoa's adjustment.
2450 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2451 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2452 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2453 // reassert its notion of the position. That call won't get processed
2454 // until after this method returns, so it will override whatever this
2455 // method does to the window position. It will also discard any pending
2456 // WINDOW_FRAME_CHANGED events.
2458 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2459 // adjust the window's position due to a display change is to hook into
2460 // this private method. This private method has remained stable from 10.6
2461 // through 10.11. If it does change, the most likely thing is that it
2462 // will be removed and no longer called and this fix will simply stop
2463 // working. The only real danger would be if Apple changed the return type
2464 // to a struct or floating-point type, which would change the calling
2466 - (id) _displayChanged
2468 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2469 [queue postEvent:event];
2470 macdrv_release_event(event);
2472 return [super _displayChanged];
2475 - (BOOL) isExcludedFromWindowsMenu
2477 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2480 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2482 BOOL ret = [super validateMenuItem:menuItem];
2484 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2485 ret = [self isKeyWindow] || (!self.disabled && !self.noForeground);
2486 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2492 /* We don't call this. It's the action method of the items in the Window menu. */
2493 - (void) makeKeyAndOrderFront:(id)sender
2495 if ([self isMiniaturized])
2496 [self deminiaturize:nil];
2497 [self orderBelow:nil orAbove:nil activate:NO];
2498 [[self ancestorWineWindow] postBroughtForwardEvent];
2500 if (![self isKeyWindow] && !self.disabled && !self.noForeground)
2501 [[WineApplicationController sharedController] windowGotFocus:self];
2504 - (void) sendEvent:(NSEvent*)event
2506 NSEventType type = event.type;
2508 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2509 interface control. For example, Control-Tab switches focus among
2510 views. We want to bypass that feature, so directly route key-down
2511 events to -keyDown:. */
2512 if (type == NSEventTypeKeyDown)
2513 [[self firstResponder] keyDown:event];
2516 if (!draggingPhase && maximized && ![self isMovable] &&
2517 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2518 type == NSEventTypeLeftMouseDown && (self.styleMask & NSWindowStyleMaskTitled))
2520 NSRect titleBar = self.frame;
2521 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2522 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2523 titleBar.origin.y = NSMaxY(contentRect);
2525 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2527 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2529 static const NSWindowButton buttons[] = {
2530 NSWindowCloseButton,
2531 NSWindowMiniaturizeButton,
2533 NSWindowFullScreenButton,
2535 BOOL hitButton = NO;
2538 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2540 NSButton* button = [self standardWindowButton:buttons[i]];
2541 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2551 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2552 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2556 else if (draggingPhase && (type == NSEventTypeLeftMouseDragged || type == NSEventTypeLeftMouseUp))
2558 if ([self isMovable])
2560 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2561 NSPoint newTopLeft = dragWindowStartPosition;
2563 newTopLeft.x += point.x - dragStartPosition.x;
2564 newTopLeft.y += point.y - dragStartPosition.y;
2566 if (draggingPhase == 2)
2568 macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2569 mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2570 [queue postEvent:mevent];
2571 macdrv_release_event(mevent);
2576 [self setFrameTopLeftPoint:newTopLeft];
2578 else if (draggingPhase == 1 && type == NSEventTypeLeftMouseDragged)
2580 macdrv_event* event;
2581 NSRect frame = [self contentRectForFrameRect:self.frame];
2583 [[WineApplicationController sharedController] flipRect:&frame];
2585 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2586 event->window_restore_requested.keep_frame = TRUE;
2587 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2588 [queue postEvent:event];
2589 macdrv_release_event(event);
2594 if (type == NSEventTypeLeftMouseUp)
2595 [self endWindowDragging];
2598 [super sendEvent:event];
2602 - (void) miniaturize:(id)sender
2604 /* When Stage Manager is enabled, miniaturize: just moves the app/window to
2605 * the background rather than minimizing the window.
2606 * Don't start minimizing the window on the Win32 side.
2608 if (stage_manager_enabled())
2610 [super miniaturize:sender];
2614 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2615 [queue postEvent:event];
2616 macdrv_release_event(event);
2618 WineWindow* parent = (WineWindow*)self.parentWindow;
2619 if ([parent isKindOfClass:[WineWindow class]])
2620 [parent grabDockIconSnapshotFromWindow:self force:YES];
2623 - (void) toggleFullScreen:(id)sender
2625 if (!self.disabled && !maximized)
2626 [super toggleFullScreen:sender];
2629 - (void) setViewsNeedDisplay:(BOOL)value
2631 if (value && ![self viewsNeedDisplay])
2633 WineDisplayLink* link = [self wineDisplayLink];
2636 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2637 if (_lastDisplayTime + [link refreshPeriod] < now)
2638 [self setAutodisplay:YES];
2642 _lastDisplayTime = now;
2646 [self setAutodisplay:YES];
2648 [super setViewsNeedDisplay:value];
2653 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2656 [self setAutodisplay:NO];
2659 - (void) displayIfNeeded
2661 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2662 [super displayIfNeeded];
2664 [self setAutodisplay:NO];
2667 - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2670 [self setAutodisplay:YES];
2671 [super setFrame:frameRect display:flag];
2674 - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2677 [self setAutodisplay:YES];
2678 [super setFrame:frameRect display:displayFlag animate:animateFlag];
2681 - (void) windowDidDrawContent
2683 if (!drawnSinceShown)
2685 drawnSinceShown = YES;
2686 dispatch_async(dispatch_get_main_queue(), ^{
2687 [self checkTransparency];
2692 - (NSArray*) childWineWindows
2694 NSArray* childWindows = self.childWindows;
2695 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2696 return [child isKindOfClass:[WineWindow class]];
2698 return [childWindows objectsAtIndexes:indexes];
2701 - (void) updateForGLSubviews
2703 if (gl_surface_mode == GL_SURFACE_BEHIND)
2704 [self checkTransparency];
2707 - (void) setRetinaMode:(int)mode
2710 double scale = mode ? 0.5 : 2.0;
2711 NSAffineTransform* transform = [NSAffineTransform transform];
2713 [transform scaleBy:scale];
2715 [[self contentView] layer].mask.contentsScale = mode ? 2.0 : 1.0;
2717 for (WineBaseView* subview in [self.contentView subviews])
2719 if ([subview isKindOfClass:[WineBaseView class]])
2720 [subview setRetinaMode:mode];
2723 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2724 frame.origin.x *= scale;
2725 frame.origin.y *= scale;
2726 frame.size.width *= scale;
2727 frame.size.height *= scale;
2728 frame = [self frameRectForContentRect:frame];
2730 savedContentMinSize = [transform transformSize:savedContentMinSize];
2731 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2732 savedContentMaxSize.width *= scale;
2733 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2734 savedContentMaxSize.height *= scale;
2736 self.contentMinSize = [transform transformSize:self.contentMinSize];
2737 NSSize temp = self.contentMaxSize;
2738 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2739 temp.width *= scale;
2740 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2741 temp.height *= scale;
2742 self.contentMaxSize = temp;
2744 ignore_windowResize = TRUE;
2745 [self setFrameAndWineFrame:frame];
2746 ignore_windowResize = FALSE;
2751 * ---------- NSResponder method overrides ----------
2753 - (void) keyDown:(NSEvent *)theEvent
2755 if ([theEvent isARepeat])
2757 if (!allowKeyRepeats)
2761 allowKeyRepeats = YES;
2763 [self postKeyEvent:theEvent];
2766 - (void) flagsChanged:(NSEvent *)theEvent
2768 static const struct {
2772 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2773 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2774 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2775 { NX_DEVICELCTLKEYMASK, kVK_Control },
2776 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2777 { NX_DEVICELALTKEYMASK, kVK_Option },
2778 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2779 { NX_DEVICELCMDKEYMASK, kVK_Command },
2780 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2783 NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2785 int i, last_changed;
2787 fix_device_modifiers_by_generic(&modifierFlags);
2788 changed = modifierFlags ^ lastModifierFlags;
2791 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2792 if (changed & modifiers[i].mask)
2795 for (i = 0; i <= last_changed; i++)
2797 if (changed & modifiers[i].mask)
2799 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2802 allowKeyRepeats = NO;
2804 if (i == last_changed)
2805 lastModifierFlags = modifierFlags;
2808 lastModifierFlags ^= modifiers[i].mask;
2809 fix_generic_modifiers_by_device(&lastModifierFlags);
2812 // Caps lock generates one event for each press-release action.
2813 // We need to simulate a pair of events for each actual event.
2814 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2816 [self postKey:modifiers[i].keycode
2818 modifiers:lastModifierFlags
2819 event:(NSEvent*)theEvent];
2823 [self postKey:modifiers[i].keycode
2825 modifiers:lastModifierFlags
2826 event:(NSEvent*)theEvent];
2831 - (void) applicationWillHide
2833 savedVisibleState = [self isVisible];
2836 - (void) applicationDidUnhide
2838 if ([self isVisible])
2839 [self becameEligibleParentOrChild];
2844 * ---------- NSWindowDelegate methods ----------
2846 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2848 macdrv_query* query;
2851 query = macdrv_create_query();
2852 query->type = QUERY_MIN_MAX_INFO;
2853 query->window = (macdrv_window)[self retain];
2854 [self.queue query:query timeout:0.5];
2855 macdrv_release_query(query);
2857 size = [self contentMaxSize];
2858 if (proposedSize.width < size.width)
2859 size.width = proposedSize.width;
2860 if (proposedSize.height < size.height)
2861 size.height = proposedSize.height;
2865 - (void)windowDidBecomeKey:(NSNotification *)notification
2867 WineApplicationController* controller = [WineApplicationController sharedController];
2868 NSEvent* event = [controller lastFlagsChanged];
2870 [self flagsChanged:event];
2872 if (causing_becomeKeyWindow == self) return;
2874 [controller windowGotFocus:self];
2877 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2879 [self checkWineDisplayLink];
2882 - (void) windowDidChangeScreen:(NSNotification*)notification
2884 [self checkWineDisplayLink];
2887 - (void)windowDidDeminiaturize:(NSNotification *)notification
2889 WineApplicationController* controller = [WineApplicationController sharedController];
2891 if (!ignore_windowDeminiaturize)
2892 [self postDidUnminimizeEvent];
2893 ignore_windowDeminiaturize = FALSE;
2895 [self becameEligibleParentOrChild];
2897 if (fullscreen && [self isOnActiveSpace])
2898 [controller updateFullscreenWindows];
2899 [controller adjustWindowLevels];
2901 if (![self parentWindow])
2902 [self postBroughtForwardEvent];
2904 if (!self.disabled && !self.noForeground)
2906 causing_becomeKeyWindow = self;
2907 [self makeKeyWindow];
2908 causing_becomeKeyWindow = nil;
2909 [controller windowGotFocus:self];
2912 [self windowDidResize:notification];
2913 [self checkWineDisplayLink];
2916 - (void) windowDidEndLiveResize:(NSNotification *)notification
2920 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2921 [queue postEvent:event];
2922 macdrv_release_event(event);
2926 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2928 enteringFullScreen = FALSE;
2929 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2930 if (pendingOrderOut)
2934 - (void) windowDidExitFullScreen:(NSNotification*)notification
2936 exitingFullScreen = FALSE;
2937 [self setFrameAndWineFrame:nonFullscreenFrame];
2938 [self windowDidResize:nil];
2939 if (pendingOrderOut)
2943 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2945 enteringFullScreen = FALSE;
2946 enteredFullScreenTime = 0;
2947 if (pendingOrderOut)
2951 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2953 exitingFullScreen = FALSE;
2954 [self windowDidResize:nil];
2955 if (pendingOrderOut)
2959 - (void)windowDidMiniaturize:(NSNotification *)notification
2961 macdrv_event* event;
2963 if (fullscreen && [self isOnActiveSpace])
2964 [[WineApplicationController sharedController] updateFullscreenWindows];
2966 [self checkWineDisplayLink];
2968 event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
2969 [queue postEvent:event];
2970 macdrv_release_event(event);
2973 - (void)windowDidMove:(NSNotification *)notification
2975 [self windowDidResize:notification];
2978 - (void)windowDidResignKey:(NSNotification *)notification
2980 macdrv_event* event;
2982 if (causing_becomeKeyWindow) return;
2984 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2985 [queue postEvent:event];
2986 macdrv_release_event(event);
2989 - (void)windowDidResize:(NSNotification *)notification skipSizeMove:(BOOL)skipSizeMove
2991 NSRect frame = self.wine_fractionalFrame;
2993 if ([self inLiveResize])
2995 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2996 resizingFromLeft = TRUE;
2997 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2998 resizingFromTop = TRUE;
3001 if (ignore_windowResize || exitingFullScreen) return;
3003 if ([self preventResizing])
3005 NSRect contentRect = [self contentRectForFrameRect:frame];
3006 [self setContentMinSize:contentRect.size];
3007 [self setContentMaxSize:contentRect.size];
3010 [self postWindowFrameChanged:frame
3011 fullscreen:([self styleMask] & NSWindowStyleMaskFullScreen) != 0
3012 resizing:[self inLiveResize]
3013 skipSizeMove:skipSizeMove];
3015 [[[self contentView] inputContext] invalidateCharacterCoordinates];
3016 [self updateFullscreen];
3019 - (void)windowDidResize:(NSNotification *)notification
3021 [self windowDidResize:notification skipSizeMove:FALSE];
3024 - (BOOL)windowShouldClose:(id)sender
3026 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
3027 [queue postEvent:event];
3028 macdrv_release_event(event);
3032 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
3036 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3037 [queue postEvent:event];
3038 macdrv_release_event(event);
3041 else if (!resizable)
3043 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
3044 [queue postEvent:event];
3045 macdrv_release_event(event);
3052 - (void) windowWillClose:(NSNotification*)notification
3056 if (fakingClose) return;
3057 if (latentParentWindow)
3059 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
3060 self.latentParentWindow = nil;
3063 for (child in latentChildWindows)
3065 if (child.latentParentWindow == self)
3066 child.latentParentWindow = nil;
3068 [latentChildWindows removeAllObjects];
3071 - (void) windowWillEnterFullScreen:(NSNotification*)notification
3073 enteringFullScreen = TRUE;
3074 nonFullscreenFrame = self.wine_fractionalFrame;
3077 - (void) windowWillExitFullScreen:(NSNotification*)notification
3079 exitingFullScreen = TRUE;
3080 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE skipSizeMove:FALSE];
3083 - (void)windowWillMiniaturize:(NSNotification *)notification
3085 [self becameIneligibleParentOrChild];
3086 [self grabDockIconSnapshotFromWindow:nil force:NO];
3089 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
3091 if ([self inLiveResize])
3094 return self.wine_fractionalFrame.size;
3097 macdrv_query* query;
3099 rect = [self frame];
3100 if (resizingFromLeft)
3101 rect.origin.x = NSMaxX(rect) - frameSize.width;
3102 if (!resizingFromTop)
3103 rect.origin.y = NSMaxY(rect) - frameSize.height;
3104 rect.size = frameSize;
3105 rect = [self contentRectForFrameRect:rect];
3106 [[WineApplicationController sharedController] flipRect:&rect];
3108 query = macdrv_create_query();
3109 query->type = QUERY_RESIZE_SIZE;
3110 query->window = (macdrv_window)[self retain];
3111 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3112 query->resize_size.from_left = resizingFromLeft;
3113 query->resize_size.from_top = resizingFromTop;
3115 if ([self.queue query:query timeout:0.1])
3117 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3118 rect = [self frameRectForContentRect:rect];
3119 frameSize = rect.size;
3122 macdrv_release_query(query);
3128 - (void) windowWillStartLiveResize:(NSNotification *)notification
3130 [self endWindowDragging];
3134 macdrv_event* event;
3135 NSRect frame = [self contentRectForFrameRect:self.frame];
3137 [[WineApplicationController sharedController] flipRect:&frame];
3139 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3140 event->window_restore_requested.keep_frame = TRUE;
3141 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3142 [queue postEvent:event];
3143 macdrv_release_event(event);
3146 [self sendResizeStartQuery];
3148 frameAtResizeStart = [self frame];
3149 resizingFromLeft = resizingFromTop = FALSE;
3152 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3154 macdrv_query* query;
3155 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3158 query = macdrv_create_query();
3159 query->type = QUERY_MIN_MAX_INFO;
3160 query->window = (macdrv_window)[self retain];
3161 [self.queue query:query timeout:0.5];
3162 macdrv_release_query(query);
3164 currentContentRect = [self contentRectForFrameRect:[self frame]];
3165 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3167 maxSize = [self contentMaxSize];
3168 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3169 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3171 // Try to keep the top-left corner where it is.
3172 newContentRect.origin.x = NSMinX(currentContentRect);
3173 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3175 // If that pushes the bottom or right off the screen, pull it up and to the left.
3176 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3177 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3178 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3179 if (NSMinY(newContentRect) < NSMinY(screenRect))
3180 newContentRect.origin.y = NSMinY(screenRect);
3182 // If that pushes the top or left off the screen, push it down and the right
3183 // again. Do this last because the top-left corner is more important than the
3185 if (NSMinX(newContentRect) < NSMinX(screenRect))
3186 newContentRect.origin.x = NSMinX(screenRect);
3187 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3188 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3190 return [self frameRectForContentRect:newContentRect];
3195 * ---------- NSPasteboardOwner methods ----------
3197 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3199 macdrv_query* query = macdrv_create_query();
3200 query->type = QUERY_PASTEBOARD_DATA;
3201 query->window = (macdrv_window)[self retain];
3202 query->pasteboard_data.type = (CFStringRef)[type copy];
3204 [self.queue query:query timeout:3];
3205 macdrv_release_query(query);
3208 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3210 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3211 [queue postEvent:event];
3212 macdrv_release_event(event);
3217 * ---------- NSDraggingDestination methods ----------
3219 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3221 return [self draggingUpdated:sender];
3224 - (void) draggingExited:(id <NSDraggingInfo>)sender
3226 // This isn't really a query. We don't need any response. However, it
3227 // has to be processed in a similar manner as the other drag-and-drop
3228 // queries in order to maintain the proper order of operations.
3229 macdrv_query* query = macdrv_create_query();
3230 query->type = QUERY_DRAG_EXITED;
3231 query->window = (macdrv_window)[self retain];
3233 [self.queue query:query timeout:0.1];
3234 macdrv_release_query(query);
3237 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3239 NSDragOperation ret;
3240 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3241 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3242 NSPasteboard* pb = [sender draggingPasteboard];
3244 macdrv_query* query = macdrv_create_query();
3245 query->type = QUERY_DRAG_OPERATION;
3246 query->window = (macdrv_window)[self retain];
3247 query->drag_operation.x = floor(cgpt.x);
3248 query->drag_operation.y = floor(cgpt.y);
3249 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3250 query->drag_operation.accepted_op = NSDragOperationNone;
3251 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3253 [self.queue query:query timeout:3];
3254 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3255 macdrv_release_query(query);
3260 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3263 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3264 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3265 NSPasteboard* pb = [sender draggingPasteboard];
3267 macdrv_query* query = macdrv_create_query();
3268 query->type = QUERY_DRAG_DROP;
3269 query->window = (macdrv_window)[self retain];
3270 query->drag_drop.x = floor(cgpt.x);
3271 query->drag_drop.y = floor(cgpt.y);
3272 query->drag_drop.op = [sender draggingSourceOperationMask];
3273 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3275 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3276 ret = query->status;
3277 macdrv_release_query(query);
3282 - (BOOL) wantsPeriodicDraggingUpdates
3290 /***********************************************************************
3291 * macdrv_create_cocoa_window
3293 * Create a Cocoa window with the given content frame and features (e.g.
3294 * title bar, close box, etc.).
3296 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3297 CGRect frame, void* hwnd, macdrv_event_queue queue)
3299 __block WineWindow* window;
3302 window = [[WineWindow createWindowWithFeatures:wf
3303 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3305 queue:(WineEventQueue*)queue] retain];
3308 return (macdrv_window)window;
3311 /***********************************************************************
3312 * macdrv_destroy_cocoa_window
3314 * Destroy a Cocoa window.
3316 void macdrv_destroy_cocoa_window(macdrv_window w)
3320 WineWindow* window = (WineWindow*)w;
3323 window.closing = TRUE;
3324 [window doOrderOut];
3327 [window.queue discardEventsMatchingMask:-1 forWindow:window];
3332 /***********************************************************************
3333 * macdrv_get_window_hwnd
3335 * Get the hwnd that was set for the window at creation.
3337 void* macdrv_get_window_hwnd(macdrv_window w)
3339 WineWindow* window = (WineWindow*)w;
3343 /***********************************************************************
3344 * macdrv_set_cocoa_window_features
3346 * Update a Cocoa window's features.
3348 void macdrv_set_cocoa_window_features(macdrv_window w,
3349 const struct macdrv_window_features* wf)
3351 WineWindow* window = (WineWindow*)w;
3354 [window setWindowFeatures:wf];
3358 /***********************************************************************
3359 * macdrv_set_cocoa_window_state
3361 * Update a Cocoa window's state.
3363 void macdrv_set_cocoa_window_state(macdrv_window w,
3364 const struct macdrv_window_state* state)
3366 WineWindow* window = (WineWindow*)w;
3369 [window setMacDrvState:state];
3373 /***********************************************************************
3374 * macdrv_set_cocoa_window_title
3376 * Set a Cocoa window's title.
3378 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3383 WineWindow* window = (WineWindow*)w;
3384 NSString* titleString;
3387 titleString = [NSString stringWithCharacters:title length:length];
3390 OnMainThreadAsync(^{
3391 [window setTitle:titleString];
3392 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3393 [NSApp changeWindowsItem:window title:titleString filename:NO];
3398 /***********************************************************************
3399 * macdrv_order_cocoa_window
3401 * Reorder a Cocoa window relative to other windows. If prev is
3402 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3403 * it is ordered above that window. Otherwise, it is ordered to the
3406 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3407 macdrv_window n, int activate)
3409 WineWindow* window = (WineWindow*)w;
3410 WineWindow* prev = (WineWindow*)p;
3411 WineWindow* next = (WineWindow*)n;
3413 OnMainThreadAsync(^{
3414 [window orderBelow:prev
3418 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3420 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3424 /***********************************************************************
3425 * macdrv_hide_cocoa_window
3427 * Hides a Cocoa window.
3429 void macdrv_hide_cocoa_window(macdrv_window w)
3431 WineWindow* window = (WineWindow*)w;
3434 [window doOrderOut];
3438 /***********************************************************************
3439 * macdrv_set_cocoa_window_frame
3441 * Move a Cocoa window.
3443 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3445 WineWindow* window = (WineWindow*)w;
3448 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3452 /***********************************************************************
3453 * macdrv_get_cocoa_window_frame
3455 * Gets the frame of a Cocoa window.
3457 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3459 WineWindow* window = (WineWindow*)w;
3464 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3465 [[WineApplicationController sharedController] flipRect:&frame];
3466 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3470 /***********************************************************************
3471 * macdrv_set_cocoa_parent_window
3473 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3474 * the parent window.
3476 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3478 WineWindow* window = (WineWindow*)w;
3481 [window setMacDrvParentWindow:(WineWindow*)parent];
3485 /***********************************************************************
3486 * macdrv_set_window_surface
3488 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3492 WineWindow* window = (WineWindow*)w;
3495 window.surface = surface;
3496 window.surface_mutex = mutex;
3501 /***********************************************************************
3502 * macdrv_window_needs_display
3504 * Mark a window as needing display in a specified rect (in non-client
3505 * area coordinates).
3507 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3511 WineWindow* window = (WineWindow*)w;
3513 OnMainThreadAsync(^{
3514 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3519 /***********************************************************************
3520 * macdrv_set_window_shape
3522 * Sets the shape of a Cocoa window from an array of rectangles. If
3523 * rects is NULL, resets the window's shape to its frame.
3525 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3529 WineWindow* window = (WineWindow*)w;
3532 if (!rects || !count)
3534 [window setShape:NULL];
3535 [window checkEmptyShaped];
3539 CGMutablePathRef path;
3542 path = CGPathCreateMutable();
3543 for (i = 0; i < count; i++)
3544 CGPathAddRect(path, NULL, cgrect_mac_from_win(rects[i]));
3545 [window setShape:path];
3546 CGPathRelease(path);
3552 /***********************************************************************
3553 * macdrv_set_window_alpha
3555 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3559 WineWindow* window = (WineWindow*)w;
3561 [window setAlphaValue:alpha];
3565 /***********************************************************************
3566 * macdrv_set_window_color_key
3568 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3573 WineWindow* window = (WineWindow*)w;
3576 window.colorKeyed = TRUE;
3577 window.colorKeyRed = keyRed;
3578 window.colorKeyGreen = keyGreen;
3579 window.colorKeyBlue = keyBlue;
3580 [window checkTransparency];
3585 /***********************************************************************
3586 * macdrv_clear_window_color_key
3588 void macdrv_clear_window_color_key(macdrv_window w)
3592 WineWindow* window = (WineWindow*)w;
3595 window.colorKeyed = FALSE;
3596 [window checkTransparency];
3601 /***********************************************************************
3602 * macdrv_window_use_per_pixel_alpha
3604 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3608 WineWindow* window = (WineWindow*)w;
3611 window.usePerPixelAlpha = use_per_pixel_alpha;
3612 [window checkTransparency];
3617 /***********************************************************************
3618 * macdrv_give_cocoa_window_focus
3620 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3621 * orders it front and, if its frame was not within the desktop bounds,
3622 * Cocoa will typically move it on-screen.
3624 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3626 WineWindow* window = (WineWindow*)w;
3629 [window makeFocused:activate];
3633 /***********************************************************************
3634 * macdrv_set_window_min_max_sizes
3636 * Sets the window's minimum and maximum content sizes.
3638 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3640 WineWindow* window = (WineWindow*)w;
3643 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3647 /***********************************************************************
3648 * macdrv_create_view
3650 * Creates and returns a view with the specified frame rect. The
3651 * caller is responsible for calling macdrv_dispose_view() on the view
3652 * when it is done with it.
3654 macdrv_view macdrv_create_view(CGRect rect)
3658 __block WineContentView* view;
3660 if (CGRectIsNull(rect)) rect = CGRectZero;
3663 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3665 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3666 [view setAutoresizingMask:NSViewNotSizable];
3667 [view setHidden:YES];
3668 [view setWantsBestResolutionOpenGLSurface:retina_on];
3669 [nc addObserver:view
3670 selector:@selector(updateGLContexts)
3671 name:NSViewGlobalFrameDidChangeNotification
3673 [nc addObserver:view
3674 selector:@selector(updateGLContexts)
3675 name:NSApplicationDidChangeScreenParametersNotification
3679 return (macdrv_view)view;
3683 /***********************************************************************
3684 * macdrv_dispose_view
3686 * Destroys a view previously returned by macdrv_create_view.
3688 void macdrv_dispose_view(macdrv_view v)
3692 WineContentView* view = (WineContentView*)v;
3695 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3696 WineWindow* window = (WineWindow*)[view window];
3698 [nc removeObserver:view
3699 name:NSViewGlobalFrameDidChangeNotification
3701 [nc removeObserver:view
3702 name:NSApplicationDidChangeScreenParametersNotification
3704 [view removeFromSuperview];
3706 [window updateForGLSubviews];
3711 /***********************************************************************
3712 * macdrv_set_view_frame
3714 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3718 WineContentView* view = (WineContentView*)v;
3720 if (CGRectIsNull(rect)) rect = CGRectZero;
3722 OnMainThreadAsync(^{
3723 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3724 NSRect oldFrame = [view frame];
3726 if (!NSEqualRects(oldFrame, newFrame))
3728 [[view superview] setNeedsDisplayInRect:oldFrame];
3729 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3730 [view setFrameSize:newFrame.size];
3731 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3732 [view setFrameOrigin:newFrame.origin];
3734 [view setFrame:newFrame];
3735 [view setNeedsDisplay:YES];
3739 int backing_size[2] = { 0 };
3740 [view wine_setBackingSize:backing_size];
3742 [(WineWindow*)[view window] updateForGLSubviews];
3748 /***********************************************************************
3749 * macdrv_set_view_superview
3751 * Move a view to a new superview and position it relative to its
3752 * siblings. If p is non-NULL, the view is ordered behind it.
3753 * Otherwise, the view is ordered above n. If s is NULL, use the
3754 * content view of w as the new superview.
3756 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3760 WineContentView* view = (WineContentView*)v;
3761 WineContentView* superview = (WineContentView*)s;
3762 WineWindow* window = (WineWindow*)w;
3763 WineContentView* prev = (WineContentView*)p;
3764 WineContentView* next = (WineContentView*)n;
3767 superview = [window contentView];
3769 OnMainThreadAsync(^{
3770 if (superview == [view superview])
3772 NSArray* subviews = [superview subviews];
3773 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3774 if (!prev && !next && index == [subviews count] - 1)
3776 if (prev && index + 1 < [subviews count] && subviews[index + 1] == prev)
3778 if (!prev && next && index > 0 && subviews[index - 1] == next)
3782 WineWindow* oldWindow = (WineWindow*)[view window];
3783 WineWindow* newWindow = (WineWindow*)[superview window];
3785 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3786 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3787 [view removeFromSuperview];
3790 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3792 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3794 if (oldWindow != newWindow)
3796 [oldWindow updateForGLSubviews];
3797 [newWindow updateForGLSubviews];
3803 /***********************************************************************
3804 * macdrv_set_view_hidden
3806 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3810 WineContentView* view = (WineContentView*)v;
3812 OnMainThreadAsync(^{
3813 [view setHidden:hidden];
3814 [(WineWindow*)view.window updateForGLSubviews];
3819 /***********************************************************************
3820 * macdrv_add_view_opengl_context
3822 * Add an OpenGL context to the list being tracked for each view.
3824 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3828 WineContentView* view = (WineContentView*)v;
3829 WineOpenGLContext *context = (WineOpenGLContext*)c;
3832 [view addGLContext:context];
3837 /***********************************************************************
3838 * macdrv_remove_view_opengl_context
3840 * Add an OpenGL context to the list being tracked for each view.
3842 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3846 WineContentView* view = (WineContentView*)v;
3847 WineOpenGLContext *context = (WineOpenGLContext*)c;
3849 OnMainThreadAsync(^{
3850 [view removeGLContext:context];
3855 macdrv_metal_device macdrv_create_metal_device(void)
3859 macdrv_metal_device ret;
3861 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3862 if (MTLCreateSystemDefaultDevice == NULL)
3866 ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3871 void macdrv_release_metal_device(macdrv_metal_device d)
3875 [(id<MTLDevice>)d release];
3879 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3881 id<MTLDevice> device = (id<MTLDevice>)d;
3882 WineContentView* view = (WineContentView*)v;
3883 __block WineMetalView *metalView;
3886 metalView = [view newMetalViewWithDevice:device];
3889 return (macdrv_metal_view)metalView;
3892 macdrv_metal_layer macdrv_view_get_metal_layer(macdrv_metal_view v)
3894 WineMetalView* view = (WineMetalView*)v;
3895 __block CAMetalLayer* layer;
3898 layer = (CAMetalLayer*)view.layer;
3901 return (macdrv_metal_layer)layer;
3904 void macdrv_view_release_metal_view(macdrv_metal_view v)
3906 WineMetalView* view = (WineMetalView*)v;
3908 [view removeFromSuperview];
3913 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3915 WineContentView* view = (WineContentView*)v;
3917 if (![view isKindOfClass:[WineContentView class]])
3920 [view wine_getBackingSize:backing_size];
3924 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3926 WineContentView* view = (WineContentView*)v;
3928 if ([view isKindOfClass:[WineContentView class]])
3929 [view wine_setBackingSize:backing_size];
3932 /***********************************************************************
3933 * macdrv_window_background_color
3935 * Returns the standard Mac window background color as a 32-bit value of
3936 * the form 0x00rrggbb.
3938 uint32_t macdrv_window_background_color(void)
3940 static uint32_t result;
3941 static dispatch_once_t once;
3943 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3944 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3945 // of it is to draw with it.
3946 dispatch_once(&once, ^{
3948 unsigned char rgbx[4];
3949 unsigned char *planes = rgbx;
3950 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3957 colorSpaceName:NSCalibratedRGBColorSpace
3961 [NSGraphicsContext saveGraphicsState];
3962 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3963 [[NSColor windowBackgroundColor] set];
3964 NSRectFill(NSMakeRect(0, 0, 1, 1));
3965 [NSGraphicsContext restoreGraphicsState];
3967 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3974 /***********************************************************************
3975 * macdrv_send_text_input_event
3977 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* himc, int* done)
3979 OnMainThreadAsync(^{
3981 macdrv_event* event;
3982 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3983 if (![window isKindOfClass:[WineWindow class]])
3985 window = (WineWindow*)[NSApp mainWindow];
3986 if (![window isKindOfClass:[WineWindow class]])
3987 window = [[WineApplicationController sharedController] frontWineWindow];
3992 NSUInteger localFlags = flags;
3997 fix_device_modifiers_by_generic(&localFlags);
3999 // An NSEvent created with +keyEventWithType:... is internally marked
4000 // as synthetic and doesn't get sent through input methods. But one
4001 // created from a CGEvent doesn't have that problem.
4002 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
4003 CGEventSetFlags(c, localFlags);
4004 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
4005 event = [NSEvent eventWithCGEvent:c];
4008 window.commandDone = FALSE;
4009 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
4014 event = macdrv_create_event(SENT_TEXT_INPUT, window);
4015 event->sent_text_input.handled = ret;
4016 event->sent_text_input.done = done;
4017 [[window queue] postEvent:event];
4018 macdrv_release_event(event);
4022 void macdrv_clear_ime_text(void)
4024 OnMainThreadAsync(^{
4025 WineWindow* window = (WineWindow*)[NSApp keyWindow];
4026 if (![window isKindOfClass:[WineWindow class]])
4028 window = (WineWindow*)[NSApp mainWindow];
4029 if (![window isKindOfClass:[WineWindow class]])
4030 window = [[WineApplicationController sharedController] frontWineWindow];
4033 [[window contentView] clearMarkedText];