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 #ifdef HAVE_METAL_METAL_H
27 #import <Metal/Metal.h>
28 #import <QuartzCore/QuartzCore.h>
31 #import "cocoa_window.h"
33 #include "macdrv_cocoa.h"
35 #import "cocoa_event.h"
36 #import "cocoa_opengl.h"
39 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
40 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
42 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
47 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
49 NSUInteger style_mask;
53 style_mask = NSWindowStyleMaskTitled;
54 if (wf->close_button) style_mask |= NSWindowStyleMaskClosable;
55 if (wf->minimize_button) style_mask |= NSWindowStyleMaskMiniaturizable;
56 if (wf->resizable || wf->maximize_button) style_mask |= NSWindowStyleMaskResizable;
57 if (wf->utility) style_mask |= NSWindowStyleMaskUtilityWindow;
59 else style_mask = NSWindowStyleMaskBorderless;
65 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
68 for (screen in screens)
70 if (NSIntersectsRect(frame, [screen frame]))
77 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
79 for (NSScreen* screen in screens)
81 if (NSContainsRect(rect, [screen frame]))
88 /* We rely on the supposedly device-dependent modifier flags to distinguish the
89 keys on the left side of the keyboard from those on the right. Some event
90 sources don't set those device-depdendent flags. If we see a device-independent
91 flag for a modifier without either corresponding device-dependent flag, assume
93 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
95 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
96 *modifiers |= NX_DEVICELCMDKEYMASK;
97 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
98 *modifiers |= NX_DEVICELSHIFTKEYMASK;
99 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
100 *modifiers |= NX_DEVICELCTLKEYMASK;
101 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
102 *modifiers |= NX_DEVICELALTKEYMASK;
105 /* As we manipulate individual bits of a modifier mask, we can end up with
106 inconsistent sets of flags. In particular, we might set or clear one of the
107 left/right-specific bits, but not the corresponding non-side-specific bit.
108 Fix that. If either side-specific bit is set, set the non-side-specific bit,
109 otherwise clear it. */
110 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
112 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
113 *modifiers |= NX_COMMANDMASK;
115 *modifiers &= ~NX_COMMANDMASK;
116 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
117 *modifiers |= NX_SHIFTMASK;
119 *modifiers &= ~NX_SHIFTMASK;
120 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
121 *modifiers |= NX_CONTROLMASK;
123 *modifiers &= ~NX_CONTROLMASK;
124 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
125 *modifiers |= NX_ALTERNATEMASK;
127 *modifiers &= ~NX_ALTERNATEMASK;
130 static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
132 fix_device_modifiers_by_generic(&modifiers);
133 NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
134 NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
136 // The MACDRV keyboard driver translates Command keys to Alt. If the
137 // Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
138 // Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
139 if (modifiers & NX_DEVICELALTKEYMASK)
140 new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
141 if (modifiers & NX_DEVICERALTKEYMASK)
142 new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
144 if (modifiers & NX_DEVICELCMDKEYMASK)
145 new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
146 if (modifiers & NX_DEVICERCMDKEYMASK)
147 new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
149 fix_generic_modifiers_by_device(&new_modifiers);
150 return new_modifiers;
154 @interface NSWindow (WineAccessPrivateMethods)
155 - (id) _displayChanged;
159 @interface WineDisplayLink : NSObject
161 CGDirectDisplayID _displayID;
162 CVDisplayLinkRef _link;
163 NSMutableSet* _windows;
165 NSTimeInterval _actualRefreshPeriod;
166 NSTimeInterval _nominalRefreshPeriod;
168 NSTimeInterval _lastDisplayTime;
171 - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
173 - (void) addWindow:(WineWindow*)window;
174 - (void) removeWindow:(WineWindow*)window;
176 - (NSTimeInterval) refreshPeriod;
182 @implementation WineDisplayLink
184 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
186 - (id) initWithDisplayID:(CGDirectDisplayID)displayID
191 CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
192 if (status == kCVReturnSuccess && !_link)
193 status = kCVReturnError;
194 if (status == kCVReturnSuccess)
195 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
196 if (status != kCVReturnSuccess)
202 _displayID = displayID;
203 _windows = [[NSMutableSet alloc] init];
212 CVDisplayLinkStop(_link);
213 CVDisplayLinkRelease(_link);
219 - (void) addWindow:(WineWindow*)window
222 @synchronized(self) {
223 firstWindow = !_windows.count;
224 [_windows addObject:window];
226 if (firstWindow || !CVDisplayLinkIsRunning(_link))
230 - (void) removeWindow:(WineWindow*)window
232 BOOL lastWindow = FALSE;
233 @synchronized(self) {
234 BOOL hadWindows = _windows.count > 0;
235 [_windows removeObject:window];
236 if (hadWindows && !_windows.count)
239 if (lastWindow && CVDisplayLinkIsRunning(_link))
240 CVDisplayLinkStop(_link);
246 @synchronized(self) {
247 windows = [_windows copy];
249 dispatch_async(dispatch_get_main_queue(), ^{
250 BOOL anyDisplayed = FALSE;
251 for (WineWindow* window in windows)
253 if ([window viewsNeedDisplay])
255 [window displayIfNeeded];
260 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
262 _lastDisplayTime = now;
263 else if (_lastDisplayTime + 2.0 < now)
264 CVDisplayLinkStop(_link);
269 - (NSTimeInterval) refreshPeriod
271 if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
272 return _actualRefreshPeriod;
274 if (_nominalRefreshPeriod)
275 return _nominalRefreshPeriod;
277 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
278 if (time.flags & kCVTimeIsIndefinite)
280 _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
281 return _nominalRefreshPeriod;
286 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
287 CVDisplayLinkStart(_link);
290 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
292 WineDisplayLink* link = displayLinkContext;
294 return kCVReturnSuccess;
300 #ifndef MAC_OS_X_VERSION_10_14
301 @protocol NSViewLayerContentScaleDelegate <NSObject>
304 - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window;
310 @interface CAShapeLayer (WineShapeMaskExtensions)
312 @property(readonly, nonatomic, getter=isEmptyShaped) BOOL emptyShaped;
316 @implementation CAShapeLayer (WineShapeMaskExtensions)
318 - (BOOL) isEmptyShaped
320 return CGRectEqualToRect(CGPathGetBoundingBox(self.path), CGRectZero);
326 @interface WineBaseView : NSView
330 #ifdef HAVE_METAL_METAL_H
331 @interface WineMetalView : WineBaseView
333 id<MTLDevice> _device;
336 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
342 @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContentScaleDelegate>
344 NSMutableArray* glContexts;
345 NSMutableArray* pendingGlContexts;
346 BOOL _everHadGLContext;
347 BOOL _cachedHasGLDescendant;
348 BOOL _cachedHasGLDescendantValid;
349 BOOL clearedGlSurface;
351 NSMutableAttributedString* markedText;
352 NSRange markedTextSelection;
357 #ifdef HAVE_METAL_METAL_H
358 WineMetalView *_metalView;
362 @property (readonly, nonatomic) BOOL everHadGLContext;
364 - (void) addGLContext:(WineOpenGLContext*)context;
365 - (void) removeGLContext:(WineOpenGLContext*)context;
366 - (void) updateGLContexts;
368 - (void) wine_getBackingSize:(int*)outBackingSize;
369 - (void) wine_setBackingSize:(const int*)newBackingSize;
371 #ifdef HAVE_METAL_METAL_H
372 - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
378 @interface WineWindow ()
380 @property (readwrite, nonatomic) BOOL disabled;
381 @property (readwrite, nonatomic) BOOL noActivate;
382 @property (readwrite, nonatomic) BOOL floating;
383 @property (readwrite, nonatomic) BOOL drawnSinceShown;
384 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
385 @property (retain, nonatomic) NSWindow* latentParentWindow;
387 @property (nonatomic) void* hwnd;
388 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
390 @property (nonatomic) void* surface;
391 @property (nonatomic) pthread_mutex_t* surface_mutex;
393 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
394 @property (readonly, nonatomic) BOOL needsTransparency;
396 @property (nonatomic) BOOL colorKeyed;
397 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
398 @property (nonatomic) BOOL usePerPixelAlpha;
400 @property (assign, nonatomic) void* imeData;
401 @property (nonatomic) BOOL commandDone;
403 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
405 - (void) setShape:(CGPathRef)newShape;
407 - (void) updateForGLSubviews;
409 - (BOOL) becameEligibleParentOrChild;
410 - (void) becameIneligibleChild;
412 - (void) windowDidDrawContent;
417 @implementation WineBaseView
419 - (void) setRetinaMode:(int)mode
421 for (WineBaseView* subview in [self subviews])
423 if ([subview isKindOfClass:[WineBaseView class]])
424 [subview setRetinaMode:mode];
428 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
433 - (BOOL) preservesContentDuringLiveResize
435 // Returning YES from this tells Cocoa to keep our view's content during
436 // a Cocoa-driven resize. In theory, we're also supposed to override
437 // -setFrameSize: to mark exposed sections as needing redisplay, but
438 // user32 will take care of that in a roundabout way. This way, we don't
439 // redraw until the window surface is flushed.
441 // This doesn't do anything when we resize the window ourselves.
445 - (BOOL)acceptsFirstResponder
447 return [[self window] contentView] == self;
450 - (BOOL) mouseDownCanMoveWindow
455 - (NSFocusRingType) focusRingType
457 return NSFocusRingTypeNone;
463 @implementation WineContentView
465 @synthesize everHadGLContext = _everHadGLContext;
469 [markedText release];
470 [glContexts release];
471 [pendingGlContexts release];
480 - (BOOL) wantsUpdateLayer
482 return YES /*!_everHadGLContext*/;
487 WineWindow* window = (WineWindow*)[self window];
488 CGImageRef image = NULL;
490 CALayer* layer = [self layer];
492 if ([window contentView] != self)
495 if (!window.surface || !window.surface_mutex)
498 pthread_mutex_lock(window.surface_mutex);
499 if (get_surface_blit_rects(window.surface, NULL, NULL))
501 imageRect = layer.bounds;
502 imageRect.origin.x *= layer.contentsScale;
503 imageRect.origin.y *= layer.contentsScale;
504 imageRect.size.width *= layer.contentsScale;
505 imageRect.size.height *= layer.contentsScale;
506 image = create_surface_image(window.surface, &imageRect, FALSE, window.colorKeyed,
507 window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue);
509 pthread_mutex_unlock(window.surface_mutex);
513 layer.contents = (id)image;
515 [window windowDidDrawContent];
517 // If the window may be transparent, then we have to invalidate the
518 // shadow every time we draw. Also, if this is the first time we've
519 // drawn since changing from transparent to opaque.
520 if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
522 window.shapeChangedSinceLastDraw = FALSE;
523 [window invalidateShadow];
528 - (void) viewWillDraw
530 [super viewWillDraw];
532 for (WineOpenGLContext* context in pendingGlContexts)
534 if (!clearedGlSurface)
536 context.shouldClearToBlack = TRUE;
537 clearedGlSurface = TRUE;
539 context.needsUpdate = TRUE;
541 [glContexts addObjectsFromArray:pendingGlContexts];
542 [pendingGlContexts removeAllObjects];
545 - (void) addGLContext:(WineOpenGLContext*)context
547 BOOL hadContext = _everHadGLContext;
549 glContexts = [[NSMutableArray alloc] init];
550 if (!pendingGlContexts)
551 pendingGlContexts = [[NSMutableArray alloc] init];
553 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
555 [glContexts addObject:context];
556 if (!clearedGlSurface)
558 context.shouldClearToBlack = TRUE;
559 clearedGlSurface = TRUE;
561 context.needsUpdate = TRUE;
565 [pendingGlContexts addObject:context];
566 [self setNeedsDisplay:YES];
569 _everHadGLContext = YES;
571 [self invalidateHasGLDescendant];
572 [(WineWindow*)[self window] updateForGLSubviews];
575 - (void) removeGLContext:(WineOpenGLContext*)context
577 [glContexts removeObjectIdenticalTo:context];
578 [pendingGlContexts removeObjectIdenticalTo:context];
579 [(WineWindow*)[self window] updateForGLSubviews];
582 - (void) updateGLContexts:(BOOL)reattach
584 for (WineOpenGLContext* context in glContexts)
586 context.needsUpdate = TRUE;
588 context.needsReattach = TRUE;
592 - (void) updateGLContexts
594 [self updateGLContexts:NO];
597 - (BOOL) _hasGLDescendant
601 if (_everHadGLContext)
603 for (WineContentView* view in [self subviews])
605 if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
611 - (BOOL) hasGLDescendant
613 if (!_cachedHasGLDescendantValid)
615 _cachedHasGLDescendant = [self _hasGLDescendant];
616 _cachedHasGLDescendantValid = YES;
618 return _cachedHasGLDescendant;
621 - (void) invalidateHasGLDescendant
623 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
624 _cachedHasGLDescendantValid = NO;
625 if (invalidateAncestors && self != [[self window] contentView])
627 WineContentView* superview = (WineContentView*)[self superview];
628 if ([superview isKindOfClass:[WineContentView class]])
629 [superview invalidateHasGLDescendant];
633 - (void) wine_getBackingSize:(int*)outBackingSize
635 @synchronized(self) {
636 memcpy(outBackingSize, backingSize, sizeof(backingSize));
639 - (void) wine_setBackingSize:(const int*)newBackingSize
641 @synchronized(self) {
642 memcpy(backingSize, newBackingSize, sizeof(backingSize));
646 #ifdef HAVE_METAL_METAL_H
647 - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
649 if (_metalView) return _metalView;
651 WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
652 [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
653 [self setAutoresizesSubviews:YES];
654 [self addSubview:view positioned:NSWindowBelow relativeTo:nil];
657 [(WineWindow*)self.window windowDidDrawContent];
663 - (void) setRetinaMode:(int)mode
665 double scale = mode ? 0.5 : 2.0;
666 NSRect frame = self.frame;
667 frame.origin.x *= scale;
668 frame.origin.y *= scale;
669 frame.size.width *= scale;
670 frame.size.height *= scale;
671 [self setFrame:frame];
672 [self setWantsBestResolutionOpenGLSurface:mode];
673 [self updateGLContexts];
675 _retinaMode = !!mode;
676 [self layer].contentsScale = mode ? 2.0 : 1.0;
677 [self layer].minificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
678 [self layer].magnificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
679 [super setRetinaMode:mode];
682 - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window
684 return (_retinaMode || newScale == 1.0);
690 [self invalidateHasGLDescendant];
693 - (void) viewDidUnhide
695 [super viewDidUnhide];
696 [self updateGLContexts:YES];
697 [self invalidateHasGLDescendant];
700 - (void) clearMarkedText
702 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
703 markedTextSelection = NSMakeRange(0, 0);
704 [[self inputContext] discardMarkedText];
707 - (void) completeText:(NSString*)text
710 WineWindow* window = (WineWindow*)[self window];
712 event = macdrv_create_event(IM_SET_TEXT, window);
713 event->im_set_text.data = [window imeData];
714 event->im_set_text.text = (CFStringRef)[text copy];
715 event->im_set_text.complete = TRUE;
717 [[window queue] postEvent:event];
719 macdrv_release_event(event);
721 [self clearMarkedText];
724 - (void) didAddSubview:(NSView*)subview
726 if ([subview isKindOfClass:[WineContentView class]])
728 WineContentView* view = (WineContentView*)subview;
729 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
730 [self invalidateHasGLDescendant];
732 [super didAddSubview:subview];
735 - (void) willRemoveSubview:(NSView*)subview
737 if ([subview isKindOfClass:[WineContentView class]])
739 WineContentView* view = (WineContentView*)subview;
740 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
741 [self invalidateHasGLDescendant];
743 #ifdef HAVE_METAL_METAL_H
744 if (subview == _metalView)
747 [super willRemoveSubview:subview];
750 - (void) setLayer:(CALayer*)newLayer
752 [super setLayer:newLayer];
753 [self updateGLContexts];
757 * ---------- NSTextInputClient methods ----------
759 - (NSTextInputContext*) inputContext
762 markedText = [[NSMutableAttributedString alloc] init];
763 return [super inputContext];
766 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
768 if ([string isKindOfClass:[NSAttributedString class]])
769 string = [string string];
771 if ([string isKindOfClass:[NSString class]])
772 [self completeText:string];
775 - (void) doCommandBySelector:(SEL)aSelector
777 [(WineWindow*)[self window] setCommandDone:TRUE];
780 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
782 if ([string isKindOfClass:[NSAttributedString class]])
783 string = [string string];
785 if ([string isKindOfClass:[NSString class]])
788 WineWindow* window = (WineWindow*)[self window];
790 if (replacementRange.location == NSNotFound)
791 replacementRange = NSMakeRange(0, [markedText length]);
793 [markedText replaceCharactersInRange:replacementRange withString:string];
794 markedTextSelection = selectedRange;
795 markedTextSelection.location += replacementRange.location;
797 event = macdrv_create_event(IM_SET_TEXT, window);
798 event->im_set_text.data = [window imeData];
799 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
800 event->im_set_text.complete = FALSE;
801 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
803 [[window queue] postEvent:event];
805 macdrv_release_event(event);
807 [[self inputContext] invalidateCharacterCoordinates];
813 [self completeText:nil];
816 - (NSRange) selectedRange
818 return markedTextSelection;
821 - (NSRange) markedRange
823 NSRange range = NSMakeRange(0, [markedText length]);
825 range.location = NSNotFound;
829 - (BOOL) hasMarkedText
831 return [markedText length] > 0;
834 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
836 if (aRange.location >= [markedText length])
839 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
841 *actualRange = aRange;
842 return [markedText attributedSubstringFromRange:aRange];
845 - (NSArray*) validAttributesForMarkedText
847 return [NSArray array];
850 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
853 WineWindow* window = (WineWindow*)[self window];
856 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
858 query = macdrv_create_query();
859 query->type = QUERY_IME_CHAR_RECT;
860 query->window = (macdrv_window)[window retain];
861 query->ime_char_rect.data = [window imeData];
862 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
864 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
866 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
867 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
868 [[WineApplicationController sharedController] flipRect:&ret];
871 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
873 macdrv_release_query(query);
876 *actualRange = aRange;
880 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
885 - (NSInteger) windowLevel
887 return [[self window] level];
893 #ifdef HAVE_METAL_METAL_H
894 @implementation WineMetalView
896 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
898 self = [super initWithFrame:frame];
901 _device = [device retain];
902 self.wantsLayer = YES;
903 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
914 - (void) setRetinaMode:(int)mode
916 self.layer.contentsScale = mode ? 2.0 : 1.0;
917 [super setRetinaMode:mode];
920 - (CALayer*) makeBackingLayer
922 CAMetalLayer *layer = [CAMetalLayer layer];
923 layer.device = _device;
924 layer.framebufferOnly = YES;
925 layer.magnificationFilter = kCAFilterNearest;
926 layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
927 layer.contentsScale = retina_on ? 2.0 : 1.0;
940 @implementation WineWindow
942 static WineWindow* causing_becomeKeyWindow;
944 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
945 @synthesize drawnSinceShown;
946 @synthesize surface, surface_mutex;
947 @synthesize shapeChangedSinceLastDraw;
948 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
949 @synthesize usePerPixelAlpha;
950 @synthesize imeData, commandDone;
952 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
953 windowFrame:(NSRect)window_frame
955 queue:(WineEventQueue*)queue
958 WineContentView* contentView;
959 NSTrackingArea* trackingArea;
960 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
962 [[WineApplicationController sharedController] flipRect:&window_frame];
964 window = [[[self alloc] initWithContentRect:window_frame
965 styleMask:style_mask_for_features(wf)
966 backing:NSBackingStoreBuffered
967 defer:YES] autorelease];
969 if (!window) return nil;
971 /* Standardize windows to eliminate differences between titled and
972 borderless windows and between NSWindow and NSPanel. */
973 [window setHidesOnDeactivate:NO];
974 [window setReleasedWhenClosed:NO];
976 [window setOneShot:YES];
977 [window disableCursorRects];
978 [window setShowsResizeIndicator:NO];
979 [window setHasShadow:wf->shadow];
980 [window setAcceptsMouseMovedEvents:YES];
981 [window setDelegate:window];
982 [window setBackgroundColor:[NSColor clearColor]];
983 [window setOpaque:NO];
985 window.queue = queue;
986 window->savedContentMinSize = NSZeroSize;
987 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
988 window->resizable = wf->resizable;
989 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
991 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
992 (NSString*)kUTTypeContent,
995 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
998 [contentView setWantsLayer:YES];
999 [contentView layer].minificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
1000 [contentView layer].magnificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
1001 [contentView layer].contentsScale = retina_on ? 2.0 : 1.0;
1002 [contentView setAutoresizesSubviews:NO];
1004 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
1005 because they give us mouse moves in the background. */
1006 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
1007 options:(NSTrackingMouseMoved |
1008 NSTrackingActiveAlways |
1009 NSTrackingInVisibleRect)
1011 userInfo:nil] autorelease];
1014 [contentView addTrackingArea:trackingArea];
1016 [window setContentView:contentView];
1017 [window setInitialFirstResponder:contentView];
1019 [nc addObserver:window
1020 selector:@selector(updateFullscreen)
1021 name:NSApplicationDidChangeScreenParametersNotification
1023 [window updateFullscreen];
1025 [nc addObserver:window
1026 selector:@selector(applicationWillHide)
1027 name:NSApplicationWillHideNotification
1029 [nc addObserver:window
1030 selector:@selector(applicationDidUnhide)
1031 name:NSApplicationDidUnhideNotification
1034 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1035 selector:@selector(checkWineDisplayLink)
1036 name:NSWorkspaceActiveSpaceDidChangeNotification
1037 object:[NSWorkspace sharedWorkspace]];
1039 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1046 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1047 [[NSNotificationCenter defaultCenter] removeObserver:self];
1049 [latentChildWindows release];
1050 [latentParentWindow release];
1054 - (BOOL) preventResizing
1056 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1057 return ([self styleMask] & NSWindowStyleMaskResizable) && (disabled || !resizable || preventForClipping);
1060 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1062 if (allow_immovable_windows && (disabled || inMaximized))
1064 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1070 - (void) adjustFeaturesForState
1072 NSUInteger style = [self styleMask];
1074 if (style & NSWindowStyleMaskClosable)
1075 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1076 if (style & NSWindowStyleMaskMiniaturizable)
1077 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1078 if (style & NSWindowStyleMaskResizable)
1079 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1080 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1081 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1083 if ([self preventResizing])
1085 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1086 [self setContentMinSize:size];
1087 [self setContentMaxSize:size];
1091 [self setContentMaxSize:savedContentMaxSize];
1092 [self setContentMinSize:savedContentMinSize];
1095 if (allow_immovable_windows || cursor_clipping_locks_windows)
1096 [self setMovable:[self allowsMovingWithMaximized:maximized]];
1099 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1101 NSUInteger style = [self styleMask];
1103 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1104 style & NSWindowStyleMaskResizable && !(style & NSWindowStyleMaskUtilityWindow) && !maximized &&
1105 !(self.parentWindow || self.latentParentWindow))
1107 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1108 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1112 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1113 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1114 if (style & NSWindowStyleMaskFullScreen)
1115 [super toggleFullScreen:nil];
1118 if (behavior != [self collectionBehavior])
1120 [self setCollectionBehavior:behavior];
1121 [self adjustFeaturesForState];
1125 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1127 static const NSUInteger usedStyles = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable |
1128 NSWindowStyleMaskResizable | NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskBorderless;
1129 NSUInteger currentStyle = [self styleMask];
1130 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1132 if (newStyle != currentStyle)
1134 NSString* title = [[[self title] copy] autorelease];
1135 BOOL showingButtons = (currentStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1136 BOOL shouldShowButtons = (newStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1137 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSWindowStyleMaskClosable))
1139 // -setStyleMask: is buggy on 10.7+ with respect to NSWindowStyleMaskResizable.
1140 // If transitioning from NSWindowStyleMaskTitled | NSWindowStyleMaskResizable to
1141 // just NSWindowStyleMaskTitled, the window buttons should disappear rather
1142 // than just being disabled. But they don't. Similarly in reverse.
1143 // The workaround is to also toggle NSWindowStyleMaskClosable at the same time.
1144 [self setStyleMask:newStyle ^ NSWindowStyleMaskClosable];
1146 [self setStyleMask:newStyle];
1148 // -setStyleMask: resets the firstResponder to the window. Set it
1149 // back to the content view.
1150 if ([[self contentView] acceptsFirstResponder])
1151 [self makeFirstResponder:[self contentView]];
1153 [self adjustFullScreenBehavior:[self collectionBehavior]];
1155 if ([[self title] length] == 0 && [title length] > 0)
1156 [self setTitle:title];
1159 resizable = wf->resizable;
1160 [self adjustFeaturesForState];
1161 [self setHasShadow:wf->shadow];
1164 // Indicates if the window would be visible if the app were not hidden.
1165 - (BOOL) wouldBeVisible
1167 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1170 - (BOOL) isOrderedIn
1172 return [self wouldBeVisible] || [self isMiniaturized];
1175 - (NSInteger) minimumLevelForActive:(BOOL)active
1179 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1180 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1181 level = NSFloatingWindowLevel;
1183 level = NSNormalWindowLevel;
1189 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1191 if (captured || fullscreen)
1194 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1196 level = NSStatusWindowLevel + 1;
1206 - (void) postDidUnminimizeEvent
1208 macdrv_event* event;
1210 /* Coalesce events by discarding any previous ones still in the queue. */
1211 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1214 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1215 [queue postEvent:event];
1216 macdrv_release_event(event);
1219 - (void) sendResizeStartQuery
1221 macdrv_query* query = macdrv_create_query();
1222 query->type = QUERY_RESIZE_START;
1223 query->window = (macdrv_window)[self retain];
1225 [self.queue query:query timeout:0.3];
1226 macdrv_release_query(query);
1229 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1231 NSWindowCollectionBehavior behavior;
1233 self.disabled = state->disabled;
1234 self.noActivate = state->no_activate;
1236 if (self.floating != state->floating)
1238 self.floating = state->floating;
1239 if (state->floating)
1241 // Became floating. If child of non-floating window, make that
1242 // relationship latent.
1243 WineWindow* parent = (WineWindow*)[self parentWindow];
1244 if (parent && !parent.floating)
1245 [self becameIneligibleChild];
1249 // Became non-floating. If parent of floating children, make that
1250 // relationship latent.
1252 for (child in [self childWineWindows])
1255 [child becameIneligibleChild];
1259 // Check our latent relationships. If floating status was the only
1260 // reason they were latent, then make them active.
1261 if ([self isVisible])
1262 [self becameEligibleParentOrChild];
1264 [[WineApplicationController sharedController] adjustWindowLevels];
1267 if (state->minimized_valid)
1269 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1271 pendingMinimize = FALSE;
1272 if (state->minimized && ![self isMiniaturized])
1274 if ([self wouldBeVisible])
1276 if ([self styleMask] & NSWindowStyleMaskFullScreen)
1278 [self postDidUnminimizeEvent];
1279 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1283 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1284 [super miniaturize:nil];
1285 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1286 event_mask_for_type(WINDOW_GOT_FOCUS) |
1287 event_mask_for_type(WINDOW_LOST_FOCUS);
1291 pendingMinimize = TRUE;
1293 else if (!state->minimized && [self isMiniaturized])
1295 ignore_windowDeminiaturize = TRUE;
1296 [self deminiaturize:nil];
1297 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1301 [queue discardEventsMatchingMask:discard forWindow:self];
1304 if (state->maximized != maximized)
1306 maximized = state->maximized;
1307 [self adjustFeaturesForState];
1309 if (!maximized && [self inLiveResize])
1310 [self sendResizeStartQuery];
1313 behavior = NSWindowCollectionBehaviorDefault;
1314 if (state->excluded_by_expose)
1315 behavior |= NSWindowCollectionBehaviorTransient;
1317 behavior |= NSWindowCollectionBehaviorManaged;
1318 if (state->excluded_by_cycle)
1320 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1321 if ([self isOrderedIn])
1322 [NSApp removeWindowsItem:self];
1326 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1327 if ([self isOrderedIn])
1328 [NSApp addWindowsItem:self title:[self title] filename:NO];
1330 [self adjustFullScreenBehavior:behavior];
1333 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1335 BOOL reordered = FALSE;
1337 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1339 if ([self level] > [child level])
1340 [child setLevel:[self level]];
1341 if (![child isVisible])
1342 [child setAutodisplay:YES];
1343 [self addChildWindow:child ordered:NSWindowAbove];
1344 [child checkWineDisplayLink];
1345 [latentChildWindows removeObjectIdenticalTo:child];
1346 child.latentParentWindow = nil;
1351 if (!latentChildWindows)
1352 latentChildWindows = [[NSMutableArray alloc] init];
1353 if (![latentChildWindows containsObject:child])
1354 [latentChildWindows addObject:child];
1355 child.latentParentWindow = self;
1361 - (BOOL) addChildWineWindow:(WineWindow*)child
1363 return [self addChildWineWindow:child assumeVisible:FALSE];
1366 - (void) removeChildWineWindow:(WineWindow*)child
1368 [self removeChildWindow:child];
1369 if (child.latentParentWindow == self)
1370 child.latentParentWindow = nil;
1371 [latentChildWindows removeObjectIdenticalTo:child];
1374 - (void) setChildWineWindows:(NSArray*)childWindows
1376 NSArray* origChildren;
1377 NSUInteger count, start, limit, i;
1379 origChildren = self.childWineWindows;
1381 // If the current and desired children arrays match up to a point, leave
1382 // those matching children alone.
1383 count = childWindows.count;
1384 limit = MIN(origChildren.count, count);
1385 for (start = 0; start < limit; start++)
1387 if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1391 // Remove all of the child windows and re-add them back-to-front so they
1392 // are in the desired order.
1393 for (i = start; i < count; i++)
1395 WineWindow* child = [childWindows objectAtIndex:i];
1396 [self removeChildWindow:child];
1398 for (i = start; i < count; i++)
1400 WineWindow* child = [childWindows objectAtIndex:i];
1401 [self addChildWindow:child ordered:NSWindowAbove];
1405 static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1407 NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1408 NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1409 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1410 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1411 if (index1 == NSNotFound)
1413 if (index2 == NSNotFound)
1414 return NSOrderedSame;
1416 return NSOrderedAscending;
1418 else if (index2 == NSNotFound)
1419 return NSOrderedDescending;
1420 else if (index1 < index2)
1421 return NSOrderedDescending;
1422 else if (index2 < index1)
1423 return NSOrderedAscending;
1425 return NSOrderedSame;
1428 - (BOOL) becameEligibleParentOrChild
1430 BOOL reordered = FALSE;
1433 if (latentParentWindow.floating || !self.floating)
1435 // If we aren't visible currently, we assume that we should be and soon
1436 // will be. So, if the latent parent is visible that's enough to assume
1437 // we can establish the parent-child relationship in Cocoa. That will
1438 // actually make us visible, which is fine.
1439 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1443 // Here, though, we may not actually be visible yet and adding a child
1444 // won't make us visible. The caller will have to call this method
1445 // again after actually making us visible.
1446 if ([self isVisible] && (count = [latentChildWindows count]))
1448 NSMutableArray* windowNumbers;
1449 NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1450 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1453 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1455 for (i = 0; i < count; i++)
1457 WineWindow* child = [latentChildWindows objectAtIndex:i];
1458 if ([child isVisible] && (self.floating || !child.floating))
1460 if (child.latentParentWindow == self)
1462 if ([self level] > [child level])
1463 [child setLevel:[self level]];
1464 [childWindows addObject:child];
1465 child.latentParentWindow = nil;
1469 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1470 [indexesToRemove addIndex:i];
1474 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1476 [childWindows sortWithOptions:NSSortStable
1477 usingComparator:^NSComparisonResult(id obj1, id obj2){
1478 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1480 [self setChildWineWindows:childWindows];
1486 - (void) becameIneligibleChild
1488 WineWindow* parent = (WineWindow*)[self parentWindow];
1491 if (!parent->latentChildWindows)
1492 parent->latentChildWindows = [[NSMutableArray alloc] init];
1493 [parent->latentChildWindows insertObject:self atIndex:0];
1494 self.latentParentWindow = parent;
1495 [parent removeChildWindow:self];
1499 - (void) becameIneligibleParentOrChild
1501 NSArray* childWindows = [self childWineWindows];
1503 [self becameIneligibleChild];
1505 if ([childWindows count])
1509 for (child in childWindows)
1511 child.latentParentWindow = self;
1512 [self removeChildWindow:child];
1515 if (latentChildWindows)
1516 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1518 latentChildWindows = [childWindows mutableCopy];
1522 // Determine if, among Wine windows, this window is directly above or below
1523 // a given other Wine window with no other Wine window intervening.
1524 // Intervening non-Wine windows are ignored.
1525 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1527 NSNumber* windowNumber;
1528 NSNumber* otherWindowNumber;
1529 NSArray* windowNumbers;
1530 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1532 if (![self isVisible] || ![otherWindow isVisible])
1535 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1536 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1537 windowNumbers = [[self class] windowNumbersWithOptions:0];
1538 windowIndex = [windowNumbers indexOfObject:windowNumber];
1539 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1541 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1544 if (orderingMode == NSWindowAbove)
1546 lowIndex = windowIndex;
1547 highIndex = otherWindowIndex;
1549 else if (orderingMode == NSWindowBelow)
1551 lowIndex = otherWindowIndex;
1552 highIndex = windowIndex;
1557 if (highIndex <= lowIndex)
1560 for (i = lowIndex + 1; i < highIndex; i++)
1562 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1563 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1564 if ([interveningWindow isKindOfClass:[WineWindow class]])
1571 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1573 NSMutableArray* windowNumbers;
1574 NSNumber* childWindowNumber;
1575 NSUInteger otherIndex;
1576 NSArray* origChildren;
1577 NSMutableArray* children;
1579 // Get the z-order from the window server and modify it to reflect the
1580 // requested window ordering.
1581 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1582 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1583 [windowNumbers removeObject:childWindowNumber];
1586 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1587 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1589 else if (mode == NSWindowAbove)
1590 [windowNumbers insertObject:childWindowNumber atIndex:0];
1592 [windowNumbers addObject:childWindowNumber];
1594 // Get our child windows and sort them in the reverse of the desired
1595 // z-order (back-to-front).
1596 origChildren = [self childWineWindows];
1597 children = [[origChildren mutableCopy] autorelease];
1598 [children sortWithOptions:NSSortStable
1599 usingComparator:^NSComparisonResult(id obj1, id obj2){
1600 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1603 [self setChildWineWindows:children];
1606 // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1607 // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1608 // (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
1609 // if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1610 // otherwise, the two ancestors are each roots of disjoint window trees
1611 // (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
1612 // (non-nil, nil) self is a descendent of other and *ancestor is the direct child
1613 - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1615 NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1619 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1625 *ancestorOfOther = child;
1629 [otherAncestors addObject:parent];
1633 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1636 NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1637 if (index != NSNotFound)
1641 *ancestorOfOther = nil;
1643 *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1649 *ancestorOfOther = otherAncestors.lastObject;;
1652 /* Returns whether or not the window was ordered in, which depends on if
1653 its frame intersects any screen. */
1654 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1656 WineApplicationController* controller = [WineApplicationController sharedController];
1657 if (![self isMiniaturized])
1659 BOOL needAdjustWindowLevels = FALSE;
1664 [controller transformProcessToForeground];
1665 if ([NSApp isHidden])
1667 wasVisible = [self isVisible];
1670 [NSApp activateIgnoringOtherApps:YES];
1672 NSDisableScreenUpdates();
1674 if ([self becameEligibleParentOrChild])
1675 needAdjustWindowLevels = TRUE;
1679 WineWindow* other = [prev isVisible] ? prev : next;
1680 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1682 if (![self isOrdered:orderingMode relativeTo:other])
1684 WineWindow* ancestor;
1685 WineWindow* ancestorOfOther;
1687 [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1690 [self setAutodisplay:YES];
1691 if (ancestorOfOther)
1693 // This window level may not be right for this window based
1694 // on floating-ness, fullscreen-ness, etc. But we set it
1695 // temporarily to allow us to order the windows properly.
1696 // Then the levels get fixed by -adjustWindowLevels.
1697 if ([ancestor level] != [ancestorOfOther level])
1698 [ancestor setLevel:[ancestorOfOther level]];
1700 parent = (WineWindow*)ancestor.parentWindow;
1701 if ([parent isKindOfClass:[WineWindow class]])
1702 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1704 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1707 if (!ancestorOfOther || ancestor != self)
1710 (parent = (WineWindow*)child.parentWindow);
1713 if ([parent isKindOfClass:[WineWindow class]])
1714 [parent order:-orderingMode childWindow:child relativeTo:nil];
1715 if (parent == ancestor)
1720 [self checkWineDisplayLink];
1721 needAdjustWindowLevels = TRUE;
1728 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1731 [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1734 // Again, temporarily set level to make sure we can order to
1736 next = [controller frontWineWindow];
1737 if (next && [self level] < [next level])
1738 [self setLevel:[next level]];
1739 [self setAutodisplay:YES];
1740 [self orderFront:nil];
1741 [self checkWineDisplayLink];
1742 needAdjustWindowLevels = TRUE;
1744 pendingOrderOut = FALSE;
1746 if ([self becameEligibleParentOrChild])
1747 needAdjustWindowLevels = TRUE;
1749 if (needAdjustWindowLevels)
1751 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1752 [controller updateFullscreenWindows];
1753 [controller adjustWindowLevels];
1756 if (pendingMinimize)
1758 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1759 [super miniaturize:nil];
1760 pendingMinimize = FALSE;
1763 NSEnableScreenUpdates();
1765 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1766 Generate a frame-changed event just in case. The back end will ignore
1767 it if nothing actually changed. */
1768 [self windowDidResize:nil skipSizeMove:TRUE];
1770 if (![self isExcludedFromWindowsMenu])
1771 [NSApp addWindowsItem:self title:[self title] filename:NO];
1777 WineApplicationController* controller = [WineApplicationController sharedController];
1778 BOOL wasVisible = [self isVisible];
1779 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1781 [self endWindowDragging];
1782 [controller windowWillOrderOut:self];
1784 if (enteringFullScreen || exitingFullScreen)
1786 pendingOrderOut = TRUE;
1787 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1788 event_mask_for_type(WINDOW_GOT_FOCUS) |
1789 event_mask_for_type(WINDOW_LOST_FOCUS) |
1790 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1791 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1792 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1797 pendingOrderOut = FALSE;
1799 if ([self isMiniaturized])
1800 pendingMinimize = TRUE;
1802 WineWindow* parent = (WineWindow*)self.parentWindow;
1803 if ([parent isKindOfClass:[WineWindow class]])
1804 [parent grabDockIconSnapshotFromWindow:self force:NO];
1806 [self becameIneligibleParentOrChild];
1807 if ([self isMiniaturized] || [self styleMask] & NSWindowStyleMaskFullScreen)
1811 fakingClose = FALSE;
1814 [self orderOut:nil];
1815 [self checkWineDisplayLink];
1816 [self setBackgroundColor:[NSColor clearColor]];
1817 [self setOpaque:NO];
1818 drawnSinceShown = NO;
1819 savedVisibleState = FALSE;
1820 if (wasVisible && wasOnActiveSpace && fullscreen)
1821 [controller updateFullscreenWindows];
1822 [controller adjustWindowLevels];
1823 [NSApp removeWindowsItem:self];
1825 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1826 event_mask_for_type(WINDOW_GOT_FOCUS) |
1827 event_mask_for_type(WINDOW_LOST_FOCUS) |
1828 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1829 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1830 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1834 - (void) updateFullscreen
1836 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1837 BOOL nowFullscreen = !([self styleMask] & NSWindowStyleMaskFullScreen) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1839 if (nowFullscreen != fullscreen)
1841 WineApplicationController* controller = [WineApplicationController sharedController];
1843 fullscreen = nowFullscreen;
1844 if ([self isVisible] && [self isOnActiveSpace])
1845 [controller updateFullscreenWindows];
1847 [controller adjustWindowLevels];
1851 - (void) setFrameAndWineFrame:(NSRect)frame
1853 [self setFrame:frame display:YES];
1856 roundedWineFrame = self.frame;
1858 #if CGFLOAT_IS_DOUBLE
1859 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1860 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1861 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1862 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1863 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1864 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1865 roundedWineFrame = wineFrame;
1867 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1868 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1869 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1870 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1871 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1872 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1873 roundedWineFrame = wineFrame;
1877 - (void) setFrameFromWine:(NSRect)contentRect
1879 /* Origin is (left, top) in a top-down space. Need to convert it to
1880 (left, bottom) in a bottom-up space. */
1881 [[WineApplicationController sharedController] flipRect:&contentRect];
1883 /* The back end is establishing a new window size and position. It's
1884 not interested in any stale events regarding those that may be sitting
1886 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1889 if (!NSIsEmptyRect(contentRect))
1891 NSRect frame, oldFrame;
1893 oldFrame = self.wine_fractionalFrame;
1894 frame = [self frameRectForContentRect:contentRect];
1895 if (!NSEqualRects(frame, oldFrame))
1897 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1898 BOOL needEnableScreenUpdates = FALSE;
1900 if ([self preventResizing])
1902 // Allow the following calls to -setFrame:display: to work even
1903 // if they would violate the content size constraints. This
1904 // shouldn't be necessary since the content size constraints are
1905 // documented to not constrain that method, but it seems to be.
1906 [self setContentMinSize:NSZeroSize];
1907 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1910 if (equalSizes && [[self childWineWindows] count])
1912 // If we change the window frame such that the origin moves
1913 // but the size doesn't change, then Cocoa moves child
1914 // windows with the parent. We don't want that so we fake
1915 // a change of the size and then change it back.
1916 NSRect bogusFrame = frame;
1917 bogusFrame.size.width++;
1919 NSDisableScreenUpdates();
1920 needEnableScreenUpdates = TRUE;
1922 ignore_windowResize = TRUE;
1923 [self setFrame:bogusFrame display:NO];
1924 ignore_windowResize = FALSE;
1927 [self setFrameAndWineFrame:frame];
1928 if ([self preventResizing])
1930 [self setContentMinSize:contentRect.size];
1931 [self setContentMaxSize:contentRect.size];
1934 if (needEnableScreenUpdates)
1935 NSEnableScreenUpdates();
1937 if (!enteringFullScreen &&
1938 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1939 nonFullscreenFrame = frame;
1941 [self updateFullscreen];
1943 if ([self isOrderedIn])
1945 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1946 event. The back end will ignore it if nothing actually changed. */
1947 [self windowDidResize:nil skipSizeMove:TRUE];
1953 - (NSRect) wine_fractionalFrame
1955 NSRect frame = self.frame;
1956 if (NSEqualRects(frame, roundedWineFrame))
1961 - (void) setMacDrvParentWindow:(WineWindow*)parent
1963 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1964 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1966 [oldParent removeChildWineWindow:self];
1967 [latentParentWindow removeChildWineWindow:self];
1968 if ([parent addChildWineWindow:self])
1969 [[WineApplicationController sharedController] adjustWindowLevels];
1970 [self adjustFullScreenBehavior:[self collectionBehavior]];
1974 - (void) setDisabled:(BOOL)newValue
1976 if (disabled != newValue)
1978 disabled = newValue;
1979 [self adjustFeaturesForState];
1983 - (BOOL) needsTransparency
1985 return self.contentView.layer.mask || self.colorKeyed || self.usePerPixelAlpha ||
1986 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
1989 - (void) checkTransparency
1991 if (![self isOpaque] && !self.needsTransparency)
1993 self.shapeChangedSinceLastDraw = TRUE;
1994 [[self contentView] setNeedsDisplay:YES];
1995 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1996 [self setOpaque:YES];
1998 else if ([self isOpaque] && self.needsTransparency)
2000 self.shapeChangedSinceLastDraw = TRUE;
2001 [[self contentView] setNeedsDisplay:YES];
2002 [self setBackgroundColor:[NSColor clearColor]];
2003 [self setOpaque:NO];
2007 - (void) setShape:(CGPathRef)newShape
2009 CALayer* layer = [[self contentView] layer];
2010 CAShapeLayer* mask = (CAShapeLayer*)layer.mask;
2011 if (CGPathEqualToPath(newShape, mask.path)) return;
2013 if (newShape && !layer.mask)
2014 layer.mask = mask = [CAShapeLayer layer];
2016 layer.mask = mask = nil;
2019 [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(mask.path))];
2021 [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(newShape))];
2023 mask.path = newShape;
2024 self.shapeChangedSinceLastDraw = TRUE;
2026 [self checkTransparency];
2027 [self checkEmptyShaped];
2030 - (void) makeFocused:(BOOL)activate
2034 [[WineApplicationController sharedController] transformProcessToForeground];
2035 [NSApp activateIgnoringOtherApps:YES];
2038 causing_becomeKeyWindow = self;
2039 [self makeKeyWindow];
2040 causing_becomeKeyWindow = nil;
2042 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2043 event_mask_for_type(WINDOW_LOST_FOCUS)
2047 - (void) postKey:(uint16_t)keyCode
2048 pressed:(BOOL)pressed
2049 modifiers:(NSUInteger)modifiers
2050 event:(NSEvent*)theEvent
2052 macdrv_event* event;
2054 WineApplicationController* controller = [WineApplicationController sharedController];
2056 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2057 event->key.keycode = keyCode;
2058 event->key.modifiers = modifiers;
2059 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
2061 if ((cgevent = [theEvent CGEvent]))
2062 controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2064 [queue postEvent:event];
2066 macdrv_release_event(event);
2068 [controller noteKey:keyCode pressed:pressed];
2071 - (void) postKeyEvent:(NSEvent *)theEvent
2073 [self flagsChanged:theEvent];
2074 [self postKey:[theEvent keyCode]
2075 pressed:[theEvent type] == NSEventTypeKeyDown
2076 modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2080 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2082 savedContentMinSize = minSize;
2083 savedContentMaxSize = maxSize;
2084 if (![self preventResizing])
2086 [self setContentMinSize:minSize];
2087 [self setContentMaxSize:maxSize];
2091 - (WineWindow*) ancestorWineWindow
2093 WineWindow* ancestor = self;
2096 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2097 if ([parent isKindOfClass:[WineWindow class]])
2105 - (void) postBroughtForwardEvent
2107 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2108 [queue postEvent:event];
2109 macdrv_release_event(event);
2112 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing skipSizeMove:(BOOL)skipSizeMove
2114 macdrv_event* event;
2115 NSUInteger style = self.styleMask;
2118 style |= NSWindowStyleMaskFullScreen;
2120 style &= ~NSWindowStyleMaskFullScreen;
2121 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2122 [[WineApplicationController sharedController] flipRect:&frame];
2124 /* Coalesce events by discarding any previous ones still in the queue. */
2125 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2128 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2129 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2130 event->window_frame_changed.fullscreen = isFullscreen;
2131 event->window_frame_changed.in_resize = resizing;
2132 event->window_frame_changed.skip_size_move_loop = skipSizeMove;
2133 [queue postEvent:event];
2134 macdrv_release_event(event);
2137 - (void) updateForCursorClipping
2139 [self adjustFeaturesForState];
2142 - (void) endWindowDragging
2146 if (draggingPhase == 3)
2148 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2149 [queue postEvent:event];
2150 macdrv_release_event(event);
2154 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2158 - (NSMutableDictionary*) displayIDToDisplayLinkMap
2160 static NSMutableDictionary* displayIDToDisplayLinkMap;
2161 if (!displayIDToDisplayLinkMap)
2163 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2165 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2168 usingBlock:^(NSNotification *note){
2169 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2170 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2171 [badDisplayIDs minusSet:validDisplayIDs];
2172 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2175 return displayIDToDisplayLinkMap;
2178 - (WineDisplayLink*) wineDisplayLink
2180 if (!_lastDisplayID)
2183 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2184 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2187 - (void) checkWineDisplayLink
2189 NSScreen* screen = self.screen;
2190 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2192 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2193 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2197 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2198 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2199 if (displayID == _lastDisplayID)
2202 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2206 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2207 [link removeWindow:self];
2211 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2214 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2215 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2217 [link addWindow:self];
2218 [self displayIfNeeded];
2220 _lastDisplayID = displayID;
2223 - (BOOL) isEmptyShaped
2225 CAShapeLayer* mask = (CAShapeLayer*)[[self contentView] layer].mask;
2226 return ([mask isEmptyShaped]);
2229 - (BOOL) canProvideSnapshot
2231 return (self.windowNumber > 0 && ![self isEmptyShaped]);
2234 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2236 if (![self isEmptyShaped])
2239 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2240 if (!force && now < lastDockIconSnapshot + 1)
2245 if (![window canProvideSnapshot])
2251 for (WineWindow* childWindow in self.childWindows)
2253 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2256 NSSize size = childWindow.frame.size;
2257 CGFloat area = size.width * size.height;
2258 if (!window || area > bestArea)
2260 window = childWindow;
2269 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2270 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2271 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2272 CFRelease(windowIDs);
2276 NSImage* appImage = [NSApp applicationIconImage];
2278 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2280 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2281 [dockIcon lockFocus];
2283 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2285 CGRect rect = CGRectMake(8, 8, 240, 240);
2286 size_t width = CGImageGetWidth(windowImage);
2287 size_t height = CGImageGetHeight(windowImage);
2290 rect.size.height *= height / (double)width;
2291 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2293 else if (width != height)
2295 rect.size.width *= width / (double)height;
2296 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2299 CGContextDrawImage(cgcontext, rect, windowImage);
2300 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2302 operation:NSCompositingOperationSourceOver
2307 [dockIcon unlockFocus];
2309 CGImageRelease(windowImage);
2311 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2312 if (![imageView isKindOfClass:[NSImageView class]])
2314 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2315 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2316 self.dockTile.contentView = imageView;
2318 imageView.image = dockIcon;
2319 [self.dockTile display];
2320 lastDockIconSnapshot = now;
2323 - (void) checkEmptyShaped
2325 if (self.dockTile.contentView && ![self isEmptyShaped])
2327 self.dockTile.contentView = nil;
2328 lastDockIconSnapshot = 0;
2330 [self checkWineDisplayLink];
2335 * ---------- NSWindow method overrides ----------
2337 - (BOOL) canBecomeKeyWindow
2339 if (causing_becomeKeyWindow == self) return YES;
2340 if (self.disabled || self.noActivate) return NO;
2341 if ([self isKeyWindow]) return YES;
2343 // If a window's collectionBehavior says it participates in cycling,
2344 // it must return YES from this method to actually be eligible.
2345 return ![self isExcludedFromWindowsMenu];
2348 - (BOOL) canBecomeMainWindow
2350 return [self canBecomeKeyWindow];
2353 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2355 // If a window is sized to completely cover a screen, then it's in
2356 // full-screen mode. In that case, we don't allow NSWindow to constrain
2358 NSArray* screens = [NSScreen screens];
2359 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2360 if (!screen_covered_by_rect(contentRect, screens) &&
2361 frame_intersects_screens(frameRect, screens))
2362 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2366 // This private method of NSWindow is called as Cocoa reacts to the display
2367 // configuration changing. Among other things, it adjusts the window's
2368 // frame based on how the screen(s) changed size. That tells Wine that the
2369 // window has been moved. We don't want that. Rather, we want to make
2370 // sure that the WinAPI notion of the window position is maintained/
2371 // restored, possibly undoing or overriding Cocoa's adjustment.
2373 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2374 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2375 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2376 // reassert its notion of the position. That call won't get processed
2377 // until after this method returns, so it will override whatever this
2378 // method does to the window position. It will also discard any pending
2379 // WINDOW_FRAME_CHANGED events.
2381 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2382 // adjust the window's position due to a display change is to hook into
2383 // this private method. This private method has remained stable from 10.6
2384 // through 10.11. If it does change, the most likely thing is that it
2385 // will be removed and no longer called and this fix will simply stop
2386 // working. The only real danger would be if Apple changed the return type
2387 // to a struct or floating-point type, which would change the calling
2389 - (id) _displayChanged
2391 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2392 [queue postEvent:event];
2393 macdrv_release_event(event);
2395 return [super _displayChanged];
2398 - (BOOL) isExcludedFromWindowsMenu
2400 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2403 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2405 BOOL ret = [super validateMenuItem:menuItem];
2407 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2408 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
2409 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2415 /* We don't call this. It's the action method of the items in the Window menu. */
2416 - (void) makeKeyAndOrderFront:(id)sender
2418 if ([self isMiniaturized])
2419 [self deminiaturize:nil];
2420 [self orderBelow:nil orAbove:nil activate:NO];
2421 [[self ancestorWineWindow] postBroughtForwardEvent];
2423 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
2424 [[WineApplicationController sharedController] windowGotFocus:self];
2427 - (void) sendEvent:(NSEvent*)event
2429 NSEventType type = event.type;
2431 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2432 interface control. For example, Control-Tab switches focus among
2433 views. We want to bypass that feature, so directly route key-down
2434 events to -keyDown:. */
2435 if (type == NSEventTypeKeyDown)
2436 [[self firstResponder] keyDown:event];
2439 if (!draggingPhase && maximized && ![self isMovable] &&
2440 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2441 type == NSEventTypeLeftMouseDown && (self.styleMask & NSWindowStyleMaskTitled))
2443 NSRect titleBar = self.frame;
2444 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2445 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2446 titleBar.origin.y = NSMaxY(contentRect);
2448 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2450 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2452 static const NSWindowButton buttons[] = {
2453 NSWindowCloseButton,
2454 NSWindowMiniaturizeButton,
2456 NSWindowFullScreenButton,
2458 BOOL hitButton = NO;
2461 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2463 NSButton* button = [self standardWindowButton:buttons[i]];
2464 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2474 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2475 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2479 else if (draggingPhase && (type == NSEventTypeLeftMouseDragged || type == NSEventTypeLeftMouseUp))
2481 if ([self isMovable])
2483 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2484 NSPoint newTopLeft = dragWindowStartPosition;
2486 newTopLeft.x += point.x - dragStartPosition.x;
2487 newTopLeft.y += point.y - dragStartPosition.y;
2489 if (draggingPhase == 2)
2491 macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2492 mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2493 [queue postEvent:mevent];
2494 macdrv_release_event(mevent);
2499 [self setFrameTopLeftPoint:newTopLeft];
2501 else if (draggingPhase == 1 && type == NSEventTypeLeftMouseDragged)
2503 macdrv_event* event;
2504 NSRect frame = [self contentRectForFrameRect:self.frame];
2506 [[WineApplicationController sharedController] flipRect:&frame];
2508 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2509 event->window_restore_requested.keep_frame = TRUE;
2510 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2511 [queue postEvent:event];
2512 macdrv_release_event(event);
2517 if (type == NSEventTypeLeftMouseUp)
2518 [self endWindowDragging];
2521 [super sendEvent:event];
2525 - (void) miniaturize:(id)sender
2527 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2528 [queue postEvent:event];
2529 macdrv_release_event(event);
2531 WineWindow* parent = (WineWindow*)self.parentWindow;
2532 if ([parent isKindOfClass:[WineWindow class]])
2533 [parent grabDockIconSnapshotFromWindow:self force:YES];
2536 - (void) toggleFullScreen:(id)sender
2538 if (!self.disabled && !maximized)
2539 [super toggleFullScreen:sender];
2542 - (void) setViewsNeedDisplay:(BOOL)value
2544 if (value && ![self viewsNeedDisplay])
2546 WineDisplayLink* link = [self wineDisplayLink];
2549 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2550 if (_lastDisplayTime + [link refreshPeriod] < now)
2551 [self setAutodisplay:YES];
2555 _lastDisplayTime = now;
2559 [self setAutodisplay:YES];
2561 [super setViewsNeedDisplay:value];
2566 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2569 [self setAutodisplay:NO];
2572 - (void) displayIfNeeded
2574 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2575 [super displayIfNeeded];
2577 [self setAutodisplay:NO];
2580 - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2583 [self setAutodisplay:YES];
2584 [super setFrame:frameRect display:flag];
2587 - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2590 [self setAutodisplay:YES];
2591 [super setFrame:frameRect display:displayFlag animate:animateFlag];
2594 - (void) windowDidDrawContent
2596 if (!drawnSinceShown)
2598 drawnSinceShown = YES;
2599 dispatch_async(dispatch_get_main_queue(), ^{
2600 [self checkTransparency];
2605 - (NSArray*) childWineWindows
2607 NSArray* childWindows = self.childWindows;
2608 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2609 return [child isKindOfClass:[WineWindow class]];
2611 return [childWindows objectsAtIndexes:indexes];
2614 - (void) updateForGLSubviews
2616 if (gl_surface_mode == GL_SURFACE_BEHIND)
2617 [self checkTransparency];
2620 - (void) setRetinaMode:(int)mode
2623 double scale = mode ? 0.5 : 2.0;
2624 NSAffineTransform* transform = [NSAffineTransform transform];
2626 [transform scaleBy:scale];
2628 [[self contentView] layer].mask.contentsScale = mode ? 2.0 : 1.0;
2630 for (WineBaseView* subview in [self.contentView subviews])
2632 if ([subview isKindOfClass:[WineBaseView class]])
2633 [subview setRetinaMode:mode];
2636 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2637 frame.origin.x *= scale;
2638 frame.origin.y *= scale;
2639 frame.size.width *= scale;
2640 frame.size.height *= scale;
2641 frame = [self frameRectForContentRect:frame];
2643 savedContentMinSize = [transform transformSize:savedContentMinSize];
2644 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2645 savedContentMaxSize.width *= scale;
2646 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2647 savedContentMaxSize.height *= scale;
2649 self.contentMinSize = [transform transformSize:self.contentMinSize];
2650 NSSize temp = self.contentMaxSize;
2651 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2652 temp.width *= scale;
2653 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2654 temp.height *= scale;
2655 self.contentMaxSize = temp;
2657 ignore_windowResize = TRUE;
2658 [self setFrameAndWineFrame:frame];
2659 ignore_windowResize = FALSE;
2664 * ---------- NSResponder method overrides ----------
2666 - (void) keyDown:(NSEvent *)theEvent
2668 if ([theEvent isARepeat])
2670 if (!allowKeyRepeats)
2674 allowKeyRepeats = YES;
2676 [self postKeyEvent:theEvent];
2679 - (void) flagsChanged:(NSEvent *)theEvent
2681 static const struct {
2685 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2686 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2687 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2688 { NX_DEVICELCTLKEYMASK, kVK_Control },
2689 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2690 { NX_DEVICELALTKEYMASK, kVK_Option },
2691 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2692 { NX_DEVICELCMDKEYMASK, kVK_Command },
2693 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2696 NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2698 int i, last_changed;
2700 fix_device_modifiers_by_generic(&modifierFlags);
2701 changed = modifierFlags ^ lastModifierFlags;
2704 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2705 if (changed & modifiers[i].mask)
2708 for (i = 0; i <= last_changed; i++)
2710 if (changed & modifiers[i].mask)
2712 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2715 allowKeyRepeats = NO;
2717 if (i == last_changed)
2718 lastModifierFlags = modifierFlags;
2721 lastModifierFlags ^= modifiers[i].mask;
2722 fix_generic_modifiers_by_device(&lastModifierFlags);
2725 // Caps lock generates one event for each press-release action.
2726 // We need to simulate a pair of events for each actual event.
2727 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2729 [self postKey:modifiers[i].keycode
2731 modifiers:lastModifierFlags
2732 event:(NSEvent*)theEvent];
2736 [self postKey:modifiers[i].keycode
2738 modifiers:lastModifierFlags
2739 event:(NSEvent*)theEvent];
2744 - (void) applicationWillHide
2746 savedVisibleState = [self isVisible];
2749 - (void) applicationDidUnhide
2751 if ([self isVisible])
2752 [self becameEligibleParentOrChild];
2757 * ---------- NSWindowDelegate methods ----------
2759 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2761 macdrv_query* query;
2764 query = macdrv_create_query();
2765 query->type = QUERY_MIN_MAX_INFO;
2766 query->window = (macdrv_window)[self retain];
2767 [self.queue query:query timeout:0.5];
2768 macdrv_release_query(query);
2770 size = [self contentMaxSize];
2771 if (proposedSize.width < size.width)
2772 size.width = proposedSize.width;
2773 if (proposedSize.height < size.height)
2774 size.height = proposedSize.height;
2778 - (void)windowDidBecomeKey:(NSNotification *)notification
2780 WineApplicationController* controller = [WineApplicationController sharedController];
2781 NSEvent* event = [controller lastFlagsChanged];
2783 [self flagsChanged:event];
2785 if (causing_becomeKeyWindow == self) return;
2787 [controller windowGotFocus:self];
2790 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2792 [self checkWineDisplayLink];
2795 - (void) windowDidChangeScreen:(NSNotification*)notification
2797 [self checkWineDisplayLink];
2800 - (void)windowDidDeminiaturize:(NSNotification *)notification
2802 WineApplicationController* controller = [WineApplicationController sharedController];
2804 if (!ignore_windowDeminiaturize)
2805 [self postDidUnminimizeEvent];
2806 ignore_windowDeminiaturize = FALSE;
2808 [self becameEligibleParentOrChild];
2810 if (fullscreen && [self isOnActiveSpace])
2811 [controller updateFullscreenWindows];
2812 [controller adjustWindowLevels];
2814 if (![self parentWindow])
2815 [self postBroughtForwardEvent];
2817 if (!self.disabled && !self.noActivate)
2819 causing_becomeKeyWindow = self;
2820 [self makeKeyWindow];
2821 causing_becomeKeyWindow = nil;
2822 [controller windowGotFocus:self];
2825 [self windowDidResize:notification];
2826 [self checkWineDisplayLink];
2829 - (void) windowDidEndLiveResize:(NSNotification *)notification
2833 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2834 [queue postEvent:event];
2835 macdrv_release_event(event);
2839 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2841 enteringFullScreen = FALSE;
2842 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2843 if (pendingOrderOut)
2847 - (void) windowDidExitFullScreen:(NSNotification*)notification
2849 exitingFullScreen = FALSE;
2850 [self setFrameAndWineFrame:nonFullscreenFrame];
2851 [self windowDidResize:nil];
2852 if (pendingOrderOut)
2856 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2858 enteringFullScreen = FALSE;
2859 enteredFullScreenTime = 0;
2860 if (pendingOrderOut)
2864 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2866 exitingFullScreen = FALSE;
2867 [self windowDidResize:nil];
2868 if (pendingOrderOut)
2872 - (void)windowDidMiniaturize:(NSNotification *)notification
2874 macdrv_event* event;
2876 if (fullscreen && [self isOnActiveSpace])
2877 [[WineApplicationController sharedController] updateFullscreenWindows];
2879 [self checkWineDisplayLink];
2881 event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
2882 [queue postEvent:event];
2883 macdrv_release_event(event);
2886 - (void)windowDidMove:(NSNotification *)notification
2888 [self windowDidResize:notification];
2891 - (void)windowDidResignKey:(NSNotification *)notification
2893 macdrv_event* event;
2895 if (causing_becomeKeyWindow) return;
2897 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2898 [queue postEvent:event];
2899 macdrv_release_event(event);
2902 - (void)windowDidResize:(NSNotification *)notification skipSizeMove:(BOOL)skipSizeMove
2904 NSRect frame = self.wine_fractionalFrame;
2906 if ([self inLiveResize])
2908 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2909 resizingFromLeft = TRUE;
2910 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2911 resizingFromTop = TRUE;
2914 if (ignore_windowResize || exitingFullScreen) return;
2916 if ([self preventResizing])
2918 NSRect contentRect = [self contentRectForFrameRect:frame];
2919 [self setContentMinSize:contentRect.size];
2920 [self setContentMaxSize:contentRect.size];
2923 [self postWindowFrameChanged:frame
2924 fullscreen:([self styleMask] & NSWindowStyleMaskFullScreen) != 0
2925 resizing:[self inLiveResize]
2926 skipSizeMove:skipSizeMove];
2928 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2929 [self updateFullscreen];
2932 - (void)windowDidResize:(NSNotification *)notification
2934 [self windowDidResize:notification skipSizeMove:FALSE];
2937 - (BOOL)windowShouldClose:(id)sender
2939 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2940 [queue postEvent:event];
2941 macdrv_release_event(event);
2945 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2949 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2950 [queue postEvent:event];
2951 macdrv_release_event(event);
2954 else if (!resizable)
2956 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2957 [queue postEvent:event];
2958 macdrv_release_event(event);
2965 - (void) windowWillClose:(NSNotification*)notification
2969 if (fakingClose) return;
2970 if (latentParentWindow)
2972 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2973 self.latentParentWindow = nil;
2976 for (child in latentChildWindows)
2978 if (child.latentParentWindow == self)
2979 child.latentParentWindow = nil;
2981 [latentChildWindows removeAllObjects];
2984 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2986 enteringFullScreen = TRUE;
2987 nonFullscreenFrame = self.wine_fractionalFrame;
2990 - (void) windowWillExitFullScreen:(NSNotification*)notification
2992 exitingFullScreen = TRUE;
2993 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE skipSizeMove:FALSE];
2996 - (void)windowWillMiniaturize:(NSNotification *)notification
2998 [self becameIneligibleParentOrChild];
2999 [self grabDockIconSnapshotFromWindow:nil force:NO];
3002 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
3004 if ([self inLiveResize])
3007 return self.wine_fractionalFrame.size;
3010 macdrv_query* query;
3012 rect = [self frame];
3013 if (resizingFromLeft)
3014 rect.origin.x = NSMaxX(rect) - frameSize.width;
3015 if (!resizingFromTop)
3016 rect.origin.y = NSMaxY(rect) - frameSize.height;
3017 rect.size = frameSize;
3018 rect = [self contentRectForFrameRect:rect];
3019 [[WineApplicationController sharedController] flipRect:&rect];
3021 query = macdrv_create_query();
3022 query->type = QUERY_RESIZE_SIZE;
3023 query->window = (macdrv_window)[self retain];
3024 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3025 query->resize_size.from_left = resizingFromLeft;
3026 query->resize_size.from_top = resizingFromTop;
3028 if ([self.queue query:query timeout:0.1])
3030 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3031 rect = [self frameRectForContentRect:rect];
3032 frameSize = rect.size;
3035 macdrv_release_query(query);
3041 - (void) windowWillStartLiveResize:(NSNotification *)notification
3043 [self endWindowDragging];
3047 macdrv_event* event;
3048 NSRect frame = [self contentRectForFrameRect:self.frame];
3050 [[WineApplicationController sharedController] flipRect:&frame];
3052 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3053 event->window_restore_requested.keep_frame = TRUE;
3054 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3055 [queue postEvent:event];
3056 macdrv_release_event(event);
3059 [self sendResizeStartQuery];
3061 frameAtResizeStart = [self frame];
3062 resizingFromLeft = resizingFromTop = FALSE;
3065 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3067 macdrv_query* query;
3068 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3071 query = macdrv_create_query();
3072 query->type = QUERY_MIN_MAX_INFO;
3073 query->window = (macdrv_window)[self retain];
3074 [self.queue query:query timeout:0.5];
3075 macdrv_release_query(query);
3077 currentContentRect = [self contentRectForFrameRect:[self frame]];
3078 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3080 maxSize = [self contentMaxSize];
3081 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3082 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3084 // Try to keep the top-left corner where it is.
3085 newContentRect.origin.x = NSMinX(currentContentRect);
3086 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3088 // If that pushes the bottom or right off the screen, pull it up and to the left.
3089 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3090 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3091 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3092 if (NSMinY(newContentRect) < NSMinY(screenRect))
3093 newContentRect.origin.y = NSMinY(screenRect);
3095 // If that pushes the top or left off the screen, push it down and the right
3096 // again. Do this last because the top-left corner is more important than the
3098 if (NSMinX(newContentRect) < NSMinX(screenRect))
3099 newContentRect.origin.x = NSMinX(screenRect);
3100 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3101 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3103 return [self frameRectForContentRect:newContentRect];
3108 * ---------- NSPasteboardOwner methods ----------
3110 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3112 macdrv_query* query = macdrv_create_query();
3113 query->type = QUERY_PASTEBOARD_DATA;
3114 query->window = (macdrv_window)[self retain];
3115 query->pasteboard_data.type = (CFStringRef)[type copy];
3117 [self.queue query:query timeout:3];
3118 macdrv_release_query(query);
3121 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3123 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3124 [queue postEvent:event];
3125 macdrv_release_event(event);
3130 * ---------- NSDraggingDestination methods ----------
3132 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3134 return [self draggingUpdated:sender];
3137 - (void) draggingExited:(id <NSDraggingInfo>)sender
3139 // This isn't really a query. We don't need any response. However, it
3140 // has to be processed in a similar manner as the other drag-and-drop
3141 // queries in order to maintain the proper order of operations.
3142 macdrv_query* query = macdrv_create_query();
3143 query->type = QUERY_DRAG_EXITED;
3144 query->window = (macdrv_window)[self retain];
3146 [self.queue query:query timeout:0.1];
3147 macdrv_release_query(query);
3150 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3152 NSDragOperation ret;
3153 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3154 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3155 NSPasteboard* pb = [sender draggingPasteboard];
3157 macdrv_query* query = macdrv_create_query();
3158 query->type = QUERY_DRAG_OPERATION;
3159 query->window = (macdrv_window)[self retain];
3160 query->drag_operation.x = floor(cgpt.x);
3161 query->drag_operation.y = floor(cgpt.y);
3162 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3163 query->drag_operation.accepted_op = NSDragOperationNone;
3164 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3166 [self.queue query:query timeout:3];
3167 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3168 macdrv_release_query(query);
3173 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3176 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3177 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3178 NSPasteboard* pb = [sender draggingPasteboard];
3180 macdrv_query* query = macdrv_create_query();
3181 query->type = QUERY_DRAG_DROP;
3182 query->window = (macdrv_window)[self retain];
3183 query->drag_drop.x = floor(cgpt.x);
3184 query->drag_drop.y = floor(cgpt.y);
3185 query->drag_drop.op = [sender draggingSourceOperationMask];
3186 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3188 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3189 ret = query->status;
3190 macdrv_release_query(query);
3195 - (BOOL) wantsPeriodicDraggingUpdates
3203 /***********************************************************************
3204 * macdrv_create_cocoa_window
3206 * Create a Cocoa window with the given content frame and features (e.g.
3207 * title bar, close box, etc.).
3209 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3210 CGRect frame, void* hwnd, macdrv_event_queue queue)
3212 __block WineWindow* window;
3215 window = [[WineWindow createWindowWithFeatures:wf
3216 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3218 queue:(WineEventQueue*)queue] retain];
3221 return (macdrv_window)window;
3224 /***********************************************************************
3225 * macdrv_destroy_cocoa_window
3227 * Destroy a Cocoa window.
3229 void macdrv_destroy_cocoa_window(macdrv_window w)
3231 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3232 WineWindow* window = (WineWindow*)w;
3235 [window doOrderOut];
3238 [window.queue discardEventsMatchingMask:-1 forWindow:window];
3244 /***********************************************************************
3245 * macdrv_get_window_hwnd
3247 * Get the hwnd that was set for the window at creation.
3249 void* macdrv_get_window_hwnd(macdrv_window w)
3251 WineWindow* window = (WineWindow*)w;
3255 /***********************************************************************
3256 * macdrv_set_cocoa_window_features
3258 * Update a Cocoa window's features.
3260 void macdrv_set_cocoa_window_features(macdrv_window w,
3261 const struct macdrv_window_features* wf)
3263 WineWindow* window = (WineWindow*)w;
3266 [window setWindowFeatures:wf];
3270 /***********************************************************************
3271 * macdrv_set_cocoa_window_state
3273 * Update a Cocoa window's state.
3275 void macdrv_set_cocoa_window_state(macdrv_window w,
3276 const struct macdrv_window_state* state)
3278 WineWindow* window = (WineWindow*)w;
3281 [window setMacDrvState:state];
3285 /***********************************************************************
3286 * macdrv_set_cocoa_window_title
3288 * Set a Cocoa window's title.
3290 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3293 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3294 WineWindow* window = (WineWindow*)w;
3295 NSString* titleString;
3298 titleString = [NSString stringWithCharacters:title length:length];
3301 OnMainThreadAsync(^{
3302 [window setTitle:titleString];
3303 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3304 [NSApp changeWindowsItem:window title:titleString filename:NO];
3310 /***********************************************************************
3311 * macdrv_order_cocoa_window
3313 * Reorder a Cocoa window relative to other windows. If prev is
3314 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3315 * it is ordered above that window. Otherwise, it is ordered to the
3318 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3319 macdrv_window n, int activate)
3321 WineWindow* window = (WineWindow*)w;
3322 WineWindow* prev = (WineWindow*)p;
3323 WineWindow* next = (WineWindow*)n;
3325 OnMainThreadAsync(^{
3326 [window orderBelow:prev
3330 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3332 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3336 /***********************************************************************
3337 * macdrv_hide_cocoa_window
3339 * Hides a Cocoa window.
3341 void macdrv_hide_cocoa_window(macdrv_window w)
3343 WineWindow* window = (WineWindow*)w;
3346 [window doOrderOut];
3350 /***********************************************************************
3351 * macdrv_set_cocoa_window_frame
3353 * Move a Cocoa window.
3355 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3357 WineWindow* window = (WineWindow*)w;
3360 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3364 /***********************************************************************
3365 * macdrv_get_cocoa_window_frame
3367 * Gets the frame of a Cocoa window.
3369 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3371 WineWindow* window = (WineWindow*)w;
3376 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3377 [[WineApplicationController sharedController] flipRect:&frame];
3378 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3382 /***********************************************************************
3383 * macdrv_set_cocoa_parent_window
3385 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3386 * the parent window.
3388 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3390 WineWindow* window = (WineWindow*)w;
3393 [window setMacDrvParentWindow:(WineWindow*)parent];
3397 /***********************************************************************
3398 * macdrv_set_window_surface
3400 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3402 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3403 WineWindow* window = (WineWindow*)w;
3406 window.surface = surface;
3407 window.surface_mutex = mutex;
3413 /***********************************************************************
3414 * macdrv_window_needs_display
3416 * Mark a window as needing display in a specified rect (in non-client
3417 * area coordinates).
3419 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3421 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3422 WineWindow* window = (WineWindow*)w;
3424 OnMainThreadAsync(^{
3425 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3431 /***********************************************************************
3432 * macdrv_set_window_shape
3434 * Sets the shape of a Cocoa window from an array of rectangles. If
3435 * rects is NULL, resets the window's shape to its frame.
3437 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3439 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3440 WineWindow* window = (WineWindow*)w;
3443 if (!rects || !count)
3445 [window setShape:NULL];
3446 [window checkEmptyShaped];
3450 CGMutablePathRef path;
3453 path = CGPathCreateMutable();
3454 for (i = 0; i < count; i++)
3455 CGPathAddRect(path, NULL, cgrect_mac_from_win(rects[i]));
3456 [window setShape:path];
3457 CGPathRelease(path);
3464 /***********************************************************************
3465 * macdrv_set_window_alpha
3467 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3469 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3470 WineWindow* window = (WineWindow*)w;
3472 [window setAlphaValue:alpha];
3477 /***********************************************************************
3478 * macdrv_set_window_color_key
3480 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3483 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3484 WineWindow* window = (WineWindow*)w;
3487 window.colorKeyed = TRUE;
3488 window.colorKeyRed = keyRed;
3489 window.colorKeyGreen = keyGreen;
3490 window.colorKeyBlue = keyBlue;
3491 [window checkTransparency];
3497 /***********************************************************************
3498 * macdrv_clear_window_color_key
3500 void macdrv_clear_window_color_key(macdrv_window w)
3502 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3503 WineWindow* window = (WineWindow*)w;
3506 window.colorKeyed = FALSE;
3507 [window checkTransparency];
3513 /***********************************************************************
3514 * macdrv_window_use_per_pixel_alpha
3516 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3518 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3519 WineWindow* window = (WineWindow*)w;
3522 window.usePerPixelAlpha = use_per_pixel_alpha;
3523 [window checkTransparency];
3529 /***********************************************************************
3530 * macdrv_give_cocoa_window_focus
3532 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3533 * orders it front and, if its frame was not within the desktop bounds,
3534 * Cocoa will typically move it on-screen.
3536 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3538 WineWindow* window = (WineWindow*)w;
3541 [window makeFocused:activate];
3545 /***********************************************************************
3546 * macdrv_set_window_min_max_sizes
3548 * Sets the window's minimum and maximum content sizes.
3550 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3552 WineWindow* window = (WineWindow*)w;
3555 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3559 /***********************************************************************
3560 * macdrv_create_view
3562 * Creates and returns a view with the specified frame rect. The
3563 * caller is responsible for calling macdrv_dispose_view() on the view
3564 * when it is done with it.
3566 macdrv_view macdrv_create_view(CGRect rect)
3568 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3569 __block WineContentView* view;
3571 if (CGRectIsNull(rect)) rect = CGRectZero;
3574 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3576 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3577 [view setWantsLayer:YES];
3578 [view layer].minificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
3579 [view layer].magnificationFilter = retina_on ? kCAFilterLinear : kCAFilterNearest;
3580 [view layer].contentsScale = retina_on ? 2.0 : 1.0;
3581 [view setAutoresizesSubviews:NO];
3582 [view setAutoresizingMask:NSViewNotSizable];
3583 [view setHidden:YES];
3584 [view setWantsBestResolutionOpenGLSurface:retina_on];
3585 [nc addObserver:view
3586 selector:@selector(updateGLContexts)
3587 name:NSViewGlobalFrameDidChangeNotification
3589 [nc addObserver:view
3590 selector:@selector(updateGLContexts)
3591 name:NSApplicationDidChangeScreenParametersNotification
3596 return (macdrv_view)view;
3599 /***********************************************************************
3600 * macdrv_dispose_view
3602 * Destroys a view previously returned by macdrv_create_view.
3604 void macdrv_dispose_view(macdrv_view v)
3606 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3607 WineContentView* view = (WineContentView*)v;
3610 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3611 WineWindow* window = (WineWindow*)[view window];
3613 [nc removeObserver:view
3614 name:NSViewGlobalFrameDidChangeNotification
3616 [nc removeObserver:view
3617 name:NSApplicationDidChangeScreenParametersNotification
3619 [view removeFromSuperview];
3621 [window updateForGLSubviews];
3627 /***********************************************************************
3628 * macdrv_set_view_frame
3630 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3632 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3633 WineContentView* view = (WineContentView*)v;
3635 if (CGRectIsNull(rect)) rect = CGRectZero;
3637 OnMainThreadAsync(^{
3638 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3639 NSRect oldFrame = [view frame];
3641 if (!NSEqualRects(oldFrame, newFrame))
3643 [[view superview] setNeedsDisplayInRect:oldFrame];
3644 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3645 [view setFrameSize:newFrame.size];
3646 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3647 [view setFrameOrigin:newFrame.origin];
3649 [view setFrame:newFrame];
3650 [view setNeedsDisplay:YES];
3654 int backing_size[2] = { 0 };
3655 [view wine_setBackingSize:backing_size];
3657 [(WineWindow*)[view window] updateForGLSubviews];
3664 /***********************************************************************
3665 * macdrv_set_view_superview
3667 * Move a view to a new superview and position it relative to its
3668 * siblings. If p is non-NULL, the view is ordered behind it.
3669 * Otherwise, the view is ordered above n. If s is NULL, use the
3670 * content view of w as the new superview.
3672 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3674 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3675 WineContentView* view = (WineContentView*)v;
3676 WineContentView* superview = (WineContentView*)s;
3677 WineWindow* window = (WineWindow*)w;
3678 WineContentView* prev = (WineContentView*)p;
3679 WineContentView* next = (WineContentView*)n;
3682 superview = [window contentView];
3684 OnMainThreadAsync(^{
3685 if (superview == [view superview])
3687 NSArray* subviews = [superview subviews];
3688 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3689 if (!prev && !next && index == [subviews count] - 1)
3691 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3693 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3697 WineWindow* oldWindow = (WineWindow*)[view window];
3698 WineWindow* newWindow = (WineWindow*)[superview window];
3700 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3701 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3702 [view removeFromSuperview];
3705 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3707 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3709 if (oldWindow != newWindow)
3711 [oldWindow updateForGLSubviews];
3712 [newWindow updateForGLSubviews];
3719 /***********************************************************************
3720 * macdrv_set_view_hidden
3722 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3724 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3725 WineContentView* view = (WineContentView*)v;
3727 OnMainThreadAsync(^{
3728 [view setHidden:hidden];
3729 [(WineWindow*)view.window updateForGLSubviews];
3735 /***********************************************************************
3736 * macdrv_add_view_opengl_context
3738 * Add an OpenGL context to the list being tracked for each view.
3740 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3742 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3743 WineContentView* view = (WineContentView*)v;
3744 WineOpenGLContext *context = (WineOpenGLContext*)c;
3747 [view addGLContext:context];
3753 /***********************************************************************
3754 * macdrv_remove_view_opengl_context
3756 * Add an OpenGL context to the list being tracked for each view.
3758 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3760 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3761 WineContentView* view = (WineContentView*)v;
3762 WineOpenGLContext *context = (WineOpenGLContext*)c;
3764 OnMainThreadAsync(^{
3765 [view removeGLContext:context];
3771 #ifdef HAVE_METAL_METAL_H
3772 macdrv_metal_device macdrv_create_metal_device(void)
3774 macdrv_metal_device ret;
3776 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3777 if (MTLCreateSystemDefaultDevice == NULL)
3781 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3782 ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3787 void macdrv_release_metal_device(macdrv_metal_device d)
3789 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3790 [(id<MTLDevice>)d release];
3794 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3796 id<MTLDevice> device = (id<MTLDevice>)d;
3797 WineContentView* view = (WineContentView*)v;
3798 __block WineMetalView *metalView;
3801 metalView = [view newMetalViewWithDevice:device];
3804 return (macdrv_metal_view)metalView;
3807 macdrv_metal_layer macdrv_view_get_metal_layer(macdrv_metal_view v)
3809 WineMetalView* view = (WineMetalView*)v;
3810 __block CAMetalLayer* layer;
3813 layer = (CAMetalLayer*)view.layer;
3816 return (macdrv_metal_layer)layer;
3819 void macdrv_view_release_metal_view(macdrv_metal_view v)
3821 WineMetalView* view = (WineMetalView*)v;
3823 [view removeFromSuperview];
3829 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3831 WineContentView* view = (WineContentView*)v;
3833 if (![view isKindOfClass:[WineContentView class]])
3836 [view wine_getBackingSize:backing_size];
3840 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3842 WineContentView* view = (WineContentView*)v;
3844 if ([view isKindOfClass:[WineContentView class]])
3845 [view wine_setBackingSize:backing_size];
3848 /***********************************************************************
3849 * macdrv_window_background_color
3851 * Returns the standard Mac window background color as a 32-bit value of
3852 * the form 0x00rrggbb.
3854 uint32_t macdrv_window_background_color(void)
3856 static uint32_t result;
3857 static dispatch_once_t once;
3859 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3860 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3861 // of it is to draw with it.
3862 dispatch_once(&once, ^{
3864 unsigned char rgbx[4];
3865 unsigned char *planes = rgbx;
3866 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3873 colorSpaceName:NSCalibratedRGBColorSpace
3877 [NSGraphicsContext saveGraphicsState];
3878 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3879 [[NSColor windowBackgroundColor] set];
3880 NSRectFill(NSMakeRect(0, 0, 1, 1));
3881 [NSGraphicsContext restoreGraphicsState];
3883 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3890 /***********************************************************************
3891 * macdrv_send_text_input_event
3893 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data, int* done)
3895 OnMainThreadAsync(^{
3897 macdrv_event* event;
3898 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3899 if (![window isKindOfClass:[WineWindow class]])
3901 window = (WineWindow*)[NSApp mainWindow];
3902 if (![window isKindOfClass:[WineWindow class]])
3903 window = [[WineApplicationController sharedController] frontWineWindow];
3908 NSUInteger localFlags = flags;
3912 window.imeData = data;
3913 fix_device_modifiers_by_generic(&localFlags);
3915 // An NSEvent created with +keyEventWithType:... is internally marked
3916 // as synthetic and doesn't get sent through input methods. But one
3917 // created from a CGEvent doesn't have that problem.
3918 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3919 CGEventSetFlags(c, localFlags);
3920 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3921 event = [NSEvent eventWithCGEvent:c];
3924 window.commandDone = FALSE;
3925 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3930 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3931 event->sent_text_input.handled = ret;
3932 event->sent_text_input.done = done;
3933 [[window queue] postEvent:event];
3934 macdrv_release_event(event);
3938 void macdrv_clear_ime_text(void)
3940 OnMainThreadAsync(^{
3941 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3942 if (![window isKindOfClass:[WineWindow class]])
3944 window = (WineWindow*)[NSApp mainWindow];
3945 if (![window isKindOfClass:[WineWindow class]])
3946 window = [[WineApplicationController sharedController] frontWineWindow];
3949 [[window contentView] clearMarkedText];