2 * MACDRV Cocoa window code
4 * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc.
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with this library; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
23 #define GL_SILENCE_DEPRECATION
24 #import <Carbon/Carbon.h>
25 #import <CoreVideo/CoreVideo.h>
26 #import <Metal/Metal.h>
27 #import <QuartzCore/QuartzCore.h>
29 #import "cocoa_window.h"
31 #include "macdrv_cocoa.h"
33 #import "cocoa_event.h"
34 #import "cocoa_opengl.h"
37 #if !defined(MAC_OS_X_VERSION_10_12) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_12
38 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
40 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
45 @interface NSWindow (PrivatePreventsActivation)
47 /* Needed to ensure proper behavior after adding or removing
48 * NSWindowStyleMaskNonactivatingPanel.
49 * Available since at least macOS 10.6. */
50 - (void)_setPreventsActivation:(BOOL)flag;
55 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
57 NSUInteger style_mask;
61 style_mask = NSWindowStyleMaskTitled;
62 if (wf->close_button) style_mask |= NSWindowStyleMaskClosable;
63 if (wf->minimize_button) style_mask |= NSWindowStyleMaskMiniaturizable;
64 if (wf->resizable || wf->maximize_button) style_mask |= NSWindowStyleMaskResizable;
65 if (wf->utility) style_mask |= NSWindowStyleMaskUtilityWindow;
67 else style_mask = NSWindowStyleMaskBorderless;
69 if (wf->prevents_app_activation) style_mask |= NSWindowStyleMaskNonactivatingPanel;
75 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
78 for (screen in screens)
80 if (NSIntersectsRect(frame, [screen frame]))
87 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
89 for (NSScreen* screen in screens)
91 if (NSContainsRect(rect, [screen frame]))
98 /* We rely on the supposedly device-dependent modifier flags to distinguish the
99 keys on the left side of the keyboard from those on the right. Some event
100 sources don't set those device-depdendent flags. If we see a device-independent
101 flag for a modifier without either corresponding device-dependent flag, assume
103 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
105 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
106 *modifiers |= NX_DEVICELCMDKEYMASK;
107 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
108 *modifiers |= NX_DEVICELSHIFTKEYMASK;
109 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
110 *modifiers |= NX_DEVICELCTLKEYMASK;
111 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
112 *modifiers |= NX_DEVICELALTKEYMASK;
115 /* As we manipulate individual bits of a modifier mask, we can end up with
116 inconsistent sets of flags. In particular, we might set or clear one of the
117 left/right-specific bits, but not the corresponding non-side-specific bit.
118 Fix that. If either side-specific bit is set, set the non-side-specific bit,
119 otherwise clear it. */
120 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
122 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
123 *modifiers |= NX_COMMANDMASK;
125 *modifiers &= ~NX_COMMANDMASK;
126 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
127 *modifiers |= NX_SHIFTMASK;
129 *modifiers &= ~NX_SHIFTMASK;
130 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
131 *modifiers |= NX_CONTROLMASK;
133 *modifiers &= ~NX_CONTROLMASK;
134 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
135 *modifiers |= NX_ALTERNATEMASK;
137 *modifiers &= ~NX_ALTERNATEMASK;
140 static inline NSUInteger adjusted_modifiers_for_settings(NSUInteger modifiers)
142 fix_device_modifiers_by_generic(&modifiers);
143 NSUInteger new_modifiers = modifiers & ~(NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK |
144 NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK);
146 // The MACDRV keyboard driver translates Command keys to Alt. If the
147 // Option key (NX_DEVICE[LR]ALTKEYMASK) should behave like Alt in
148 // Windows, rewrite it to Command (NX_DEVICE[LR]CMDKEYMASK).
149 if (modifiers & NX_DEVICELALTKEYMASK)
150 new_modifiers |= left_option_is_alt ? NX_DEVICELCMDKEYMASK : NX_DEVICELALTKEYMASK;
151 if (modifiers & NX_DEVICERALTKEYMASK)
152 new_modifiers |= right_option_is_alt ? NX_DEVICERCMDKEYMASK : NX_DEVICERALTKEYMASK;
154 if (modifiers & NX_DEVICELCMDKEYMASK)
155 new_modifiers |= left_command_is_ctrl ? NX_DEVICELCTLKEYMASK : NX_DEVICELCMDKEYMASK;
156 if (modifiers & NX_DEVICERCMDKEYMASK)
157 new_modifiers |= right_command_is_ctrl ? NX_DEVICERCTLKEYMASK : NX_DEVICERCMDKEYMASK;
159 fix_generic_modifiers_by_device(&new_modifiers);
160 return new_modifiers;
164 @interface NSWindow (WineAccessPrivateMethods)
165 - (id) _displayChanged;
169 @interface WineDisplayLink : NSObject
171 CGDirectDisplayID _displayID;
172 CVDisplayLinkRef _link;
173 NSMutableSet* _windows;
175 NSTimeInterval _actualRefreshPeriod;
176 NSTimeInterval _nominalRefreshPeriod;
178 NSTimeInterval _lastDisplayTime;
181 - (id) initWithDisplayID:(CGDirectDisplayID)displayID;
183 - (void) addWindow:(WineWindow*)window;
184 - (void) removeWindow:(WineWindow*)window;
186 - (NSTimeInterval) refreshPeriod;
192 @implementation WineDisplayLink
194 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext);
196 - (id) initWithDisplayID:(CGDirectDisplayID)displayID
201 CVReturn status = CVDisplayLinkCreateWithCGDisplay(displayID, &_link);
202 if (status == kCVReturnSuccess && !_link)
203 status = kCVReturnError;
204 if (status == kCVReturnSuccess)
205 status = CVDisplayLinkSetOutputCallback(_link, WineDisplayLinkCallback, self);
206 if (status != kCVReturnSuccess)
212 _displayID = displayID;
213 _windows = [[NSMutableSet alloc] init];
222 CVDisplayLinkStop(_link);
223 CVDisplayLinkRelease(_link);
229 - (void) addWindow:(WineWindow*)window
232 @synchronized(self) {
233 firstWindow = !_windows.count;
234 [_windows addObject:window];
236 if (firstWindow || !CVDisplayLinkIsRunning(_link))
240 - (void) removeWindow:(WineWindow*)window
242 BOOL lastWindow = FALSE;
243 @synchronized(self) {
244 BOOL hadWindows = _windows.count > 0;
245 [_windows removeObject:window];
246 if (hadWindows && !_windows.count)
249 if (lastWindow && CVDisplayLinkIsRunning(_link))
250 CVDisplayLinkStop(_link);
256 @synchronized(self) {
257 windows = [_windows copy];
259 dispatch_async(dispatch_get_main_queue(), ^{
260 BOOL anyDisplayed = FALSE;
261 for (WineWindow* window in windows)
263 if ([window viewsNeedDisplay])
265 [window displayIfNeeded];
270 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
272 _lastDisplayTime = now;
273 else if (_lastDisplayTime + 2.0 < now)
274 CVDisplayLinkStop(_link);
279 - (NSTimeInterval) refreshPeriod
281 if (_actualRefreshPeriod || (_actualRefreshPeriod = CVDisplayLinkGetActualOutputVideoRefreshPeriod(_link)))
282 return _actualRefreshPeriod;
284 if (_nominalRefreshPeriod)
285 return _nominalRefreshPeriod;
287 CVTime time = CVDisplayLinkGetNominalOutputVideoRefreshPeriod(_link);
288 if (time.flags & kCVTimeIsIndefinite)
290 _nominalRefreshPeriod = time.timeValue / (double)time.timeScale;
291 return _nominalRefreshPeriod;
296 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
297 CVDisplayLinkStart(_link);
300 static CVReturn WineDisplayLinkCallback(CVDisplayLinkRef displayLink, const CVTimeStamp* inNow, const CVTimeStamp* inOutputTime, CVOptionFlags flagsIn, CVOptionFlags* flagsOut, void* displayLinkContext)
302 WineDisplayLink* link = displayLinkContext;
304 return kCVReturnSuccess;
310 #ifndef MAC_OS_X_VERSION_10_14
311 @protocol NSViewLayerContentScaleDelegate <NSObject>
314 - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window;
320 @interface CAShapeLayer (WineShapeMaskExtensions)
322 @property(readonly, nonatomic, getter=isEmptyShaped) BOOL emptyShaped;
326 @implementation CAShapeLayer (WineShapeMaskExtensions)
328 - (BOOL) isEmptyShaped
330 return CGRectEqualToRect(CGPathGetBoundingBox(self.path), CGRectZero);
336 @interface WineBaseView : NSView
340 @interface WineMetalView : WineBaseView
342 id<MTLDevice> _device;
345 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device;
350 @interface WineContentView : WineBaseView <NSTextInputClient, NSViewLayerContentScaleDelegate>
352 NSMutableArray* glContexts;
353 NSMutableArray* pendingGlContexts;
354 BOOL _everHadGLContext;
355 BOOL _cachedHasGLDescendant;
356 BOOL _cachedHasGLDescendantValid;
357 BOOL clearedGlSurface;
359 NSMutableAttributedString* markedText;
360 NSRange markedTextSelection;
364 WineMetalView *_metalView;
367 @property (readonly, nonatomic) BOOL everHadGLContext;
369 - (void) addGLContext:(WineOpenGLContext*)context;
370 - (void) removeGLContext:(WineOpenGLContext*)context;
371 - (void) updateGLContexts;
373 - (void) wine_getBackingSize:(int*)outBackingSize;
374 - (void) wine_setBackingSize:(const int*)newBackingSize;
376 - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device;
381 @interface WineWindow ()
383 @property (readwrite, nonatomic) BOOL disabled;
384 @property (readwrite, nonatomic) BOOL noForeground;
385 @property (readwrite, nonatomic) BOOL preventsAppActivation;
386 @property (readwrite, nonatomic) BOOL floating;
387 @property (readwrite, nonatomic) BOOL drawnSinceShown;
388 @property (readwrite, nonatomic) BOOL closing;
389 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
390 @property (retain, nonatomic) NSWindow* latentParentWindow;
392 @property (nonatomic) void* hwnd;
393 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
395 @property (nonatomic) void* surface;
396 @property (nonatomic) pthread_mutex_t* surface_mutex;
398 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
399 @property (readonly, nonatomic) BOOL needsTransparency;
401 @property (nonatomic) BOOL colorKeyed;
402 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
403 @property (nonatomic) BOOL usePerPixelAlpha;
405 @property (assign, nonatomic) void* himc;
406 @property (nonatomic) BOOL commandDone;
408 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
410 - (void) setShape:(CGPathRef)newShape;
412 - (void) updateForGLSubviews;
414 - (BOOL) becameEligibleParentOrChild;
415 - (void) becameIneligibleChild;
417 - (void) windowDidDrawContent;
422 @implementation WineBaseView
424 - (void) setRetinaMode:(int)mode
426 for (WineBaseView* subview in [self subviews])
428 if ([subview isKindOfClass:[WineBaseView class]])
429 [subview setRetinaMode:mode];
433 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
438 - (BOOL) preservesContentDuringLiveResize
440 // Returning YES from this tells Cocoa to keep our view's content during
441 // a Cocoa-driven resize. In theory, we're also supposed to override
442 // -setFrameSize: to mark exposed sections as needing redisplay, but
443 // user32 will take care of that in a roundabout way. This way, we don't
444 // redraw until the window surface is flushed.
446 // This doesn't do anything when we resize the window ourselves.
450 - (BOOL)acceptsFirstResponder
452 return [[self window] contentView] == self;
455 - (BOOL) mouseDownCanMoveWindow
460 - (NSFocusRingType) focusRingType
462 return NSFocusRingTypeNone;
468 @implementation WineContentView
470 @synthesize everHadGLContext = _everHadGLContext;
472 - (instancetype) initWithFrame:(NSRect)frame
474 self = [super initWithFrame:frame];
477 [self setWantsLayer:YES];
478 [self setLayerRetinaProperties:retina_on];
479 [self setAutoresizesSubviews:NO];
486 [markedText release];
487 [glContexts release];
488 [pendingGlContexts release];
497 - (BOOL) wantsUpdateLayer
499 return YES /*!_everHadGLContext*/;
504 WineWindow* window = (WineWindow*)[self window];
505 CGImageRef image = NULL;
507 CALayer* layer = [self layer];
509 if ([window contentView] != self)
512 if (window.closing || !window.surface || !window.surface_mutex)
515 pthread_mutex_lock(window.surface_mutex);
516 if (get_surface_blit_rects(window.surface, NULL, NULL))
518 imageRect = layer.bounds;
519 imageRect.origin.x *= layer.contentsScale;
520 imageRect.origin.y *= layer.contentsScale;
521 imageRect.size.width *= layer.contentsScale;
522 imageRect.size.height *= layer.contentsScale;
523 image = create_surface_image(window.surface, &imageRect, FALSE, window.colorKeyed,
524 window.colorKeyRed, window.colorKeyGreen, window.colorKeyBlue);
526 pthread_mutex_unlock(window.surface_mutex);
530 layer.contents = (id)image;
532 [window windowDidDrawContent];
534 // If the window may be transparent, then we have to invalidate the
535 // shadow every time we draw. Also, if this is the first time we've
536 // drawn since changing from transparent to opaque.
537 if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
539 window.shapeChangedSinceLastDraw = FALSE;
540 [window invalidateShadow];
545 - (void) viewWillDraw
547 [super viewWillDraw];
549 for (WineOpenGLContext* context in pendingGlContexts)
551 if (!clearedGlSurface)
553 context.shouldClearToBlack = TRUE;
554 clearedGlSurface = TRUE;
556 context.needsUpdate = TRUE;
558 [glContexts addObjectsFromArray:pendingGlContexts];
559 [pendingGlContexts removeAllObjects];
562 - (void) addGLContext:(WineOpenGLContext*)context
564 BOOL hadContext = _everHadGLContext;
566 glContexts = [[NSMutableArray alloc] init];
567 if (!pendingGlContexts)
568 pendingGlContexts = [[NSMutableArray alloc] init];
570 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
572 [glContexts addObject:context];
573 if (!clearedGlSurface)
575 context.shouldClearToBlack = TRUE;
576 clearedGlSurface = TRUE;
578 context.needsUpdate = TRUE;
582 [pendingGlContexts addObject:context];
583 [self setNeedsDisplay:YES];
586 _everHadGLContext = YES;
588 [self invalidateHasGLDescendant];
589 [(WineWindow*)[self window] updateForGLSubviews];
592 - (void) removeGLContext:(WineOpenGLContext*)context
594 [glContexts removeObjectIdenticalTo:context];
595 [pendingGlContexts removeObjectIdenticalTo:context];
596 [(WineWindow*)[self window] updateForGLSubviews];
599 - (void) updateGLContexts:(BOOL)reattach
601 for (WineOpenGLContext* context in glContexts)
603 context.needsUpdate = TRUE;
605 context.needsReattach = TRUE;
609 - (void) updateGLContexts
611 [self updateGLContexts:NO];
614 - (BOOL) _hasGLDescendant
618 if (_everHadGLContext)
620 for (WineContentView* view in [self subviews])
622 if ([view isKindOfClass:[WineContentView class]] && [view hasGLDescendant])
628 - (BOOL) hasGLDescendant
630 if (!_cachedHasGLDescendantValid)
632 _cachedHasGLDescendant = [self _hasGLDescendant];
633 _cachedHasGLDescendantValid = YES;
635 return _cachedHasGLDescendant;
638 - (void) invalidateHasGLDescendant
640 BOOL invalidateAncestors = _cachedHasGLDescendantValid;
641 _cachedHasGLDescendantValid = NO;
642 if (invalidateAncestors && self != [[self window] contentView])
644 WineContentView* superview = (WineContentView*)[self superview];
645 if ([superview isKindOfClass:[WineContentView class]])
646 [superview invalidateHasGLDescendant];
650 - (void) wine_getBackingSize:(int*)outBackingSize
652 @synchronized(self) {
653 memcpy(outBackingSize, backingSize, sizeof(backingSize));
656 - (void) wine_setBackingSize:(const int*)newBackingSize
658 @synchronized(self) {
659 memcpy(backingSize, newBackingSize, sizeof(backingSize));
663 - (WineMetalView*) newMetalViewWithDevice:(id<MTLDevice>)device
665 if (_metalView) return _metalView;
667 WineMetalView* view = [[WineMetalView alloc] initWithFrame:[self bounds] device:device];
668 [view setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable];
669 [self setAutoresizesSubviews:YES];
670 [self addSubview:view positioned:NSWindowBelow relativeTo:nil];
673 [(WineWindow*)self.window windowDidDrawContent];
678 - (void) setLayerRetinaProperties:(int)mode
680 [self layer].contentsScale = mode ? 2.0 : 1.0;
681 [self layer].minificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
682 [self layer].magnificationFilter = mode ? kCAFilterLinear : kCAFilterNearest;
684 /* On macOS 10.13 and earlier, the desired minificationFilter seems to be
685 * ignored and "nearest" filtering is used, which looks terrible.
686 * Enabling rasterization seems to work around this, only enable
687 * it when there may be down-scaling (retina mode enabled).
689 if (floor(NSAppKitVersionNumber) < 1671 /*NSAppKitVersionNumber10_14*/)
693 [self layer].shouldRasterize = YES;
694 [self layer].rasterizationScale = 2.0;
697 [self layer].shouldRasterize = NO;
701 - (void) setRetinaMode:(int)mode
703 double scale = mode ? 0.5 : 2.0;
704 NSRect frame = self.frame;
705 frame.origin.x *= scale;
706 frame.origin.y *= scale;
707 frame.size.width *= scale;
708 frame.size.height *= scale;
709 [self setFrame:frame];
710 [self setWantsBestResolutionOpenGLSurface:mode];
711 [self updateGLContexts];
712 [self setLayerRetinaProperties:mode];
714 [super setRetinaMode:mode];
717 - (BOOL) layer:(CALayer*)layer shouldInheritContentsScale:(CGFloat)newScale fromWindow:(NSWindow*)window
719 /* This method is invoked when the contentsScale of the layer is not
720 * equal to the contentsScale of the window.
721 * (Initially when the layer is first created, and later if the window
722 * contentsScale changes, i.e. moved between retina/non-retina monitors).
724 * We usually want to return YES, so the "moving windows between
725 * retina/non-retina monitors" case works right.
726 * But return NO when we need an intentional mismatch between the
727 * window and layer contentsScale
728 * (non-retina mode with a retina monitor, and vice-versa).
730 if (layer.contentsScale != window.backingScaleFactor)
738 [self invalidateHasGLDescendant];
741 - (void) viewDidUnhide
743 [super viewDidUnhide];
744 [self updateGLContexts:YES];
745 [self invalidateHasGLDescendant];
748 - (void) clearMarkedText
750 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
751 markedTextSelection = NSMakeRange(0, 0);
752 [[self inputContext] discardMarkedText];
755 - (void) completeText:(NSString*)text
758 WineWindow* window = (WineWindow*)[self window];
760 event = macdrv_create_event(IM_SET_TEXT, window);
761 event->im_set_text.himc = [window himc];
762 event->im_set_text.text = (CFStringRef)[text copy];
763 event->im_set_text.complete = TRUE;
765 [[window queue] postEvent:event];
767 macdrv_release_event(event);
769 [self clearMarkedText];
772 - (void) didAddSubview:(NSView*)subview
774 if ([subview isKindOfClass:[WineContentView class]])
776 WineContentView* view = (WineContentView*)subview;
777 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
778 [self invalidateHasGLDescendant];
780 [super didAddSubview:subview];
783 - (void) willRemoveSubview:(NSView*)subview
785 if ([subview isKindOfClass:[WineContentView class]])
787 WineContentView* view = (WineContentView*)subview;
788 if (!view->_cachedHasGLDescendantValid || view->_cachedHasGLDescendant)
789 [self invalidateHasGLDescendant];
791 if (subview == _metalView)
793 [super willRemoveSubview:subview];
796 - (void) setLayer:(CALayer*)newLayer
798 [super setLayer:newLayer];
799 [self updateGLContexts];
803 * ---------- NSTextInputClient methods ----------
805 - (NSTextInputContext*) inputContext
808 markedText = [[NSMutableAttributedString alloc] init];
809 return [super inputContext];
812 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
814 if ([string isKindOfClass:[NSAttributedString class]])
815 string = [string string];
817 if ([string isKindOfClass:[NSString class]])
818 [self completeText:string];
821 - (void) doCommandBySelector:(SEL)aSelector
823 [(WineWindow*)[self window] setCommandDone:TRUE];
826 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
828 if ([string isKindOfClass:[NSAttributedString class]])
829 string = [string string];
831 if ([string isKindOfClass:[NSString class]])
834 WineWindow* window = (WineWindow*)[self window];
836 if (replacementRange.location == NSNotFound)
837 replacementRange = NSMakeRange(0, [markedText length]);
839 [markedText replaceCharactersInRange:replacementRange withString:string];
840 markedTextSelection = selectedRange;
841 markedTextSelection.location += replacementRange.location;
843 event = macdrv_create_event(IM_SET_TEXT, window);
844 event->im_set_text.himc = [window himc];
845 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
846 event->im_set_text.complete = FALSE;
847 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
849 [[window queue] postEvent:event];
851 macdrv_release_event(event);
853 [[self inputContext] invalidateCharacterCoordinates];
859 [self completeText:nil];
862 - (NSRange) selectedRange
864 return markedTextSelection;
867 - (NSRange) markedRange
869 NSRange range = NSMakeRange(0, [markedText length]);
871 range.location = NSNotFound;
875 - (BOOL) hasMarkedText
877 return [markedText length] > 0;
880 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
882 if (aRange.location >= [markedText length])
885 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
887 *actualRange = aRange;
888 return [markedText attributedSubstringFromRange:aRange];
891 - (NSArray*) validAttributesForMarkedText
893 return [NSArray array];
896 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
899 WineWindow* window = (WineWindow*)[self window];
902 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
904 query = macdrv_create_query();
905 query->type = QUERY_IME_CHAR_RECT;
906 query->window = (macdrv_window)[window retain];
907 query->ime_char_rect.himc = [window himc];
908 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
910 if ([window.queue query:query timeout:0.3 flags:WineQueryNoPreemptWait])
912 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
913 ret = NSRectFromCGRect(cgrect_mac_from_win(query->ime_char_rect.rect));
914 [[WineApplicationController sharedController] flipRect:&ret];
917 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
919 macdrv_release_query(query);
922 *actualRange = aRange;
926 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
931 - (NSInteger) windowLevel
933 return [[self window] level];
939 @implementation WineMetalView
941 - (id) initWithFrame:(NSRect)frame device:(id<MTLDevice>)device
943 self = [super initWithFrame:frame];
946 _device = [device retain];
947 self.wantsLayer = YES;
948 self.layerContentsRedrawPolicy = NSViewLayerContentsRedrawNever;
959 - (void) setRetinaMode:(int)mode
961 self.layer.contentsScale = mode ? 2.0 : 1.0;
962 [super setRetinaMode:mode];
965 - (CALayer*) makeBackingLayer
967 CAMetalLayer *layer = [CAMetalLayer layer];
968 layer.device = _device;
969 layer.framebufferOnly = YES;
970 layer.magnificationFilter = kCAFilterNearest;
971 layer.backgroundColor = CGColorGetConstantColor(kCGColorBlack);
972 layer.contentsScale = retina_on ? 2.0 : 1.0;
984 @implementation WineWindow
986 static WineWindow* causing_becomeKeyWindow;
988 @synthesize disabled, noForeground, preventsAppActivation, floating, fullscreen, fakingClose, closing, latentParentWindow, hwnd, queue;
989 @synthesize drawnSinceShown;
990 @synthesize surface, surface_mutex;
991 @synthesize shapeChangedSinceLastDraw;
992 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
993 @synthesize usePerPixelAlpha;
994 @synthesize himc, commandDone;
996 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
997 windowFrame:(NSRect)window_frame
999 queue:(WineEventQueue*)queue
1002 WineContentView* contentView;
1003 NSTrackingArea* trackingArea;
1004 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1006 [[WineApplicationController sharedController] flipRect:&window_frame];
1008 window = [[[self alloc] initWithContentRect:window_frame
1009 styleMask:style_mask_for_features(wf)
1010 backing:NSBackingStoreBuffered
1011 defer:YES] autorelease];
1013 if (!window) return nil;
1015 /* Standardize windows to eliminate differences between titled and
1016 borderless windows and between NSWindow and NSPanel. */
1017 [window setHidesOnDeactivate:NO];
1018 [window setReleasedWhenClosed:NO];
1020 [window setOneShot:YES];
1021 [window disableCursorRects];
1022 [window setShowsResizeIndicator:NO];
1023 [window setHasShadow:wf->shadow];
1024 [window setAcceptsMouseMovedEvents:YES];
1025 [window setDelegate:window];
1026 [window setBackgroundColor:[NSColor clearColor]];
1027 [window setOpaque:NO];
1029 window.queue = queue;
1030 window->savedContentMinSize = NSZeroSize;
1031 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
1032 window->resizable = wf->resizable;
1033 window->_lastDisplayTime = [[NSDate distantPast] timeIntervalSinceReferenceDate];
1035 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
1036 (NSString*)kUTTypeContent,
1039 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
1043 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
1044 because they give us mouse moves in the background. */
1045 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
1046 options:(NSTrackingMouseMoved |
1047 NSTrackingActiveAlways |
1048 NSTrackingInVisibleRect)
1050 userInfo:nil] autorelease];
1053 [contentView addTrackingArea:trackingArea];
1055 [window setContentView:contentView];
1056 [window setInitialFirstResponder:contentView];
1058 [nc addObserver:window
1059 selector:@selector(updateFullscreen)
1060 name:NSApplicationDidChangeScreenParametersNotification
1062 [window updateFullscreen];
1064 [nc addObserver:window
1065 selector:@selector(applicationWillHide)
1066 name:NSApplicationWillHideNotification
1068 [nc addObserver:window
1069 selector:@selector(applicationDidUnhide)
1070 name:NSApplicationDidUnhideNotification
1073 [[[NSWorkspace sharedWorkspace] notificationCenter] addObserver:window
1074 selector:@selector(checkWineDisplayLink)
1075 name:NSWorkspaceActiveSpaceDidChangeNotification
1076 object:[NSWorkspace sharedWorkspace]];
1078 [window setFrameAndWineFrame:[window frameRectForContentRect:window_frame]];
1085 [[[NSWorkspace sharedWorkspace] notificationCenter] removeObserver:self];
1086 [[NSNotificationCenter defaultCenter] removeObserver:self];
1088 [latentChildWindows release];
1089 [latentParentWindow release];
1093 - (BOOL) preventResizing
1095 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
1096 return ([self styleMask] & NSWindowStyleMaskResizable) && (disabled || !resizable || preventForClipping);
1099 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
1101 if (allow_immovable_windows && (disabled || inMaximized))
1103 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
1109 - (void) adjustFeaturesForState
1111 NSUInteger style = [self styleMask];
1113 if (style & NSWindowStyleMaskClosable)
1114 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
1115 if (style & NSWindowStyleMaskMiniaturizable)
1116 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
1117 if (style & NSWindowStyleMaskResizable)
1118 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
1119 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
1120 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
1122 if ([self preventResizing])
1124 NSSize size = [self contentRectForFrameRect:self.wine_fractionalFrame].size;
1125 [self setContentMinSize:size];
1126 [self setContentMaxSize:size];
1130 [self setContentMaxSize:savedContentMaxSize];
1131 [self setContentMinSize:savedContentMinSize];
1134 if (allow_immovable_windows || cursor_clipping_locks_windows)
1135 [self setMovable:[self allowsMovingWithMaximized:maximized]];
1138 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
1140 NSUInteger style = [self styleMask];
1142 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
1143 style & NSWindowStyleMaskResizable && !(style & NSWindowStyleMaskUtilityWindow) && !maximized &&
1144 !(self.parentWindow || self.latentParentWindow))
1146 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
1147 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
1151 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
1152 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
1153 if (style & NSWindowStyleMaskFullScreen)
1154 [super toggleFullScreen:nil];
1157 if (behavior != [self collectionBehavior])
1159 [self setCollectionBehavior:behavior];
1160 [self adjustFeaturesForState];
1164 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
1166 static const NSUInteger usedStyles = NSWindowStyleMaskTitled | NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable |
1167 NSWindowStyleMaskResizable | NSWindowStyleMaskUtilityWindow | NSWindowStyleMaskBorderless |
1168 NSWindowStyleMaskNonactivatingPanel;
1169 NSUInteger currentStyle = [self styleMask];
1170 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
1172 self.preventsAppActivation = wf->prevents_app_activation;
1174 if (newStyle != currentStyle)
1176 NSString* title = [[[self title] copy] autorelease];
1177 BOOL showingButtons = (currentStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1178 BOOL shouldShowButtons = (newStyle & (NSWindowStyleMaskClosable | NSWindowStyleMaskMiniaturizable | NSWindowStyleMaskResizable)) != 0;
1179 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSWindowStyleMaskClosable))
1181 // -setStyleMask: is buggy on 10.7+ with respect to NSWindowStyleMaskResizable.
1182 // If transitioning from NSWindowStyleMaskTitled | NSWindowStyleMaskResizable to
1183 // just NSWindowStyleMaskTitled, the window buttons should disappear rather
1184 // than just being disabled. But they don't. Similarly in reverse.
1185 // The workaround is to also toggle NSWindowStyleMaskClosable at the same time.
1186 [self setStyleMask:newStyle ^ NSWindowStyleMaskClosable];
1188 [self setStyleMask:newStyle];
1190 BOOL isNonActivating = (currentStyle & NSWindowStyleMaskNonactivatingPanel) != 0;
1191 BOOL shouldBeNonActivating = (newStyle & NSWindowStyleMaskNonactivatingPanel) != 0;
1192 if (isNonActivating != shouldBeNonActivating) {
1193 // Changing NSWindowStyleMaskNonactivatingPanel with -setStyleMask is also
1194 // buggy. If it's added, clicking the title bar will still activate the
1195 // app. If it's removed, nothing changes at all.
1196 // This private method ensures the correct behavior.
1197 if ([self respondsToSelector:@selector(_setPreventsActivation:)])
1198 [self _setPreventsActivation:shouldBeNonActivating];
1201 // -setStyleMask: resets the firstResponder to the window. Set it
1202 // back to the content view.
1203 if ([[self contentView] acceptsFirstResponder])
1204 [self makeFirstResponder:[self contentView]];
1206 [self adjustFullScreenBehavior:[self collectionBehavior]];
1208 if ([[self title] length] == 0 && [title length] > 0)
1209 [self setTitle:title];
1212 resizable = wf->resizable;
1213 [self adjustFeaturesForState];
1214 [self setHasShadow:wf->shadow];
1217 // Indicates if the window would be visible if the app were not hidden.
1218 - (BOOL) wouldBeVisible
1220 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
1223 - (BOOL) isOrderedIn
1225 return [self wouldBeVisible] || [self isMiniaturized];
1228 - (NSInteger) minimumLevelForActive:(BOOL)active
1232 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
1233 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
1234 level = NSFloatingWindowLevel;
1236 level = NSNormalWindowLevel;
1242 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
1244 if (captured || fullscreen)
1247 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
1249 level = NSStatusWindowLevel + 1;
1259 - (void) postDidUnminimizeEvent
1261 macdrv_event* event;
1263 /* Coalesce events by discarding any previous ones still in the queue. */
1264 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1267 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1268 [queue postEvent:event];
1269 macdrv_release_event(event);
1272 - (void) sendResizeStartQuery
1274 macdrv_query* query = macdrv_create_query();
1275 query->type = QUERY_RESIZE_START;
1276 query->window = (macdrv_window)[self retain];
1278 [self.queue query:query timeout:0.3];
1279 macdrv_release_query(query);
1282 - (void) setMacDrvState:(const struct macdrv_window_state*)state
1284 NSWindowCollectionBehavior behavior;
1286 self.disabled = state->disabled;
1287 self.noForeground = state->no_foreground;
1289 if (self.floating != state->floating)
1291 self.floating = state->floating;
1292 if (state->floating)
1294 // Became floating. If child of non-floating window, make that
1295 // relationship latent.
1296 WineWindow* parent = (WineWindow*)[self parentWindow];
1297 if (parent && !parent.floating)
1298 [self becameIneligibleChild];
1302 // Became non-floating. If parent of floating children, make that
1303 // relationship latent.
1305 for (child in [self childWineWindows])
1308 [child becameIneligibleChild];
1312 // Check our latent relationships. If floating status was the only
1313 // reason they were latent, then make them active.
1314 if ([self isVisible])
1315 [self becameEligibleParentOrChild];
1317 [[WineApplicationController sharedController] adjustWindowLevels];
1320 if (state->minimized_valid)
1322 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1324 pendingMinimize = FALSE;
1325 if (state->minimized && ![self isMiniaturized])
1327 if ([self wouldBeVisible])
1329 if ([self styleMask] & NSWindowStyleMaskFullScreen)
1331 [self postDidUnminimizeEvent];
1332 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
1336 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1337 [super miniaturize:nil];
1338 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1339 event_mask_for_type(WINDOW_GOT_FOCUS) |
1340 event_mask_for_type(WINDOW_LOST_FOCUS);
1344 pendingMinimize = TRUE;
1346 else if (!state->minimized && [self isMiniaturized])
1348 ignore_windowDeminiaturize = TRUE;
1349 [self deminiaturize:nil];
1350 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
1354 [queue discardEventsMatchingMask:discard forWindow:self];
1357 if (state->maximized != maximized)
1359 maximized = state->maximized;
1360 [self adjustFeaturesForState];
1362 if (!maximized && [self inLiveResize])
1363 [self sendResizeStartQuery];
1366 behavior = NSWindowCollectionBehaviorDefault;
1367 if (state->excluded_by_expose)
1368 behavior |= NSWindowCollectionBehaviorTransient;
1370 behavior |= NSWindowCollectionBehaviorManaged;
1371 if (state->excluded_by_cycle)
1373 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
1374 if ([self isOrderedIn])
1375 [NSApp removeWindowsItem:self];
1379 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
1380 if ([self isOrderedIn])
1381 [NSApp addWindowsItem:self title:[self title] filename:NO];
1383 [self adjustFullScreenBehavior:behavior];
1386 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
1388 BOOL reordered = FALSE;
1390 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
1392 if ([self level] > [child level])
1393 [child setLevel:[self level]];
1394 if (![child isVisible])
1395 [child setAutodisplay:YES];
1396 [self addChildWindow:child ordered:NSWindowAbove];
1397 [child checkWineDisplayLink];
1398 [latentChildWindows removeObjectIdenticalTo:child];
1399 child.latentParentWindow = nil;
1404 if (!latentChildWindows)
1405 latentChildWindows = [[NSMutableArray alloc] init];
1406 if (![latentChildWindows containsObject:child])
1407 [latentChildWindows addObject:child];
1408 child.latentParentWindow = self;
1414 - (BOOL) addChildWineWindow:(WineWindow*)child
1416 return [self addChildWineWindow:child assumeVisible:FALSE];
1419 - (void) removeChildWineWindow:(WineWindow*)child
1421 [self removeChildWindow:child];
1422 if (child.latentParentWindow == self)
1423 child.latentParentWindow = nil;
1424 [latentChildWindows removeObjectIdenticalTo:child];
1427 - (void) setChildWineWindows:(NSArray*)childWindows
1429 NSArray* origChildren;
1430 NSUInteger count, start, limit, i;
1432 origChildren = self.childWineWindows;
1434 // If the current and desired children arrays match up to a point, leave
1435 // those matching children alone.
1436 count = childWindows.count;
1437 limit = MIN(origChildren.count, count);
1438 for (start = 0; start < limit; start++)
1440 if ([origChildren objectAtIndex:start] != [childWindows objectAtIndex:start])
1444 // Remove all of the child windows and re-add them back-to-front so they
1445 // are in the desired order.
1446 for (i = start; i < count; i++)
1448 WineWindow* child = [childWindows objectAtIndex:i];
1449 [self removeChildWindow:child];
1451 for (i = start; i < count; i++)
1453 WineWindow* child = [childWindows objectAtIndex:i];
1454 [self addChildWindow:child ordered:NSWindowAbove];
1458 static NSComparisonResult compare_windows_back_to_front(NSWindow* window1, NSWindow* window2, NSArray* windowNumbers)
1460 NSNumber* window1Number = [NSNumber numberWithInteger:[window1 windowNumber]];
1461 NSNumber* window2Number = [NSNumber numberWithInteger:[window2 windowNumber]];
1462 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1463 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1464 if (index1 == NSNotFound)
1466 if (index2 == NSNotFound)
1467 return NSOrderedSame;
1469 return NSOrderedAscending;
1471 else if (index2 == NSNotFound)
1472 return NSOrderedDescending;
1473 else if (index1 < index2)
1474 return NSOrderedDescending;
1475 else if (index2 < index1)
1476 return NSOrderedAscending;
1478 return NSOrderedSame;
1481 - (BOOL) becameEligibleParentOrChild
1483 BOOL reordered = FALSE;
1486 if (latentParentWindow.floating || !self.floating)
1488 // If we aren't visible currently, we assume that we should be and soon
1489 // will be. So, if the latent parent is visible that's enough to assume
1490 // we can establish the parent-child relationship in Cocoa. That will
1491 // actually make us visible, which is fine.
1492 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
1496 // Here, though, we may not actually be visible yet and adding a child
1497 // won't make us visible. The caller will have to call this method
1498 // again after actually making us visible.
1499 if ([self isVisible] && (count = [latentChildWindows count]))
1501 NSMutableArray* windowNumbers;
1502 NSMutableArray* childWindows = [[self.childWineWindows mutableCopy] autorelease];
1503 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
1506 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1508 for (i = 0; i < count; i++)
1510 WineWindow* child = [latentChildWindows objectAtIndex:i];
1511 if ([child isVisible] && (self.floating || !child.floating))
1513 if (child.latentParentWindow == self)
1515 if ([self level] > [child level])
1516 [child setLevel:[self level]];
1517 [childWindows addObject:child];
1518 child.latentParentWindow = nil;
1522 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1523 [indexesToRemove addIndex:i];
1527 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1529 [childWindows sortWithOptions:NSSortStable
1530 usingComparator:^NSComparisonResult(id obj1, id obj2){
1531 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1533 [self setChildWineWindows:childWindows];
1539 - (void) becameIneligibleChild
1541 WineWindow* parent = (WineWindow*)[self parentWindow];
1544 if (!parent->latentChildWindows)
1545 parent->latentChildWindows = [[NSMutableArray alloc] init];
1546 [parent->latentChildWindows insertObject:self atIndex:0];
1547 self.latentParentWindow = parent;
1548 [parent removeChildWindow:self];
1552 - (void) becameIneligibleParentOrChild
1554 NSArray* childWindows = [self childWineWindows];
1556 [self becameIneligibleChild];
1558 if ([childWindows count])
1562 for (child in childWindows)
1564 child.latentParentWindow = self;
1565 [self removeChildWindow:child];
1568 if (latentChildWindows)
1569 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1571 latentChildWindows = [childWindows mutableCopy];
1575 // Determine if, among Wine windows, this window is directly above or below
1576 // a given other Wine window with no other Wine window intervening.
1577 // Intervening non-Wine windows are ignored.
1578 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1580 NSNumber* windowNumber;
1581 NSNumber* otherWindowNumber;
1582 NSArray* windowNumbers;
1583 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1585 if (![self isVisible] || ![otherWindow isVisible])
1588 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1589 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1590 windowNumbers = [[self class] windowNumbersWithOptions:0];
1591 windowIndex = [windowNumbers indexOfObject:windowNumber];
1592 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1594 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1597 if (orderingMode == NSWindowAbove)
1599 lowIndex = windowIndex;
1600 highIndex = otherWindowIndex;
1602 else if (orderingMode == NSWindowBelow)
1604 lowIndex = otherWindowIndex;
1605 highIndex = windowIndex;
1610 if (highIndex <= lowIndex)
1613 for (i = lowIndex + 1; i < highIndex; i++)
1615 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1616 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1617 if ([interveningWindow isKindOfClass:[WineWindow class]])
1624 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1626 NSMutableArray* windowNumbers;
1627 NSNumber* childWindowNumber;
1628 NSUInteger otherIndex;
1629 NSArray* origChildren;
1630 NSMutableArray* children;
1632 // Get the z-order from the window server and modify it to reflect the
1633 // requested window ordering.
1634 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1635 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1636 [windowNumbers removeObject:childWindowNumber];
1639 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1640 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1642 else if (mode == NSWindowAbove)
1643 [windowNumbers insertObject:childWindowNumber atIndex:0];
1645 [windowNumbers addObject:childWindowNumber];
1647 // Get our child windows and sort them in the reverse of the desired
1648 // z-order (back-to-front).
1649 origChildren = [self childWineWindows];
1650 children = [[origChildren mutableCopy] autorelease];
1651 [children sortWithOptions:NSSortStable
1652 usingComparator:^NSComparisonResult(id obj1, id obj2){
1653 return compare_windows_back_to_front(obj1, obj2, windowNumbers);
1656 [self setChildWineWindows:children];
1659 // Search the ancestor windows of self and other to find a place where some ancestors are siblings of each other.
1660 // There are three possible results in terms of the values of *ancestor and *ancestorOfOther on return:
1661 // (non-nil, non-nil) there is a level in the window tree where the two windows have sibling ancestors
1662 // if *ancestor has a parent Wine window, then it's the parent of the other ancestor, too
1663 // otherwise, the two ancestors are each roots of disjoint window trees
1664 // (nil, non-nil) the other window is a descendent of self and *ancestorOfOther is the direct child
1665 // (non-nil, nil) self is a descendent of other and *ancestor is the direct child
1666 - (void) getSiblingWindowsForWindow:(WineWindow*)other ancestor:(WineWindow**)ancestor ancestorOfOther:(WineWindow**)ancestorOfOther
1668 NSMutableArray* otherAncestors = [NSMutableArray arrayWithObject:other];
1672 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1678 *ancestorOfOther = child;
1682 [otherAncestors addObject:parent];
1686 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1689 NSUInteger index = [otherAncestors indexOfObjectIdenticalTo:parent];
1690 if (index != NSNotFound)
1694 *ancestorOfOther = nil;
1696 *ancestorOfOther = [otherAncestors objectAtIndex:index - 1];
1702 *ancestorOfOther = otherAncestors.lastObject;;
1705 /* Returns whether or not the window was ordered in, which depends on if
1706 its frame intersects any screen. */
1707 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1709 WineApplicationController* controller = [WineApplicationController sharedController];
1710 if (![self isMiniaturized])
1712 BOOL needAdjustWindowLevels = FALSE;
1717 [controller transformProcessToForeground:!self.preventsAppActivation];
1718 if ([NSApp isHidden])
1720 wasVisible = [self isVisible];
1723 [controller tryToActivateIgnoringOtherApps:YES];
1725 NSDisableScreenUpdates();
1727 if ([self becameEligibleParentOrChild])
1728 needAdjustWindowLevels = TRUE;
1732 WineWindow* other = [prev isVisible] ? prev : next;
1733 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1735 if (![self isOrdered:orderingMode relativeTo:other])
1737 WineWindow* ancestor;
1738 WineWindow* ancestorOfOther;
1740 [self getSiblingWindowsForWindow:other ancestor:&ancestor ancestorOfOther:&ancestorOfOther];
1743 [self setAutodisplay:YES];
1744 if (ancestorOfOther)
1746 // This window level may not be right for this window based
1747 // on floating-ness, fullscreen-ness, etc. But we set it
1748 // temporarily to allow us to order the windows properly.
1749 // Then the levels get fixed by -adjustWindowLevels.
1750 if ([ancestor level] != [ancestorOfOther level])
1751 [ancestor setLevel:[ancestorOfOther level]];
1753 parent = (WineWindow*)ancestor.parentWindow;
1754 if ([parent isKindOfClass:[WineWindow class]])
1755 [parent order:orderingMode childWindow:ancestor relativeTo:ancestorOfOther];
1757 [ancestor orderWindow:orderingMode relativeTo:[ancestorOfOther windowNumber]];
1760 if (!ancestorOfOther || ancestor != self)
1763 (parent = (WineWindow*)child.parentWindow);
1766 if ([parent isKindOfClass:[WineWindow class]])
1767 [parent order:-orderingMode childWindow:child relativeTo:nil];
1768 if (parent == ancestor)
1773 [self checkWineDisplayLink];
1774 needAdjustWindowLevels = TRUE;
1781 (parent = (WineWindow*)child.parentWindow) && [parent isKindOfClass:[WineWindow class]];
1784 [parent order:NSWindowAbove childWindow:child relativeTo:nil];
1787 // Again, temporarily set level to make sure we can order to
1789 next = [controller frontWineWindow];
1790 if (next && [self level] < [next level])
1791 [self setLevel:[next level]];
1792 [self setAutodisplay:YES];
1793 [self orderFront:nil];
1794 [self checkWineDisplayLink];
1795 needAdjustWindowLevels = TRUE;
1797 pendingOrderOut = FALSE;
1799 if ([self becameEligibleParentOrChild])
1800 needAdjustWindowLevels = TRUE;
1802 if (needAdjustWindowLevels)
1804 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1805 [controller updateFullscreenWindows];
1806 [controller adjustWindowLevels];
1809 if (pendingMinimize)
1811 [self setStyleMask:([self styleMask] | NSWindowStyleMaskMiniaturizable)];
1812 [super miniaturize:nil];
1813 pendingMinimize = FALSE;
1816 NSEnableScreenUpdates();
1818 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1819 Generate a frame-changed event just in case. The back end will ignore
1820 it if nothing actually changed. */
1821 [self windowDidResize:nil skipSizeMove:TRUE];
1823 if (![self isExcludedFromWindowsMenu])
1824 [NSApp addWindowsItem:self title:[self title] filename:NO];
1830 WineApplicationController* controller = [WineApplicationController sharedController];
1831 BOOL wasVisible = [self isVisible];
1832 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1834 [self endWindowDragging];
1835 [controller windowWillOrderOut:self];
1837 if (enteringFullScreen || exitingFullScreen)
1839 pendingOrderOut = TRUE;
1840 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1841 event_mask_for_type(WINDOW_GOT_FOCUS) |
1842 event_mask_for_type(WINDOW_LOST_FOCUS) |
1843 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1844 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1845 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1850 pendingOrderOut = FALSE;
1852 if ([self isMiniaturized])
1853 pendingMinimize = TRUE;
1855 WineWindow* parent = (WineWindow*)self.parentWindow;
1856 if ([parent isKindOfClass:[WineWindow class]])
1857 [parent grabDockIconSnapshotFromWindow:self force:NO];
1859 [self becameIneligibleParentOrChild];
1860 if ([self isMiniaturized] || [self styleMask] & NSWindowStyleMaskFullScreen)
1864 fakingClose = FALSE;
1867 [self orderOut:nil];
1868 [self checkWineDisplayLink];
1869 [self setBackgroundColor:[NSColor clearColor]];
1870 [self setOpaque:NO];
1871 drawnSinceShown = NO;
1872 savedVisibleState = FALSE;
1873 if (wasVisible && wasOnActiveSpace && fullscreen)
1874 [controller updateFullscreenWindows];
1875 [controller adjustWindowLevels];
1876 [NSApp removeWindowsItem:self];
1878 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1879 event_mask_for_type(WINDOW_GOT_FOCUS) |
1880 event_mask_for_type(WINDOW_LOST_FOCUS) |
1881 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1882 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1883 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1887 - (void) updateFullscreen
1889 NSRect contentRect = [self contentRectForFrameRect:self.wine_fractionalFrame];
1890 BOOL nowFullscreen = !([self styleMask] & NSWindowStyleMaskFullScreen) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1892 if (nowFullscreen != fullscreen)
1894 WineApplicationController* controller = [WineApplicationController sharedController];
1896 fullscreen = nowFullscreen;
1897 if ([self isVisible] && [self isOnActiveSpace])
1898 [controller updateFullscreenWindows];
1900 [controller adjustWindowLevels];
1904 - (void) setFrameAndWineFrame:(NSRect)frame
1906 [self setFrame:frame display:YES];
1909 roundedWineFrame = self.frame;
1911 #if CGFLOAT_IS_DOUBLE
1912 if ((!modf(wineFrame.origin.x, &junk) && !modf(wineFrame.origin.y, &junk) &&
1913 !modf(wineFrame.size.width, &junk) && !modf(wineFrame.size.height, &junk)) ||
1914 fabs(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1915 fabs(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1916 fabs(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1917 fabs(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1918 roundedWineFrame = wineFrame;
1920 if ((!modff(wineFrame.origin.x, &junk) && !modff(wineFrame.origin.y, &junk) &&
1921 !modff(wineFrame.size.width, &junk) && !modff(wineFrame.size.height, &junk)) ||
1922 fabsf(wineFrame.origin.x - roundedWineFrame.origin.x) >= 1 ||
1923 fabsf(wineFrame.origin.y - roundedWineFrame.origin.y) >= 1 ||
1924 fabsf(wineFrame.size.width - roundedWineFrame.size.width) >= 1 ||
1925 fabsf(wineFrame.size.height - roundedWineFrame.size.height) >= 1)
1926 roundedWineFrame = wineFrame;
1930 - (void) setFrameFromWine:(NSRect)contentRect
1932 /* Origin is (left, top) in a top-down space. Need to convert it to
1933 (left, bottom) in a bottom-up space. */
1934 [[WineApplicationController sharedController] flipRect:&contentRect];
1936 /* The back end is establishing a new window size and position. It's
1937 not interested in any stale events regarding those that may be sitting
1939 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1942 if (!NSIsEmptyRect(contentRect))
1944 NSRect frame, oldFrame;
1946 oldFrame = self.wine_fractionalFrame;
1947 frame = [self frameRectForContentRect:contentRect];
1948 if (!NSEqualRects(frame, oldFrame))
1950 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1951 BOOL needEnableScreenUpdates = FALSE;
1953 if ([self preventResizing])
1955 // Allow the following calls to -setFrame:display: to work even
1956 // if they would violate the content size constraints. This
1957 // shouldn't be necessary since the content size constraints are
1958 // documented to not constrain that method, but it seems to be.
1959 [self setContentMinSize:NSZeroSize];
1960 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1963 if (equalSizes && [[self childWineWindows] count])
1965 // If we change the window frame such that the origin moves
1966 // but the size doesn't change, then Cocoa moves child
1967 // windows with the parent. We don't want that so we fake
1968 // a change of the size and then change it back.
1969 NSRect bogusFrame = frame;
1970 bogusFrame.size.width++;
1972 NSDisableScreenUpdates();
1973 needEnableScreenUpdates = TRUE;
1975 ignore_windowResize = TRUE;
1976 [self setFrame:bogusFrame display:NO];
1977 ignore_windowResize = FALSE;
1980 [self setFrameAndWineFrame:frame];
1981 if ([self preventResizing])
1983 [self setContentMinSize:contentRect.size];
1984 [self setContentMaxSize:contentRect.size];
1987 if (needEnableScreenUpdates)
1988 NSEnableScreenUpdates();
1990 if (!enteringFullScreen &&
1991 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1992 nonFullscreenFrame = frame;
1994 [self updateFullscreen];
1996 if ([self isOrderedIn])
1998 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1999 event. The back end will ignore it if nothing actually changed. */
2000 [self windowDidResize:nil skipSizeMove:TRUE];
2006 - (NSRect) wine_fractionalFrame
2008 NSRect frame = self.frame;
2009 if (NSEqualRects(frame, roundedWineFrame))
2014 - (void) setMacDrvParentWindow:(WineWindow*)parent
2016 WineWindow* oldParent = (WineWindow*)[self parentWindow];
2017 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
2019 [oldParent removeChildWineWindow:self];
2020 [latentParentWindow removeChildWineWindow:self];
2021 if ([parent addChildWineWindow:self])
2022 [[WineApplicationController sharedController] adjustWindowLevels];
2023 [self adjustFullScreenBehavior:[self collectionBehavior]];
2027 - (void) setDisabled:(BOOL)newValue
2029 if (disabled != newValue)
2031 disabled = newValue;
2032 [self adjustFeaturesForState];
2036 - (BOOL) needsTransparency
2038 return self.contentView.layer.mask || self.colorKeyed || self.usePerPixelAlpha ||
2039 (gl_surface_mode == GL_SURFACE_BEHIND && [(WineContentView*)self.contentView hasGLDescendant]);
2042 - (void) checkTransparency
2044 if (![self isOpaque] && !self.needsTransparency)
2046 self.shapeChangedSinceLastDraw = TRUE;
2047 [[self contentView] setNeedsDisplay:YES];
2048 [self setBackgroundColor:[NSColor windowBackgroundColor]];
2049 [self setOpaque:YES];
2051 else if ([self isOpaque] && self.needsTransparency)
2053 self.shapeChangedSinceLastDraw = TRUE;
2054 [[self contentView] setNeedsDisplay:YES];
2055 [self setBackgroundColor:[NSColor clearColor]];
2056 [self setOpaque:NO];
2060 - (void) setShape:(CGPathRef)newShape
2062 CALayer* layer = [[self contentView] layer];
2063 CAShapeLayer* mask = (CAShapeLayer*)layer.mask;
2064 if (CGPathEqualToPath(newShape, mask.path)) return;
2066 if (newShape && !layer.mask)
2067 layer.mask = mask = [CAShapeLayer layer];
2069 layer.mask = mask = nil;
2072 [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(mask.path))];
2074 [[self contentView] setNeedsDisplayInRect:NSRectFromCGRect(CGPathGetBoundingBox(newShape))];
2076 mask.path = newShape;
2077 self.shapeChangedSinceLastDraw = TRUE;
2079 [self checkTransparency];
2080 [self checkEmptyShaped];
2083 - (void) makeFocused:(BOOL)activate
2087 WineApplicationController *controller = [WineApplicationController sharedController];
2088 [controller transformProcessToForeground:YES];
2089 [controller tryToActivateIgnoringOtherApps:YES];
2092 causing_becomeKeyWindow = self;
2093 [self makeKeyWindow];
2094 causing_becomeKeyWindow = nil;
2096 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
2097 event_mask_for_type(WINDOW_LOST_FOCUS)
2101 - (void) postKey:(uint16_t)keyCode
2102 pressed:(BOOL)pressed
2103 modifiers:(NSUInteger)modifiers
2104 event:(NSEvent*)theEvent
2106 macdrv_event* event;
2108 WineApplicationController* controller = [WineApplicationController sharedController];
2110 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
2111 event->key.keycode = keyCode;
2112 event->key.modifiers = modifiers;
2113 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
2115 if ((cgevent = [theEvent CGEvent]))
2116 controller.keyboardType = CGEventGetIntegerValueField(cgevent, kCGKeyboardEventKeyboardType);
2118 [queue postEvent:event];
2120 macdrv_release_event(event);
2122 [controller noteKey:keyCode pressed:pressed];
2125 - (void) postKeyEvent:(NSEvent *)theEvent
2127 [self flagsChanged:theEvent];
2128 [self postKey:[theEvent keyCode]
2129 pressed:[theEvent type] == NSEventTypeKeyDown
2130 modifiers:adjusted_modifiers_for_settings([theEvent modifierFlags])
2134 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
2136 savedContentMinSize = minSize;
2137 savedContentMaxSize = maxSize;
2138 if (![self preventResizing])
2140 [self setContentMinSize:minSize];
2141 [self setContentMaxSize:maxSize];
2145 - (WineWindow*) ancestorWineWindow
2147 WineWindow* ancestor = self;
2150 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
2151 if ([parent isKindOfClass:[WineWindow class]])
2159 - (void) postBroughtForwardEvent
2161 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
2162 [queue postEvent:event];
2163 macdrv_release_event(event);
2166 - (void) postWindowFrameChanged:(NSRect)frame fullscreen:(BOOL)isFullscreen resizing:(BOOL)resizing skipSizeMove:(BOOL)skipSizeMove
2168 macdrv_event* event;
2169 NSUInteger style = self.styleMask;
2172 style |= NSWindowStyleMaskFullScreen;
2174 style &= ~NSWindowStyleMaskFullScreen;
2175 frame = [[self class] contentRectForFrameRect:frame styleMask:style];
2176 [[WineApplicationController sharedController] flipRect:&frame];
2178 /* Coalesce events by discarding any previous ones still in the queue. */
2179 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2182 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2183 event->window_frame_changed.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2184 event->window_frame_changed.fullscreen = isFullscreen;
2185 event->window_frame_changed.in_resize = resizing;
2186 event->window_frame_changed.skip_size_move_loop = skipSizeMove;
2187 [queue postEvent:event];
2188 macdrv_release_event(event);
2191 - (void) updateForCursorClipping
2193 [self adjustFeaturesForState];
2196 - (void) endWindowDragging
2200 if (draggingPhase == 3)
2202 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
2203 [queue postEvent:event];
2204 macdrv_release_event(event);
2208 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
2212 - (NSMutableDictionary*) displayIDToDisplayLinkMap
2214 static NSMutableDictionary* displayIDToDisplayLinkMap;
2215 if (!displayIDToDisplayLinkMap)
2217 displayIDToDisplayLinkMap = [[NSMutableDictionary alloc] init];
2219 [[NSNotificationCenter defaultCenter] addObserverForName:NSApplicationDidChangeScreenParametersNotification
2222 usingBlock:^(NSNotification *note){
2223 NSMutableSet* badDisplayIDs = [NSMutableSet setWithArray:displayIDToDisplayLinkMap.allKeys];
2224 NSSet* validDisplayIDs = [NSSet setWithArray:[[NSScreen screens] valueForKeyPath:@"deviceDescription.NSScreenNumber"]];
2225 [badDisplayIDs minusSet:validDisplayIDs];
2226 [displayIDToDisplayLinkMap removeObjectsForKeys:[badDisplayIDs allObjects]];
2229 return displayIDToDisplayLinkMap;
2232 - (WineDisplayLink*) wineDisplayLink
2234 if (!_lastDisplayID)
2237 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2238 return [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2241 - (void) checkWineDisplayLink
2243 NSScreen* screen = self.screen;
2244 if (![self isVisible] || ![self isOnActiveSpace] || [self isMiniaturized] || [self isEmptyShaped])
2246 #if defined(MAC_OS_X_VERSION_10_9) && MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_9
2247 if ([self respondsToSelector:@selector(occlusionState)] && !(self.occlusionState & NSWindowOcclusionStateVisible))
2251 NSNumber* displayIDNumber = [screen.deviceDescription objectForKey:@"NSScreenNumber"];
2252 CGDirectDisplayID displayID = [displayIDNumber unsignedIntValue];
2253 if (displayID == _lastDisplayID)
2256 NSMutableDictionary* displayIDToDisplayLinkMap = [self displayIDToDisplayLinkMap];
2260 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:[NSNumber numberWithUnsignedInt:_lastDisplayID]];
2261 [link removeWindow:self];
2265 WineDisplayLink* link = [displayIDToDisplayLinkMap objectForKey:displayIDNumber];
2268 link = [[[WineDisplayLink alloc] initWithDisplayID:displayID] autorelease];
2269 [displayIDToDisplayLinkMap setObject:link forKey:displayIDNumber];
2271 [link addWindow:self];
2272 [self displayIfNeeded];
2274 _lastDisplayID = displayID;
2277 - (BOOL) isEmptyShaped
2279 CAShapeLayer* mask = (CAShapeLayer*)[[self contentView] layer].mask;
2280 return ([mask isEmptyShaped]);
2283 - (BOOL) canProvideSnapshot
2285 return (self.windowNumber > 0 && ![self isEmptyShaped]);
2288 - (void) grabDockIconSnapshotFromWindow:(WineWindow*)window force:(BOOL)force
2290 if (![self isEmptyShaped])
2293 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2294 if (!force && now < lastDockIconSnapshot + 1)
2299 if (![window canProvideSnapshot])
2305 for (WineWindow* childWindow in self.childWindows)
2307 if (![childWindow isKindOfClass:[WineWindow class]] || ![childWindow canProvideSnapshot])
2310 NSSize size = childWindow.frame.size;
2311 CGFloat area = size.width * size.height;
2312 if (!window || area > bestArea)
2314 window = childWindow;
2323 const void* windowID = (const void*)(CGWindowID)window.windowNumber;
2324 CFArrayRef windowIDs = CFArrayCreate(NULL, &windowID, 1, NULL);
2325 CGImageRef windowImage = CGWindowListCreateImageFromArray(CGRectNull, windowIDs, kCGWindowImageBoundsIgnoreFraming);
2326 CFRelease(windowIDs);
2330 NSImage* appImage = [NSApp applicationIconImage];
2332 appImage = [NSImage imageNamed:NSImageNameApplicationIcon];
2334 NSImage* dockIcon = [[[NSImage alloc] initWithSize:NSMakeSize(256, 256)] autorelease];
2335 [dockIcon lockFocus];
2337 CGContextRef cgcontext = [[NSGraphicsContext currentContext] graphicsPort];
2339 CGRect rect = CGRectMake(8, 8, 240, 240);
2340 size_t width = CGImageGetWidth(windowImage);
2341 size_t height = CGImageGetHeight(windowImage);
2344 rect.size.height *= height / (double)width;
2345 rect.origin.y += (CGRectGetWidth(rect) - CGRectGetHeight(rect)) / 2;
2347 else if (width != height)
2349 rect.size.width *= width / (double)height;
2350 rect.origin.x += (CGRectGetHeight(rect) - CGRectGetWidth(rect)) / 2;
2353 CGContextDrawImage(cgcontext, rect, windowImage);
2354 [appImage drawInRect:NSMakeRect(156, 4, 96, 96)
2356 operation:NSCompositingOperationSourceOver
2361 [dockIcon unlockFocus];
2363 CGImageRelease(windowImage);
2365 NSImageView* imageView = (NSImageView*)self.dockTile.contentView;
2366 if (![imageView isKindOfClass:[NSImageView class]])
2368 imageView = [[[NSImageView alloc] initWithFrame:NSMakeRect(0, 0, 256, 256)] autorelease];
2369 imageView.imageScaling = NSImageScaleProportionallyUpOrDown;
2370 self.dockTile.contentView = imageView;
2372 imageView.image = dockIcon;
2373 [self.dockTile display];
2374 lastDockIconSnapshot = now;
2377 - (void) checkEmptyShaped
2379 if (self.dockTile.contentView && ![self isEmptyShaped])
2381 self.dockTile.contentView = nil;
2382 lastDockIconSnapshot = 0;
2384 [self checkWineDisplayLink];
2389 * ---------- NSWindow method overrides ----------
2391 - (BOOL) canBecomeKeyWindow
2393 if (causing_becomeKeyWindow == self) return YES;
2394 if (self.disabled || self.noForeground) return NO;
2395 if ([self isKeyWindow]) return YES;
2397 // If a window's collectionBehavior says it participates in cycling,
2398 // it must return YES from this method to actually be eligible.
2399 return ![self isExcludedFromWindowsMenu];
2402 - (BOOL) canBecomeMainWindow
2404 return [self canBecomeKeyWindow];
2407 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
2409 // If a window is sized to completely cover a screen, then it's in
2410 // full-screen mode. In that case, we don't allow NSWindow to constrain
2412 NSArray* screens = [NSScreen screens];
2413 NSRect contentRect = [self contentRectForFrameRect:frameRect];
2414 if (!screen_covered_by_rect(contentRect, screens) &&
2415 frame_intersects_screens(frameRect, screens))
2416 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
2420 // This private method of NSWindow is called as Cocoa reacts to the display
2421 // configuration changing. Among other things, it adjusts the window's
2422 // frame based on how the screen(s) changed size. That tells Wine that the
2423 // window has been moved. We don't want that. Rather, we want to make
2424 // sure that the WinAPI notion of the window position is maintained/
2425 // restored, possibly undoing or overriding Cocoa's adjustment.
2427 // So, we queue a REASSERT_WINDOW_POSITION event to the back end before
2428 // Cocoa has a chance to adjust the frame, thus preceding any resulting
2429 // WINDOW_FRAME_CHANGED event that may get queued. The back end will
2430 // reassert its notion of the position. That call won't get processed
2431 // until after this method returns, so it will override whatever this
2432 // method does to the window position. It will also discard any pending
2433 // WINDOW_FRAME_CHANGED events.
2435 // Unfortunately, the only way I've found to know when Cocoa is _about to_
2436 // adjust the window's position due to a display change is to hook into
2437 // this private method. This private method has remained stable from 10.6
2438 // through 10.11. If it does change, the most likely thing is that it
2439 // will be removed and no longer called and this fix will simply stop
2440 // working. The only real danger would be if Apple changed the return type
2441 // to a struct or floating-point type, which would change the calling
2443 - (id) _displayChanged
2445 macdrv_event* event = macdrv_create_event(REASSERT_WINDOW_POSITION, self);
2446 [queue postEvent:event];
2447 macdrv_release_event(event);
2449 return [super _displayChanged];
2452 - (BOOL) isExcludedFromWindowsMenu
2454 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
2457 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
2459 BOOL ret = [super validateMenuItem:menuItem];
2461 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
2462 ret = [self isKeyWindow] || (!self.disabled && !self.noForeground);
2463 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
2469 /* We don't call this. It's the action method of the items in the Window menu. */
2470 - (void) makeKeyAndOrderFront:(id)sender
2472 if ([self isMiniaturized])
2473 [self deminiaturize:nil];
2474 [self orderBelow:nil orAbove:nil activate:NO];
2475 [[self ancestorWineWindow] postBroughtForwardEvent];
2477 if (![self isKeyWindow] && !self.disabled && !self.noForeground)
2478 [[WineApplicationController sharedController] windowGotFocus:self];
2481 - (void) sendEvent:(NSEvent*)event
2483 NSEventType type = event.type;
2485 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
2486 interface control. For example, Control-Tab switches focus among
2487 views. We want to bypass that feature, so directly route key-down
2488 events to -keyDown:. */
2489 if (type == NSEventTypeKeyDown)
2490 [[self firstResponder] keyDown:event];
2493 if (!draggingPhase && maximized && ![self isMovable] &&
2494 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
2495 type == NSEventTypeLeftMouseDown && (self.styleMask & NSWindowStyleMaskTitled))
2497 NSRect titleBar = self.frame;
2498 NSRect contentRect = [self contentRectForFrameRect:titleBar];
2499 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
2500 titleBar.origin.y = NSMaxY(contentRect);
2502 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
2504 if (NSMouseInRect(dragStartPosition, titleBar, NO))
2506 static const NSWindowButton buttons[] = {
2507 NSWindowCloseButton,
2508 NSWindowMiniaturizeButton,
2510 NSWindowFullScreenButton,
2512 BOOL hitButton = NO;
2515 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
2517 NSButton* button = [self standardWindowButton:buttons[i]];
2518 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
2528 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
2529 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
2533 else if (draggingPhase && (type == NSEventTypeLeftMouseDragged || type == NSEventTypeLeftMouseUp))
2535 if ([self isMovable])
2537 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
2538 NSPoint newTopLeft = dragWindowStartPosition;
2540 newTopLeft.x += point.x - dragStartPosition.x;
2541 newTopLeft.y += point.y - dragStartPosition.y;
2543 if (draggingPhase == 2)
2545 macdrv_event* mevent = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
2546 mevent->window_drag_begin.no_activate = [event wine_commandKeyDown];
2547 [queue postEvent:mevent];
2548 macdrv_release_event(mevent);
2553 [self setFrameTopLeftPoint:newTopLeft];
2555 else if (draggingPhase == 1 && type == NSEventTypeLeftMouseDragged)
2557 macdrv_event* event;
2558 NSRect frame = [self contentRectForFrameRect:self.frame];
2560 [[WineApplicationController sharedController] flipRect:&frame];
2562 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2563 event->window_restore_requested.keep_frame = TRUE;
2564 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
2565 [queue postEvent:event];
2566 macdrv_release_event(event);
2571 if (type == NSEventTypeLeftMouseUp)
2572 [self endWindowDragging];
2575 [super sendEvent:event];
2579 - (void) miniaturize:(id)sender
2581 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
2582 [queue postEvent:event];
2583 macdrv_release_event(event);
2585 WineWindow* parent = (WineWindow*)self.parentWindow;
2586 if ([parent isKindOfClass:[WineWindow class]])
2587 [parent grabDockIconSnapshotFromWindow:self force:YES];
2590 - (void) toggleFullScreen:(id)sender
2592 if (!self.disabled && !maximized)
2593 [super toggleFullScreen:sender];
2596 - (void) setViewsNeedDisplay:(BOOL)value
2598 if (value && ![self viewsNeedDisplay])
2600 WineDisplayLink* link = [self wineDisplayLink];
2603 NSTimeInterval now = [[NSProcessInfo processInfo] systemUptime];
2604 if (_lastDisplayTime + [link refreshPeriod] < now)
2605 [self setAutodisplay:YES];
2609 _lastDisplayTime = now;
2613 [self setAutodisplay:YES];
2615 [super setViewsNeedDisplay:value];
2620 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2623 [self setAutodisplay:NO];
2626 - (void) displayIfNeeded
2628 _lastDisplayTime = [[NSProcessInfo processInfo] systemUptime];
2629 [super displayIfNeeded];
2631 [self setAutodisplay:NO];
2634 - (void) setFrame:(NSRect)frameRect display:(BOOL)flag
2637 [self setAutodisplay:YES];
2638 [super setFrame:frameRect display:flag];
2641 - (void) setFrame:(NSRect)frameRect display:(BOOL)displayFlag animate:(BOOL)animateFlag
2644 [self setAutodisplay:YES];
2645 [super setFrame:frameRect display:displayFlag animate:animateFlag];
2648 - (void) windowDidDrawContent
2650 if (!drawnSinceShown)
2652 drawnSinceShown = YES;
2653 dispatch_async(dispatch_get_main_queue(), ^{
2654 [self checkTransparency];
2659 - (NSArray*) childWineWindows
2661 NSArray* childWindows = self.childWindows;
2662 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
2663 return [child isKindOfClass:[WineWindow class]];
2665 return [childWindows objectsAtIndexes:indexes];
2668 - (void) updateForGLSubviews
2670 if (gl_surface_mode == GL_SURFACE_BEHIND)
2671 [self checkTransparency];
2674 - (void) setRetinaMode:(int)mode
2677 double scale = mode ? 0.5 : 2.0;
2678 NSAffineTransform* transform = [NSAffineTransform transform];
2680 [transform scaleBy:scale];
2682 [[self contentView] layer].mask.contentsScale = mode ? 2.0 : 1.0;
2684 for (WineBaseView* subview in [self.contentView subviews])
2686 if ([subview isKindOfClass:[WineBaseView class]])
2687 [subview setRetinaMode:mode];
2690 frame = [self contentRectForFrameRect:self.wine_fractionalFrame];
2691 frame.origin.x *= scale;
2692 frame.origin.y *= scale;
2693 frame.size.width *= scale;
2694 frame.size.height *= scale;
2695 frame = [self frameRectForContentRect:frame];
2697 savedContentMinSize = [transform transformSize:savedContentMinSize];
2698 if (savedContentMaxSize.width != FLT_MAX && savedContentMaxSize.width != CGFLOAT_MAX)
2699 savedContentMaxSize.width *= scale;
2700 if (savedContentMaxSize.height != FLT_MAX && savedContentMaxSize.height != CGFLOAT_MAX)
2701 savedContentMaxSize.height *= scale;
2703 self.contentMinSize = [transform transformSize:self.contentMinSize];
2704 NSSize temp = self.contentMaxSize;
2705 if (temp.width != FLT_MAX && temp.width != CGFLOAT_MAX)
2706 temp.width *= scale;
2707 if (temp.height != FLT_MAX && temp.height != CGFLOAT_MAX)
2708 temp.height *= scale;
2709 self.contentMaxSize = temp;
2711 ignore_windowResize = TRUE;
2712 [self setFrameAndWineFrame:frame];
2713 ignore_windowResize = FALSE;
2718 * ---------- NSResponder method overrides ----------
2720 - (void) keyDown:(NSEvent *)theEvent
2722 if ([theEvent isARepeat])
2724 if (!allowKeyRepeats)
2728 allowKeyRepeats = YES;
2730 [self postKeyEvent:theEvent];
2733 - (void) flagsChanged:(NSEvent *)theEvent
2735 static const struct {
2739 { NX_ALPHASHIFTMASK, kVK_CapsLock },
2740 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
2741 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
2742 { NX_DEVICELCTLKEYMASK, kVK_Control },
2743 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
2744 { NX_DEVICELALTKEYMASK, kVK_Option },
2745 { NX_DEVICERALTKEYMASK, kVK_RightOption },
2746 { NX_DEVICELCMDKEYMASK, kVK_Command },
2747 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
2750 NSUInteger modifierFlags = adjusted_modifiers_for_settings([theEvent modifierFlags]);
2752 int i, last_changed;
2754 fix_device_modifiers_by_generic(&modifierFlags);
2755 changed = modifierFlags ^ lastModifierFlags;
2758 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
2759 if (changed & modifiers[i].mask)
2762 for (i = 0; i <= last_changed; i++)
2764 if (changed & modifiers[i].mask)
2766 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
2769 allowKeyRepeats = NO;
2771 if (i == last_changed)
2772 lastModifierFlags = modifierFlags;
2775 lastModifierFlags ^= modifiers[i].mask;
2776 fix_generic_modifiers_by_device(&lastModifierFlags);
2779 // Caps lock generates one event for each press-release action.
2780 // We need to simulate a pair of events for each actual event.
2781 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
2783 [self postKey:modifiers[i].keycode
2785 modifiers:lastModifierFlags
2786 event:(NSEvent*)theEvent];
2790 [self postKey:modifiers[i].keycode
2792 modifiers:lastModifierFlags
2793 event:(NSEvent*)theEvent];
2798 - (void) applicationWillHide
2800 savedVisibleState = [self isVisible];
2803 - (void) applicationDidUnhide
2805 if ([self isVisible])
2806 [self becameEligibleParentOrChild];
2811 * ---------- NSWindowDelegate methods ----------
2813 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
2815 macdrv_query* query;
2818 query = macdrv_create_query();
2819 query->type = QUERY_MIN_MAX_INFO;
2820 query->window = (macdrv_window)[self retain];
2821 [self.queue query:query timeout:0.5];
2822 macdrv_release_query(query);
2824 size = [self contentMaxSize];
2825 if (proposedSize.width < size.width)
2826 size.width = proposedSize.width;
2827 if (proposedSize.height < size.height)
2828 size.height = proposedSize.height;
2832 - (void)windowDidBecomeKey:(NSNotification *)notification
2834 WineApplicationController* controller = [WineApplicationController sharedController];
2835 NSEvent* event = [controller lastFlagsChanged];
2837 [self flagsChanged:event];
2839 if (causing_becomeKeyWindow == self) return;
2841 [controller windowGotFocus:self];
2844 - (void) windowDidChangeOcclusionState:(NSNotification*)notification
2846 [self checkWineDisplayLink];
2849 - (void) windowDidChangeScreen:(NSNotification*)notification
2851 [self checkWineDisplayLink];
2854 - (void)windowDidDeminiaturize:(NSNotification *)notification
2856 WineApplicationController* controller = [WineApplicationController sharedController];
2858 if (!ignore_windowDeminiaturize)
2859 [self postDidUnminimizeEvent];
2860 ignore_windowDeminiaturize = FALSE;
2862 [self becameEligibleParentOrChild];
2864 if (fullscreen && [self isOnActiveSpace])
2865 [controller updateFullscreenWindows];
2866 [controller adjustWindowLevels];
2868 if (![self parentWindow])
2869 [self postBroughtForwardEvent];
2871 if (!self.disabled && !self.noForeground)
2873 causing_becomeKeyWindow = self;
2874 [self makeKeyWindow];
2875 causing_becomeKeyWindow = nil;
2876 [controller windowGotFocus:self];
2879 [self windowDidResize:notification];
2880 [self checkWineDisplayLink];
2883 - (void) windowDidEndLiveResize:(NSNotification *)notification
2887 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
2888 [queue postEvent:event];
2889 macdrv_release_event(event);
2893 - (void) windowDidEnterFullScreen:(NSNotification*)notification
2895 enteringFullScreen = FALSE;
2896 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
2897 if (pendingOrderOut)
2901 - (void) windowDidExitFullScreen:(NSNotification*)notification
2903 exitingFullScreen = FALSE;
2904 [self setFrameAndWineFrame:nonFullscreenFrame];
2905 [self windowDidResize:nil];
2906 if (pendingOrderOut)
2910 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
2912 enteringFullScreen = FALSE;
2913 enteredFullScreenTime = 0;
2914 if (pendingOrderOut)
2918 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
2920 exitingFullScreen = FALSE;
2921 [self windowDidResize:nil];
2922 if (pendingOrderOut)
2926 - (void)windowDidMiniaturize:(NSNotification *)notification
2928 macdrv_event* event;
2930 if (fullscreen && [self isOnActiveSpace])
2931 [[WineApplicationController sharedController] updateFullscreenWindows];
2933 [self checkWineDisplayLink];
2935 event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
2936 [queue postEvent:event];
2937 macdrv_release_event(event);
2940 - (void)windowDidMove:(NSNotification *)notification
2942 [self windowDidResize:notification];
2945 - (void)windowDidResignKey:(NSNotification *)notification
2947 macdrv_event* event;
2949 if (causing_becomeKeyWindow) return;
2951 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
2952 [queue postEvent:event];
2953 macdrv_release_event(event);
2956 - (void)windowDidResize:(NSNotification *)notification skipSizeMove:(BOOL)skipSizeMove
2958 NSRect frame = self.wine_fractionalFrame;
2960 if ([self inLiveResize])
2962 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
2963 resizingFromLeft = TRUE;
2964 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
2965 resizingFromTop = TRUE;
2968 if (ignore_windowResize || exitingFullScreen) return;
2970 if ([self preventResizing])
2972 NSRect contentRect = [self contentRectForFrameRect:frame];
2973 [self setContentMinSize:contentRect.size];
2974 [self setContentMaxSize:contentRect.size];
2977 [self postWindowFrameChanged:frame
2978 fullscreen:([self styleMask] & NSWindowStyleMaskFullScreen) != 0
2979 resizing:[self inLiveResize]
2980 skipSizeMove:skipSizeMove];
2982 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2983 [self updateFullscreen];
2986 - (void)windowDidResize:(NSNotification *)notification
2988 [self windowDidResize:notification skipSizeMove:FALSE];
2991 - (BOOL)windowShouldClose:(id)sender
2993 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2994 [queue postEvent:event];
2995 macdrv_release_event(event);
2999 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
3003 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3004 [queue postEvent:event];
3005 macdrv_release_event(event);
3008 else if (!resizable)
3010 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
3011 [queue postEvent:event];
3012 macdrv_release_event(event);
3019 - (void) windowWillClose:(NSNotification*)notification
3023 if (fakingClose) return;
3024 if (latentParentWindow)
3026 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
3027 self.latentParentWindow = nil;
3030 for (child in latentChildWindows)
3032 if (child.latentParentWindow == self)
3033 child.latentParentWindow = nil;
3035 [latentChildWindows removeAllObjects];
3038 - (void) windowWillEnterFullScreen:(NSNotification*)notification
3040 enteringFullScreen = TRUE;
3041 nonFullscreenFrame = self.wine_fractionalFrame;
3044 - (void) windowWillExitFullScreen:(NSNotification*)notification
3046 exitingFullScreen = TRUE;
3047 [self postWindowFrameChanged:nonFullscreenFrame fullscreen:FALSE resizing:FALSE skipSizeMove:FALSE];
3050 - (void)windowWillMiniaturize:(NSNotification *)notification
3052 [self becameIneligibleParentOrChild];
3053 [self grabDockIconSnapshotFromWindow:nil force:NO];
3056 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
3058 if ([self inLiveResize])
3061 return self.wine_fractionalFrame.size;
3064 macdrv_query* query;
3066 rect = [self frame];
3067 if (resizingFromLeft)
3068 rect.origin.x = NSMaxX(rect) - frameSize.width;
3069 if (!resizingFromTop)
3070 rect.origin.y = NSMaxY(rect) - frameSize.height;
3071 rect.size = frameSize;
3072 rect = [self contentRectForFrameRect:rect];
3073 [[WineApplicationController sharedController] flipRect:&rect];
3075 query = macdrv_create_query();
3076 query->type = QUERY_RESIZE_SIZE;
3077 query->window = (macdrv_window)[self retain];
3078 query->resize_size.rect = cgrect_win_from_mac(NSRectToCGRect(rect));
3079 query->resize_size.from_left = resizingFromLeft;
3080 query->resize_size.from_top = resizingFromTop;
3082 if ([self.queue query:query timeout:0.1])
3084 rect = NSRectFromCGRect(cgrect_mac_from_win(query->resize_size.rect));
3085 rect = [self frameRectForContentRect:rect];
3086 frameSize = rect.size;
3089 macdrv_release_query(query);
3095 - (void) windowWillStartLiveResize:(NSNotification *)notification
3097 [self endWindowDragging];
3101 macdrv_event* event;
3102 NSRect frame = [self contentRectForFrameRect:self.frame];
3104 [[WineApplicationController sharedController] flipRect:&frame];
3106 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
3107 event->window_restore_requested.keep_frame = TRUE;
3108 event->window_restore_requested.frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3109 [queue postEvent:event];
3110 macdrv_release_event(event);
3113 [self sendResizeStartQuery];
3115 frameAtResizeStart = [self frame];
3116 resizingFromLeft = resizingFromTop = FALSE;
3119 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
3121 macdrv_query* query;
3122 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
3125 query = macdrv_create_query();
3126 query->type = QUERY_MIN_MAX_INFO;
3127 query->window = (macdrv_window)[self retain];
3128 [self.queue query:query timeout:0.5];
3129 macdrv_release_query(query);
3131 currentContentRect = [self contentRectForFrameRect:[self frame]];
3132 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
3134 maxSize = [self contentMaxSize];
3135 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
3136 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
3138 // Try to keep the top-left corner where it is.
3139 newContentRect.origin.x = NSMinX(currentContentRect);
3140 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
3142 // If that pushes the bottom or right off the screen, pull it up and to the left.
3143 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
3144 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
3145 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
3146 if (NSMinY(newContentRect) < NSMinY(screenRect))
3147 newContentRect.origin.y = NSMinY(screenRect);
3149 // If that pushes the top or left off the screen, push it down and the right
3150 // again. Do this last because the top-left corner is more important than the
3152 if (NSMinX(newContentRect) < NSMinX(screenRect))
3153 newContentRect.origin.x = NSMinX(screenRect);
3154 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
3155 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
3157 return [self frameRectForContentRect:newContentRect];
3162 * ---------- NSPasteboardOwner methods ----------
3164 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
3166 macdrv_query* query = macdrv_create_query();
3167 query->type = QUERY_PASTEBOARD_DATA;
3168 query->window = (macdrv_window)[self retain];
3169 query->pasteboard_data.type = (CFStringRef)[type copy];
3171 [self.queue query:query timeout:3];
3172 macdrv_release_query(query);
3175 - (void) pasteboardChangedOwner:(NSPasteboard*)sender
3177 macdrv_event* event = macdrv_create_event(LOST_PASTEBOARD_OWNERSHIP, self);
3178 [queue postEvent:event];
3179 macdrv_release_event(event);
3184 * ---------- NSDraggingDestination methods ----------
3186 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
3188 return [self draggingUpdated:sender];
3191 - (void) draggingExited:(id <NSDraggingInfo>)sender
3193 // This isn't really a query. We don't need any response. However, it
3194 // has to be processed in a similar manner as the other drag-and-drop
3195 // queries in order to maintain the proper order of operations.
3196 macdrv_query* query = macdrv_create_query();
3197 query->type = QUERY_DRAG_EXITED;
3198 query->window = (macdrv_window)[self retain];
3200 [self.queue query:query timeout:0.1];
3201 macdrv_release_query(query);
3204 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
3206 NSDragOperation ret;
3207 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3208 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3209 NSPasteboard* pb = [sender draggingPasteboard];
3211 macdrv_query* query = macdrv_create_query();
3212 query->type = QUERY_DRAG_OPERATION;
3213 query->window = (macdrv_window)[self retain];
3214 query->drag_operation.x = floor(cgpt.x);
3215 query->drag_operation.y = floor(cgpt.y);
3216 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
3217 query->drag_operation.accepted_op = NSDragOperationNone;
3218 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
3220 [self.queue query:query timeout:3];
3221 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
3222 macdrv_release_query(query);
3227 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
3230 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
3231 CGPoint cgpt = cgpoint_win_from_mac(NSPointToCGPoint(pt));
3232 NSPasteboard* pb = [sender draggingPasteboard];
3234 macdrv_query* query = macdrv_create_query();
3235 query->type = QUERY_DRAG_DROP;
3236 query->window = (macdrv_window)[self retain];
3237 query->drag_drop.x = floor(cgpt.x);
3238 query->drag_drop.y = floor(cgpt.y);
3239 query->drag_drop.op = [sender draggingSourceOperationMask];
3240 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
3242 [self.queue query:query timeout:3 * 60 flags:WineQueryProcessEvents];
3243 ret = query->status;
3244 macdrv_release_query(query);
3249 - (BOOL) wantsPeriodicDraggingUpdates
3257 /***********************************************************************
3258 * macdrv_create_cocoa_window
3260 * Create a Cocoa window with the given content frame and features (e.g.
3261 * title bar, close box, etc.).
3263 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
3264 CGRect frame, void* hwnd, macdrv_event_queue queue)
3266 __block WineWindow* window;
3269 window = [[WineWindow createWindowWithFeatures:wf
3270 windowFrame:NSRectFromCGRect(cgrect_mac_from_win(frame))
3272 queue:(WineEventQueue*)queue] retain];
3275 return (macdrv_window)window;
3278 /***********************************************************************
3279 * macdrv_destroy_cocoa_window
3281 * Destroy a Cocoa window.
3283 void macdrv_destroy_cocoa_window(macdrv_window w)
3285 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3286 WineWindow* window = (WineWindow*)w;
3289 window.closing = TRUE;
3290 [window doOrderOut];
3293 [window.queue discardEventsMatchingMask:-1 forWindow:window];
3299 /***********************************************************************
3300 * macdrv_get_window_hwnd
3302 * Get the hwnd that was set for the window at creation.
3304 void* macdrv_get_window_hwnd(macdrv_window w)
3306 WineWindow* window = (WineWindow*)w;
3310 /***********************************************************************
3311 * macdrv_set_cocoa_window_features
3313 * Update a Cocoa window's features.
3315 void macdrv_set_cocoa_window_features(macdrv_window w,
3316 const struct macdrv_window_features* wf)
3318 WineWindow* window = (WineWindow*)w;
3321 [window setWindowFeatures:wf];
3325 /***********************************************************************
3326 * macdrv_set_cocoa_window_state
3328 * Update a Cocoa window's state.
3330 void macdrv_set_cocoa_window_state(macdrv_window w,
3331 const struct macdrv_window_state* state)
3333 WineWindow* window = (WineWindow*)w;
3336 [window setMacDrvState:state];
3340 /***********************************************************************
3341 * macdrv_set_cocoa_window_title
3343 * Set a Cocoa window's title.
3345 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
3348 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3349 WineWindow* window = (WineWindow*)w;
3350 NSString* titleString;
3353 titleString = [NSString stringWithCharacters:title length:length];
3356 OnMainThreadAsync(^{
3357 [window setTitle:titleString];
3358 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
3359 [NSApp changeWindowsItem:window title:titleString filename:NO];
3365 /***********************************************************************
3366 * macdrv_order_cocoa_window
3368 * Reorder a Cocoa window relative to other windows. If prev is
3369 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
3370 * it is ordered above that window. Otherwise, it is ordered to the
3373 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
3374 macdrv_window n, int activate)
3376 WineWindow* window = (WineWindow*)w;
3377 WineWindow* prev = (WineWindow*)p;
3378 WineWindow* next = (WineWindow*)n;
3380 OnMainThreadAsync(^{
3381 [window orderBelow:prev
3385 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3387 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
3391 /***********************************************************************
3392 * macdrv_hide_cocoa_window
3394 * Hides a Cocoa window.
3396 void macdrv_hide_cocoa_window(macdrv_window w)
3398 WineWindow* window = (WineWindow*)w;
3401 [window doOrderOut];
3405 /***********************************************************************
3406 * macdrv_set_cocoa_window_frame
3408 * Move a Cocoa window.
3410 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
3412 WineWindow* window = (WineWindow*)w;
3415 [window setFrameFromWine:NSRectFromCGRect(cgrect_mac_from_win(*new_frame))];
3419 /***********************************************************************
3420 * macdrv_get_cocoa_window_frame
3422 * Gets the frame of a Cocoa window.
3424 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
3426 WineWindow* window = (WineWindow*)w;
3431 frame = [window contentRectForFrameRect:[window wine_fractionalFrame]];
3432 [[WineApplicationController sharedController] flipRect:&frame];
3433 *out_frame = cgrect_win_from_mac(NSRectToCGRect(frame));
3437 /***********************************************************************
3438 * macdrv_set_cocoa_parent_window
3440 * Sets the parent window for a Cocoa window. If parent is NULL, clears
3441 * the parent window.
3443 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
3445 WineWindow* window = (WineWindow*)w;
3448 [window setMacDrvParentWindow:(WineWindow*)parent];
3452 /***********************************************************************
3453 * macdrv_set_window_surface
3455 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
3457 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3458 WineWindow* window = (WineWindow*)w;
3461 window.surface = surface;
3462 window.surface_mutex = mutex;
3468 /***********************************************************************
3469 * macdrv_window_needs_display
3471 * Mark a window as needing display in a specified rect (in non-client
3472 * area coordinates).
3474 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
3476 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3477 WineWindow* window = (WineWindow*)w;
3479 OnMainThreadAsync(^{
3480 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3486 /***********************************************************************
3487 * macdrv_set_window_shape
3489 * Sets the shape of a Cocoa window from an array of rectangles. If
3490 * rects is NULL, resets the window's shape to its frame.
3492 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
3494 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3495 WineWindow* window = (WineWindow*)w;
3498 if (!rects || !count)
3500 [window setShape:NULL];
3501 [window checkEmptyShaped];
3505 CGMutablePathRef path;
3508 path = CGPathCreateMutable();
3509 for (i = 0; i < count; i++)
3510 CGPathAddRect(path, NULL, cgrect_mac_from_win(rects[i]));
3511 [window setShape:path];
3512 CGPathRelease(path);
3519 /***********************************************************************
3520 * macdrv_set_window_alpha
3522 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
3524 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3525 WineWindow* window = (WineWindow*)w;
3527 [window setAlphaValue:alpha];
3532 /***********************************************************************
3533 * macdrv_set_window_color_key
3535 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
3538 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3539 WineWindow* window = (WineWindow*)w;
3542 window.colorKeyed = TRUE;
3543 window.colorKeyRed = keyRed;
3544 window.colorKeyGreen = keyGreen;
3545 window.colorKeyBlue = keyBlue;
3546 [window checkTransparency];
3552 /***********************************************************************
3553 * macdrv_clear_window_color_key
3555 void macdrv_clear_window_color_key(macdrv_window w)
3557 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3558 WineWindow* window = (WineWindow*)w;
3561 window.colorKeyed = FALSE;
3562 [window checkTransparency];
3568 /***********************************************************************
3569 * macdrv_window_use_per_pixel_alpha
3571 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
3573 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3574 WineWindow* window = (WineWindow*)w;
3577 window.usePerPixelAlpha = use_per_pixel_alpha;
3578 [window checkTransparency];
3584 /***********************************************************************
3585 * macdrv_give_cocoa_window_focus
3587 * Makes the Cocoa window "key" (gives it keyboard focus). This also
3588 * orders it front and, if its frame was not within the desktop bounds,
3589 * Cocoa will typically move it on-screen.
3591 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
3593 WineWindow* window = (WineWindow*)w;
3596 [window makeFocused:activate];
3600 /***********************************************************************
3601 * macdrv_set_window_min_max_sizes
3603 * Sets the window's minimum and maximum content sizes.
3605 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
3607 WineWindow* window = (WineWindow*)w;
3610 [window setWineMinSize:NSSizeFromCGSize(cgsize_mac_from_win(min_size)) maxSize:NSSizeFromCGSize(cgsize_mac_from_win(max_size))];
3614 /***********************************************************************
3615 * macdrv_create_view
3617 * Creates and returns a view with the specified frame rect. The
3618 * caller is responsible for calling macdrv_dispose_view() on the view
3619 * when it is done with it.
3621 macdrv_view macdrv_create_view(CGRect rect)
3623 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3624 __block WineContentView* view;
3626 if (CGRectIsNull(rect)) rect = CGRectZero;
3629 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3631 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(cgrect_mac_from_win(rect))];
3632 [view setAutoresizingMask:NSViewNotSizable];
3633 [view setHidden:YES];
3634 [view setWantsBestResolutionOpenGLSurface:retina_on];
3635 [nc addObserver:view
3636 selector:@selector(updateGLContexts)
3637 name:NSViewGlobalFrameDidChangeNotification
3639 [nc addObserver:view
3640 selector:@selector(updateGLContexts)
3641 name:NSApplicationDidChangeScreenParametersNotification
3646 return (macdrv_view)view;
3649 /***********************************************************************
3650 * macdrv_dispose_view
3652 * Destroys a view previously returned by macdrv_create_view.
3654 void macdrv_dispose_view(macdrv_view v)
3656 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3657 WineContentView* view = (WineContentView*)v;
3660 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
3661 WineWindow* window = (WineWindow*)[view window];
3663 [nc removeObserver:view
3664 name:NSViewGlobalFrameDidChangeNotification
3666 [nc removeObserver:view
3667 name:NSApplicationDidChangeScreenParametersNotification
3669 [view removeFromSuperview];
3671 [window updateForGLSubviews];
3677 /***********************************************************************
3678 * macdrv_set_view_frame
3680 void macdrv_set_view_frame(macdrv_view v, CGRect rect)
3682 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3683 WineContentView* view = (WineContentView*)v;
3685 if (CGRectIsNull(rect)) rect = CGRectZero;
3687 OnMainThreadAsync(^{
3688 NSRect newFrame = NSRectFromCGRect(cgrect_mac_from_win(rect));
3689 NSRect oldFrame = [view frame];
3691 if (!NSEqualRects(oldFrame, newFrame))
3693 [[view superview] setNeedsDisplayInRect:oldFrame];
3694 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
3695 [view setFrameSize:newFrame.size];
3696 else if (NSEqualSizes(oldFrame.size, newFrame.size))
3697 [view setFrameOrigin:newFrame.origin];
3699 [view setFrame:newFrame];
3700 [view setNeedsDisplay:YES];
3704 int backing_size[2] = { 0 };
3705 [view wine_setBackingSize:backing_size];
3707 [(WineWindow*)[view window] updateForGLSubviews];
3714 /***********************************************************************
3715 * macdrv_set_view_superview
3717 * Move a view to a new superview and position it relative to its
3718 * siblings. If p is non-NULL, the view is ordered behind it.
3719 * Otherwise, the view is ordered above n. If s is NULL, use the
3720 * content view of w as the new superview.
3722 void macdrv_set_view_superview(macdrv_view v, macdrv_view s, macdrv_window w, macdrv_view p, macdrv_view n)
3724 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3725 WineContentView* view = (WineContentView*)v;
3726 WineContentView* superview = (WineContentView*)s;
3727 WineWindow* window = (WineWindow*)w;
3728 WineContentView* prev = (WineContentView*)p;
3729 WineContentView* next = (WineContentView*)n;
3732 superview = [window contentView];
3734 OnMainThreadAsync(^{
3735 if (superview == [view superview])
3737 NSArray* subviews = [superview subviews];
3738 NSUInteger index = [subviews indexOfObjectIdenticalTo:view];
3739 if (!prev && !next && index == [subviews count] - 1)
3741 if (prev && index + 1 < [subviews count] && [subviews objectAtIndex:index + 1] == prev)
3743 if (!prev && next && index > 0 && [subviews objectAtIndex:index - 1] == next)
3747 WineWindow* oldWindow = (WineWindow*)[view window];
3748 WineWindow* newWindow = (WineWindow*)[superview window];
3750 #if !defined(MAC_OS_X_VERSION_10_10) || MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_10
3751 if (floor(NSAppKitVersionNumber) <= 1265 /*NSAppKitVersionNumber10_9*/)
3752 [view removeFromSuperview];
3755 [superview addSubview:view positioned:NSWindowBelow relativeTo:prev];
3757 [superview addSubview:view positioned:NSWindowAbove relativeTo:next];
3759 if (oldWindow != newWindow)
3761 [oldWindow updateForGLSubviews];
3762 [newWindow updateForGLSubviews];
3769 /***********************************************************************
3770 * macdrv_set_view_hidden
3772 void macdrv_set_view_hidden(macdrv_view v, int hidden)
3774 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3775 WineContentView* view = (WineContentView*)v;
3777 OnMainThreadAsync(^{
3778 [view setHidden:hidden];
3779 [(WineWindow*)view.window updateForGLSubviews];
3785 /***********************************************************************
3786 * macdrv_add_view_opengl_context
3788 * Add an OpenGL context to the list being tracked for each view.
3790 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3792 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3793 WineContentView* view = (WineContentView*)v;
3794 WineOpenGLContext *context = (WineOpenGLContext*)c;
3797 [view addGLContext:context];
3803 /***********************************************************************
3804 * macdrv_remove_view_opengl_context
3806 * Add an OpenGL context to the list being tracked for each view.
3808 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
3810 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3811 WineContentView* view = (WineContentView*)v;
3812 WineOpenGLContext *context = (WineOpenGLContext*)c;
3814 OnMainThreadAsync(^{
3815 [view removeGLContext:context];
3821 macdrv_metal_device macdrv_create_metal_device(void)
3823 macdrv_metal_device ret;
3825 #if MAC_OS_X_VERSION_MIN_REQUIRED < MAC_OS_X_VERSION_10_11
3826 if (MTLCreateSystemDefaultDevice == NULL)
3830 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3831 ret = (macdrv_metal_device)MTLCreateSystemDefaultDevice();
3836 void macdrv_release_metal_device(macdrv_metal_device d)
3838 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
3839 [(id<MTLDevice>)d release];
3843 macdrv_metal_view macdrv_view_create_metal_view(macdrv_view v, macdrv_metal_device d)
3845 id<MTLDevice> device = (id<MTLDevice>)d;
3846 WineContentView* view = (WineContentView*)v;
3847 __block WineMetalView *metalView;
3850 metalView = [view newMetalViewWithDevice:device];
3853 return (macdrv_metal_view)metalView;
3856 macdrv_metal_layer macdrv_view_get_metal_layer(macdrv_metal_view v)
3858 WineMetalView* view = (WineMetalView*)v;
3859 __block CAMetalLayer* layer;
3862 layer = (CAMetalLayer*)view.layer;
3865 return (macdrv_metal_layer)layer;
3868 void macdrv_view_release_metal_view(macdrv_metal_view v)
3870 WineMetalView* view = (WineMetalView*)v;
3872 [view removeFromSuperview];
3877 int macdrv_get_view_backing_size(macdrv_view v, int backing_size[2])
3879 WineContentView* view = (WineContentView*)v;
3881 if (![view isKindOfClass:[WineContentView class]])
3884 [view wine_getBackingSize:backing_size];
3888 void macdrv_set_view_backing_size(macdrv_view v, const int backing_size[2])
3890 WineContentView* view = (WineContentView*)v;
3892 if ([view isKindOfClass:[WineContentView class]])
3893 [view wine_setBackingSize:backing_size];
3896 /***********************************************************************
3897 * macdrv_window_background_color
3899 * Returns the standard Mac window background color as a 32-bit value of
3900 * the form 0x00rrggbb.
3902 uint32_t macdrv_window_background_color(void)
3904 static uint32_t result;
3905 static dispatch_once_t once;
3907 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
3908 // color spaces (RGB or grayscale). So, the only way to get RGB values out
3909 // of it is to draw with it.
3910 dispatch_once(&once, ^{
3912 unsigned char rgbx[4];
3913 unsigned char *planes = rgbx;
3914 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
3921 colorSpaceName:NSCalibratedRGBColorSpace
3925 [NSGraphicsContext saveGraphicsState];
3926 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
3927 [[NSColor windowBackgroundColor] set];
3928 NSRectFill(NSMakeRect(0, 0, 1, 1));
3929 [NSGraphicsContext restoreGraphicsState];
3931 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
3938 /***********************************************************************
3939 * macdrv_send_text_input_event
3941 void macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* himc, int* done)
3943 OnMainThreadAsync(^{
3945 macdrv_event* event;
3946 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3947 if (![window isKindOfClass:[WineWindow class]])
3949 window = (WineWindow*)[NSApp mainWindow];
3950 if (![window isKindOfClass:[WineWindow class]])
3951 window = [[WineApplicationController sharedController] frontWineWindow];
3956 NSUInteger localFlags = flags;
3961 fix_device_modifiers_by_generic(&localFlags);
3963 // An NSEvent created with +keyEventWithType:... is internally marked
3964 // as synthetic and doesn't get sent through input methods. But one
3965 // created from a CGEvent doesn't have that problem.
3966 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
3967 CGEventSetFlags(c, localFlags);
3968 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
3969 event = [NSEvent eventWithCGEvent:c];
3972 window.commandDone = FALSE;
3973 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;
3978 event = macdrv_create_event(SENT_TEXT_INPUT, window);
3979 event->sent_text_input.handled = ret;
3980 event->sent_text_input.done = done;
3981 [[window queue] postEvent:event];
3982 macdrv_release_event(event);
3986 void macdrv_clear_ime_text(void)
3988 OnMainThreadAsync(^{
3989 WineWindow* window = (WineWindow*)[NSApp keyWindow];
3990 if (![window isKindOfClass:[WineWindow class]])
3992 window = (WineWindow*)[NSApp mainWindow];
3993 if (![window isKindOfClass:[WineWindow class]])
3994 window = [[WineApplicationController sharedController] frontWineWindow];
3997 [[window contentView] clearMarkedText];