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
21 #import <Carbon/Carbon.h>
23 #import "cocoa_window.h"
25 #include "macdrv_cocoa.h"
27 #import "cocoa_event.h"
28 #import "cocoa_opengl.h"
31 #if !defined(MAC_OS_X_VERSION_10_7) || MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_7
33 NSWindowCollectionBehaviorFullScreenPrimary = 1 << 7,
34 NSWindowCollectionBehaviorFullScreenAuxiliary = 1 << 8,
35 NSWindowFullScreenButton = 7,
36 NSFullScreenWindowMask = 1 << 14,
39 @interface NSWindow (WineFullScreenExtensions)
40 - (void) toggleFullScreen:(id)sender;
45 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
47 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
51 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
53 NSUInteger style_mask;
57 style_mask = NSTitledWindowMask;
58 if (wf->close_button) style_mask |= NSClosableWindowMask;
59 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
60 if (wf->resizable || wf->maximize_button) style_mask |= NSResizableWindowMask;
61 if (wf->utility) style_mask |= NSUtilityWindowMask;
63 else style_mask = NSBorderlessWindowMask;
69 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
72 for (screen in screens)
74 if (NSIntersectsRect(frame, [screen frame]))
81 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
83 for (NSScreen* screen in screens)
85 if (NSContainsRect(rect, [screen frame]))
92 /* We rely on the supposedly device-dependent modifier flags to distinguish the
93 keys on the left side of the keyboard from those on the right. Some event
94 sources don't set those device-depdendent flags. If we see a device-independent
95 flag for a modifier without either corresponding device-dependent flag, assume
97 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
99 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
100 *modifiers |= NX_DEVICELCMDKEYMASK;
101 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
102 *modifiers |= NX_DEVICELSHIFTKEYMASK;
103 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
104 *modifiers |= NX_DEVICELCTLKEYMASK;
105 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
106 *modifiers |= NX_DEVICELALTKEYMASK;
109 /* As we manipulate individual bits of a modifier mask, we can end up with
110 inconsistent sets of flags. In particular, we might set or clear one of the
111 left/right-specific bits, but not the corresponding non-side-specific bit.
112 Fix that. If either side-specific bit is set, set the non-side-specific bit,
113 otherwise clear it. */
114 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
116 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
117 *modifiers |= NX_COMMANDMASK;
119 *modifiers &= ~NX_COMMANDMASK;
120 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
121 *modifiers |= NX_SHIFTMASK;
123 *modifiers &= ~NX_SHIFTMASK;
124 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
125 *modifiers |= NX_CONTROLMASK;
127 *modifiers &= ~NX_CONTROLMASK;
128 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
129 *modifiers |= NX_ALTERNATEMASK;
131 *modifiers &= ~NX_ALTERNATEMASK;
134 static inline NSUInteger adjusted_modifiers_for_option_behavior(NSUInteger modifiers)
136 fix_device_modifiers_by_generic(&modifiers);
137 if (left_option_is_alt && (modifiers & NX_DEVICELALTKEYMASK))
139 modifiers |= NX_DEVICELCMDKEYMASK;
140 modifiers &= ~NX_DEVICELALTKEYMASK;
142 if (right_option_is_alt && (modifiers & NX_DEVICERALTKEYMASK))
144 modifiers |= NX_DEVICERCMDKEYMASK;
145 modifiers &= ~NX_DEVICERALTKEYMASK;
147 fix_generic_modifiers_by_device(&modifiers);
153 @interface WineContentView : NSView <NSTextInputClient>
155 NSMutableArray* glContexts;
156 NSMutableArray* pendingGlContexts;
157 BOOL clearedGlSurface;
159 NSMutableAttributedString* markedText;
160 NSRange markedTextSelection;
163 - (void) addGLContext:(WineOpenGLContext*)context;
164 - (void) removeGLContext:(WineOpenGLContext*)context;
165 - (void) updateGLContexts;
170 @interface WineWindow ()
172 @property (readwrite, nonatomic) BOOL disabled;
173 @property (readwrite, nonatomic) BOOL noActivate;
174 @property (readwrite, nonatomic) BOOL floating;
175 @property (readwrite, getter=isFakingClose, nonatomic) BOOL fakingClose;
176 @property (retain, nonatomic) NSWindow* latentParentWindow;
178 @property (nonatomic) void* hwnd;
179 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
181 @property (nonatomic) void* surface;
182 @property (nonatomic) pthread_mutex_t* surface_mutex;
184 @property (copy, nonatomic) NSBezierPath* shape;
185 @property (copy, nonatomic) NSData* shapeData;
186 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
187 @property (readonly, nonatomic) BOOL needsTransparency;
189 @property (nonatomic) BOOL colorKeyed;
190 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
191 @property (nonatomic) BOOL usePerPixelAlpha;
193 @property (assign, nonatomic) void* imeData;
194 @property (nonatomic) BOOL commandDone;
196 @property (retain, nonatomic) NSTimer* liveResizeDisplayTimer;
198 @property (readonly, copy, nonatomic) NSArray* childWineWindows;
200 - (void) updateColorSpace;
202 - (BOOL) becameEligibleParentOrChild;
203 - (void) becameIneligibleChild;
208 @implementation WineContentView
212 [markedText release];
213 [glContexts release];
214 [pendingGlContexts release];
223 - (void) drawRect:(NSRect)rect
225 WineWindow* window = (WineWindow*)[self window];
227 for (WineOpenGLContext* context in pendingGlContexts)
229 if (!clearedGlSurface)
231 context.shouldClearToBlack = TRUE;
232 clearedGlSurface = TRUE;
234 context.needsUpdate = TRUE;
236 [glContexts addObjectsFromArray:pendingGlContexts];
237 [pendingGlContexts removeAllObjects];
239 if ([window contentView] != self)
242 if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
244 [[NSColor clearColor] setFill];
247 [window.shape addClip];
249 [[NSColor windowBackgroundColor] setFill];
253 if (window.surface && window.surface_mutex &&
254 !pthread_mutex_lock(window.surface_mutex))
259 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
261 CGContextRef context;
264 [window.shape addClip];
266 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
267 CGContextSetBlendMode(context, kCGBlendModeCopy);
268 CGContextSetInterpolationQuality(context, kCGInterpolationNone);
270 for (i = 0; i < count; i++)
275 imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
276 image = create_surface_image(window.surface, &imageRect, FALSE);
280 if (window.colorKeyed)
282 CGImageRef maskedImage;
283 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
284 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
285 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
286 maskedImage = CGImageCreateWithMaskingColors(image, components);
289 CGImageRelease(image);
294 CGContextDrawImage(context, imageRect, image);
296 CGImageRelease(image);
301 pthread_mutex_unlock(window.surface_mutex);
304 // If the window may be transparent, then we have to invalidate the
305 // shadow every time we draw. Also, if this is the first time we've
306 // drawn since changing from transparent to opaque.
307 if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
309 window.shapeChangedSinceLastDraw = FALSE;
310 [window invalidateShadow];
314 - (void) addGLContext:(WineOpenGLContext*)context
317 glContexts = [[NSMutableArray alloc] init];
318 if (!pendingGlContexts)
319 pendingGlContexts = [[NSMutableArray alloc] init];
321 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
323 [glContexts addObject:context];
324 if (!clearedGlSurface)
326 context.shouldClearToBlack = TRUE;
327 clearedGlSurface = TRUE;
329 context.needsUpdate = TRUE;
333 [pendingGlContexts addObject:context];
334 [self setNeedsDisplay:YES];
337 [(WineWindow*)[self window] updateColorSpace];
340 - (void) removeGLContext:(WineOpenGLContext*)context
342 [glContexts removeObjectIdenticalTo:context];
343 [pendingGlContexts removeObjectIdenticalTo:context];
344 [(WineWindow*)[self window] updateColorSpace];
347 - (void) updateGLContexts
349 for (WineOpenGLContext* context in glContexts)
350 context.needsUpdate = TRUE;
353 - (BOOL) hasGLContext
355 return [glContexts count] || [pendingGlContexts count];
358 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
363 - (BOOL) preservesContentDuringLiveResize
365 // Returning YES from this tells Cocoa to keep our view's content during
366 // a Cocoa-driven resize. In theory, we're also supposed to override
367 // -setFrameSize: to mark exposed sections as needing redisplay, but
368 // user32 will take care of that in a roundabout way. This way, we don't
369 // redraw until the window surface is flushed.
371 // This doesn't do anything when we resize the window ourselves.
375 - (BOOL)acceptsFirstResponder
377 return [[self window] contentView] == self;
380 - (BOOL) mouseDownCanMoveWindow
385 - (void) completeText:(NSString*)text
388 WineWindow* window = (WineWindow*)[self window];
390 event = macdrv_create_event(IM_SET_TEXT, window);
391 event->im_set_text.data = [window imeData];
392 event->im_set_text.text = (CFStringRef)[text copy];
393 event->im_set_text.complete = TRUE;
395 [[window queue] postEvent:event];
397 macdrv_release_event(event);
399 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
400 markedTextSelection = NSMakeRange(0, 0);
401 [[self inputContext] discardMarkedText];
404 - (NSFocusRingType) focusRingType
406 return NSFocusRingTypeNone;
410 * ---------- NSTextInputClient methods ----------
412 - (NSTextInputContext*) inputContext
415 markedText = [[NSMutableAttributedString alloc] init];
416 return [super inputContext];
419 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
421 if ([string isKindOfClass:[NSAttributedString class]])
422 string = [string string];
424 if ([string isKindOfClass:[NSString class]])
425 [self completeText:string];
428 - (void) doCommandBySelector:(SEL)aSelector
430 [(WineWindow*)[self window] setCommandDone:TRUE];
433 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
435 if ([string isKindOfClass:[NSAttributedString class]])
436 string = [string string];
438 if ([string isKindOfClass:[NSString class]])
441 WineWindow* window = (WineWindow*)[self window];
443 if (replacementRange.location == NSNotFound)
444 replacementRange = NSMakeRange(0, [markedText length]);
446 [markedText replaceCharactersInRange:replacementRange withString:string];
447 markedTextSelection = selectedRange;
448 markedTextSelection.location += replacementRange.location;
450 event = macdrv_create_event(IM_SET_TEXT, window);
451 event->im_set_text.data = [window imeData];
452 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
453 event->im_set_text.complete = FALSE;
454 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
456 [[window queue] postEvent:event];
458 macdrv_release_event(event);
460 [[self inputContext] invalidateCharacterCoordinates];
466 [self completeText:nil];
469 - (NSRange) selectedRange
471 return markedTextSelection;
474 - (NSRange) markedRange
476 NSRange range = NSMakeRange(0, [markedText length]);
478 range.location = NSNotFound;
482 - (BOOL) hasMarkedText
484 return [markedText length] > 0;
487 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
489 if (aRange.location >= [markedText length])
492 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
494 *actualRange = aRange;
495 return [markedText attributedSubstringFromRange:aRange];
498 - (NSArray*) validAttributesForMarkedText
500 return [NSArray array];
503 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
506 WineWindow* window = (WineWindow*)[self window];
509 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
511 query = macdrv_create_query();
512 query->type = QUERY_IME_CHAR_RECT;
513 query->window = (macdrv_window)[window retain];
514 query->ime_char_rect.data = [window imeData];
515 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
517 if ([window.queue query:query timeout:1])
519 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
520 ret = NSRectFromCGRect(query->ime_char_rect.rect);
521 [[WineApplicationController sharedController] flipRect:&ret];
524 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
526 macdrv_release_query(query);
529 *actualRange = aRange;
533 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
538 - (NSInteger) windowLevel
540 return [[self window] level];
546 @implementation WineWindow
548 static WineWindow* causing_becomeKeyWindow;
550 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
551 @synthesize surface, surface_mutex;
552 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
553 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
554 @synthesize usePerPixelAlpha;
555 @synthesize imeData, commandDone;
556 @synthesize liveResizeDisplayTimer;
558 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
559 windowFrame:(NSRect)window_frame
561 queue:(WineEventQueue*)queue
564 WineContentView* contentView;
565 NSTrackingArea* trackingArea;
566 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
568 [[WineApplicationController sharedController] flipRect:&window_frame];
570 window = [[[self alloc] initWithContentRect:window_frame
571 styleMask:style_mask_for_features(wf)
572 backing:NSBackingStoreBuffered
573 defer:YES] autorelease];
575 if (!window) return nil;
577 /* Standardize windows to eliminate differences between titled and
578 borderless windows and between NSWindow and NSPanel. */
579 [window setHidesOnDeactivate:NO];
580 [window setReleasedWhenClosed:NO];
582 [window setOneShot:YES];
583 [window disableCursorRects];
584 [window setShowsResizeIndicator:NO];
585 [window setHasShadow:wf->shadow];
586 [window setAcceptsMouseMovedEvents:YES];
587 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
588 [window setDelegate:window];
590 window.queue = queue;
591 window->savedContentMinSize = NSZeroSize;
592 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
593 window->resizable = wf->resizable;
595 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
596 (NSString*)kUTTypeContent,
599 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
602 [contentView setAutoresizesSubviews:NO];
604 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
605 because they give us mouse moves in the background. */
606 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
607 options:(NSTrackingMouseMoved |
608 NSTrackingActiveAlways |
609 NSTrackingInVisibleRect)
611 userInfo:nil] autorelease];
614 [contentView addTrackingArea:trackingArea];
616 [window setContentView:contentView];
617 [window setInitialFirstResponder:contentView];
619 [nc addObserver:window
620 selector:@selector(updateFullscreen)
621 name:NSApplicationDidChangeScreenParametersNotification
623 [window updateFullscreen];
625 [nc addObserver:window
626 selector:@selector(applicationWillHide)
627 name:NSApplicationWillHideNotification
629 [nc addObserver:window
630 selector:@selector(applicationDidUnhide)
631 name:NSApplicationDidUnhideNotification
639 [[NSNotificationCenter defaultCenter] removeObserver:self];
640 [liveResizeDisplayTimer invalidate];
641 [liveResizeDisplayTimer release];
643 [latentChildWindows release];
644 [latentParentWindow release];
650 - (BOOL) preventResizing
652 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
653 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || maximized || preventForClipping);
656 - (void) adjustFeaturesForState
658 NSUInteger style = [self styleMask];
660 if (style & NSClosableWindowMask)
661 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
662 if (style & NSMiniaturizableWindowMask)
663 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
664 if (style & NSResizableWindowMask)
665 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
666 if ([self respondsToSelector:@selector(toggleFullScreen:)])
668 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
669 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
672 if ([self preventResizing])
674 NSSize size = [self contentRectForFrameRect:[self frame]].size;
675 [self setContentMinSize:size];
676 [self setContentMaxSize:size];
680 [self setContentMaxSize:savedContentMaxSize];
681 [self setContentMinSize:savedContentMinSize];
684 if (allow_immovable_windows || cursor_clipping_locks_windows)
686 if (allow_immovable_windows && (disabled || maximized))
687 [self setMovable:NO];
688 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
689 [self setMovable:NO];
691 [self setMovable:YES];
695 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
697 if ([self respondsToSelector:@selector(toggleFullScreen:)])
699 NSUInteger style = [self styleMask];
701 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
702 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized)
704 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
705 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
709 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
710 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
711 if (style & NSFullScreenWindowMask)
712 [super toggleFullScreen:nil];
716 if (behavior != [self collectionBehavior])
718 [self setCollectionBehavior:behavior];
719 [self adjustFeaturesForState];
723 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
725 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
726 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
727 NSUInteger currentStyle = [self styleMask];
728 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
730 if (newStyle != currentStyle)
732 NSString* title = [[[self title] copy] autorelease];
733 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
734 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
735 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
737 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
738 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
739 // just NSTitledWindowMask, the window buttons should disappear rather
740 // than just being disabled. But they don't. Similarly in reverse.
741 // The workaround is to also toggle NSClosableWindowMask at the same time.
742 [self setStyleMask:newStyle ^ NSClosableWindowMask];
744 [self setStyleMask:newStyle];
746 // -setStyleMask: resets the firstResponder to the window. Set it
747 // back to the content view.
748 if ([[self contentView] acceptsFirstResponder])
749 [self makeFirstResponder:[self contentView]];
751 [self adjustFullScreenBehavior:[self collectionBehavior]];
753 if ([[self title] length] == 0 && [title length] > 0)
754 [self setTitle:title];
757 resizable = wf->resizable;
758 [self adjustFeaturesForState];
759 [self setHasShadow:wf->shadow];
762 // Indicates if the window would be visible if the app were not hidden.
763 - (BOOL) wouldBeVisible
765 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
770 return [self wouldBeVisible] || [self isMiniaturized];
773 - (NSInteger) minimumLevelForActive:(BOOL)active
777 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
778 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
779 level = NSFloatingWindowLevel;
781 level = NSNormalWindowLevel;
787 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
789 if (captured || fullscreen)
792 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
794 level = NSStatusWindowLevel + 1;
804 - (void) postDidUnminimizeEvent
808 /* Coalesce events by discarding any previous ones still in the queue. */
809 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
812 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
813 [queue postEvent:event];
814 macdrv_release_event(event);
817 - (void) setMacDrvState:(const struct macdrv_window_state*)state
819 NSWindowCollectionBehavior behavior;
821 self.disabled = state->disabled;
822 self.noActivate = state->no_activate;
824 if (self.floating != state->floating)
826 self.floating = state->floating;
829 // Became floating. If child of non-floating window, make that
830 // relationship latent.
831 WineWindow* parent = (WineWindow*)[self parentWindow];
832 if (parent && !parent.floating)
833 [self becameIneligibleChild];
837 // Became non-floating. If parent of floating children, make that
838 // relationship latent.
840 for (child in [self childWineWindows])
843 [child becameIneligibleChild];
847 // Check our latent relationships. If floating status was the only
848 // reason they were latent, then make them active.
849 if ([self isVisible])
850 [self becameEligibleParentOrChild];
852 [[WineApplicationController sharedController] adjustWindowLevels];
855 if (state->minimized_valid)
857 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
859 pendingMinimize = FALSE;
860 if (state->minimized && ![self isMiniaturized])
862 if ([self wouldBeVisible])
864 if ([self styleMask] & NSFullScreenWindowMask)
866 [self postDidUnminimizeEvent];
867 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
871 [super miniaturize:nil];
872 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
873 event_mask_for_type(WINDOW_GOT_FOCUS) |
874 event_mask_for_type(WINDOW_LOST_FOCUS);
878 pendingMinimize = TRUE;
880 else if (!state->minimized && [self isMiniaturized])
882 ignore_windowDeminiaturize = TRUE;
883 [self deminiaturize:nil];
884 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
888 [queue discardEventsMatchingMask:discard forWindow:self];
891 if (state->maximized != maximized)
893 maximized = state->maximized;
894 [self adjustFeaturesForState];
897 behavior = NSWindowCollectionBehaviorDefault;
898 if (state->excluded_by_expose)
899 behavior |= NSWindowCollectionBehaviorTransient;
901 behavior |= NSWindowCollectionBehaviorManaged;
902 if (state->excluded_by_cycle)
904 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
905 if ([self isOrderedIn])
906 [NSApp removeWindowsItem:self];
910 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
911 if ([self isOrderedIn])
912 [NSApp addWindowsItem:self title:[self title] filename:NO];
914 [self adjustFullScreenBehavior:behavior];
917 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
919 BOOL reordered = FALSE;
921 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
923 if ([self level] > [child level])
924 [child setLevel:[self level]];
925 [self addChildWindow:child ordered:NSWindowAbove];
926 [latentChildWindows removeObjectIdenticalTo:child];
927 child.latentParentWindow = nil;
932 if (!latentChildWindows)
933 latentChildWindows = [[NSMutableArray alloc] init];
934 if (![latentChildWindows containsObject:child])
935 [latentChildWindows addObject:child];
936 child.latentParentWindow = self;
942 - (BOOL) addChildWineWindow:(WineWindow*)child
944 return [self addChildWineWindow:child assumeVisible:FALSE];
947 - (void) removeChildWineWindow:(WineWindow*)child
949 [self removeChildWindow:child];
950 if (child.latentParentWindow == self)
951 child.latentParentWindow = nil;
952 [latentChildWindows removeObjectIdenticalTo:child];
955 - (BOOL) becameEligibleParentOrChild
957 BOOL reordered = FALSE;
960 if (latentParentWindow.floating || !self.floating)
962 // If we aren't visible currently, we assume that we should be and soon
963 // will be. So, if the latent parent is visible that's enough to assume
964 // we can establish the parent-child relationship in Cocoa. That will
965 // actually make us visible, which is fine.
966 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
970 // Here, though, we may not actually be visible yet and adding a child
971 // won't make us visible. The caller will have to call this method
972 // again after actually making us visible.
973 if ([self isVisible] && (count = [latentChildWindows count]))
975 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
978 for (i = 0; i < count; i++)
980 WineWindow* child = [latentChildWindows objectAtIndex:i];
981 if ([child isVisible] && (self.floating || !child.floating))
983 if (child.latentParentWindow == self)
985 if ([self level] > [child level])
986 [child setLevel:[self level]];
987 [self addChildWindow:child ordered:NSWindowAbove];
988 child.latentParentWindow = nil;
992 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
993 [indexesToRemove addIndex:i];
997 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1003 - (void) becameIneligibleChild
1005 WineWindow* parent = (WineWindow*)[self parentWindow];
1008 if (!parent->latentChildWindows)
1009 parent->latentChildWindows = [[NSMutableArray alloc] init];
1010 [parent->latentChildWindows insertObject:self atIndex:0];
1011 self.latentParentWindow = parent;
1012 [parent removeChildWindow:self];
1016 - (void) becameIneligibleParentOrChild
1018 NSArray* childWindows = [self childWineWindows];
1020 [self becameIneligibleChild];
1022 if ([childWindows count])
1026 for (child in childWindows)
1028 child.latentParentWindow = self;
1029 [self removeChildWindow:child];
1032 if (latentChildWindows)
1033 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1035 latentChildWindows = [childWindows mutableCopy];
1039 // Determine if, among Wine windows, this window is directly above or below
1040 // a given other Wine window with no other Wine window intervening.
1041 // Intervening non-Wine windows are ignored.
1042 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1044 NSNumber* windowNumber;
1045 NSNumber* otherWindowNumber;
1046 NSArray* windowNumbers;
1047 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1049 if (![self isVisible] || ![otherWindow isVisible])
1052 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1053 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1054 windowNumbers = [[self class] windowNumbersWithOptions:0];
1055 windowIndex = [windowNumbers indexOfObject:windowNumber];
1056 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1058 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1061 if (orderingMode == NSWindowAbove)
1063 lowIndex = windowIndex;
1064 highIndex = otherWindowIndex;
1066 else if (orderingMode == NSWindowBelow)
1068 lowIndex = otherWindowIndex;
1069 highIndex = windowIndex;
1074 if (highIndex <= lowIndex)
1077 for (i = lowIndex + 1; i < highIndex; i++)
1079 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1080 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1081 if ([interveningWindow isKindOfClass:[WineWindow class]])
1088 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1090 NSMutableArray* windowNumbers;
1091 NSNumber* childWindowNumber;
1092 NSUInteger otherIndex, limit;
1093 NSArray* origChildren;
1094 NSMutableArray* children;
1096 // Get the z-order from the window server and modify it to reflect the
1097 // requested window ordering.
1098 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1099 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1100 [windowNumbers removeObject:childWindowNumber];
1101 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1102 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1104 // Get our child windows and sort them in the reverse of the desired
1105 // z-order (back-to-front).
1106 origChildren = [self childWineWindows];
1107 children = [[origChildren mutableCopy] autorelease];
1108 [children sortWithOptions:NSSortStable
1109 usingComparator:^NSComparisonResult(id obj1, id obj2){
1110 NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1111 NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1112 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1113 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1114 if (index1 == NSNotFound)
1116 if (index2 == NSNotFound)
1117 return NSOrderedSame;
1119 return NSOrderedAscending;
1121 else if (index2 == NSNotFound)
1122 return NSOrderedDescending;
1123 else if (index1 < index2)
1124 return NSOrderedDescending;
1125 else if (index2 < index1)
1126 return NSOrderedAscending;
1128 return NSOrderedSame;
1131 // If the current and desired children arrays match up to a point, leave
1132 // those matching children alone.
1133 limit = MIN([origChildren count], [children count]);
1134 for (otherIndex = 0; otherIndex < limit; otherIndex++)
1136 if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1139 [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1141 // Remove all of the child windows and re-add them back-to-front so they
1142 // are in the desired order.
1143 for (other in children)
1144 [self removeChildWindow:other];
1145 for (other in children)
1146 [self addChildWindow:other ordered:NSWindowAbove];
1149 /* Returns whether or not the window was ordered in, which depends on if
1150 its frame intersects any screen. */
1151 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1153 WineApplicationController* controller = [WineApplicationController sharedController];
1154 if (![self isMiniaturized])
1156 BOOL needAdjustWindowLevels = FALSE;
1159 [controller transformProcessToForeground];
1161 wasVisible = [self isVisible];
1164 [NSApp activateIgnoringOtherApps:YES];
1166 NSDisableScreenUpdates();
1168 if ([self becameEligibleParentOrChild])
1169 needAdjustWindowLevels = TRUE;
1173 WineWindow* other = [prev isVisible] ? prev : next;
1174 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1176 if (![self isOrdered:orderingMode relativeTo:other])
1178 WineWindow* parent = (WineWindow*)[self parentWindow];
1179 WineWindow* otherParent = (WineWindow*)[other parentWindow];
1181 // This window level may not be right for this window based
1182 // on floating-ness, fullscreen-ness, etc. But we set it
1183 // temporarily to allow us to order the windows properly.
1184 // Then the levels get fixed by -adjustWindowLevels.
1185 if ([self level] != [other level])
1186 [self setLevel:[other level]];
1187 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1189 // The above call to -[NSWindow orderWindow:relativeTo:] won't
1190 // reorder windows which are both children of the same parent
1191 // relative to each other, so do that separately.
1192 if (parent && parent == otherParent)
1193 [parent order:orderingMode childWindow:self relativeTo:other];
1195 needAdjustWindowLevels = TRUE;
1200 // Again, temporarily set level to make sure we can order to
1202 next = [controller frontWineWindow];
1203 if (next && [self level] < [next level])
1204 [self setLevel:[next level]];
1205 [self orderFront:nil];
1206 needAdjustWindowLevels = TRUE;
1209 if ([self becameEligibleParentOrChild])
1210 needAdjustWindowLevels = TRUE;
1212 if (needAdjustWindowLevels)
1214 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1215 [controller updateFullscreenWindows];
1216 [controller adjustWindowLevels];
1219 if (pendingMinimize)
1221 [super miniaturize:nil];
1222 pendingMinimize = FALSE;
1225 NSEnableScreenUpdates();
1227 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1228 Generate a frame-changed event just in case. The back end will ignore
1229 it if nothing actually changed. */
1230 [self windowDidResize:nil];
1232 if (![self isExcludedFromWindowsMenu])
1233 [NSApp addWindowsItem:self title:[self title] filename:NO];
1239 WineApplicationController* controller = [WineApplicationController sharedController];
1240 BOOL wasVisible = [self isVisible];
1241 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1243 if ([self isMiniaturized])
1244 pendingMinimize = TRUE;
1246 [self becameIneligibleParentOrChild];
1247 if ([self isMiniaturized])
1251 fakingClose = FALSE;
1254 [self orderOut:nil];
1255 savedVisibleState = FALSE;
1256 if (wasVisible && wasOnActiveSpace && fullscreen)
1257 [controller updateFullscreenWindows];
1258 [controller adjustWindowLevels];
1259 [NSApp removeWindowsItem:self];
1261 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1262 event_mask_for_type(WINDOW_GOT_FOCUS) |
1263 event_mask_for_type(WINDOW_LOST_FOCUS) |
1264 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1265 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1266 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1270 - (void) updateFullscreen
1272 NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1273 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1275 if (nowFullscreen != fullscreen)
1277 WineApplicationController* controller = [WineApplicationController sharedController];
1279 fullscreen = nowFullscreen;
1280 if ([self isVisible] && [self isOnActiveSpace])
1281 [controller updateFullscreenWindows];
1283 [controller adjustWindowLevels];
1287 - (void) setFrameFromWine:(NSRect)contentRect
1289 /* Origin is (left, top) in a top-down space. Need to convert it to
1290 (left, bottom) in a bottom-up space. */
1291 [[WineApplicationController sharedController] flipRect:&contentRect];
1293 /* The back end is establishing a new window size and position. It's
1294 not interested in any stale events regarding those that may be sitting
1296 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1299 if (!NSIsEmptyRect(contentRect))
1301 NSRect frame, oldFrame;
1303 oldFrame = [self frame];
1304 frame = [self frameRectForContentRect:contentRect];
1305 if (!NSEqualRects(frame, oldFrame))
1307 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1308 BOOL needEnableScreenUpdates = FALSE;
1310 if ([self preventResizing])
1312 // Allow the following calls to -setFrame:display: to work even
1313 // if they would violate the content size constraints. This
1314 // shouldn't be necessary since the content size constraints are
1315 // documented to not constrain that method, but it seems to be.
1316 [self setContentMinSize:NSZeroSize];
1317 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1320 if (equalSizes && [[self childWineWindows] count])
1322 // If we change the window frame such that the origin moves
1323 // but the size doesn't change, then Cocoa moves child
1324 // windows with the parent. We don't want that so we fake
1325 // a change of the size and then change it back.
1326 NSRect bogusFrame = frame;
1327 bogusFrame.size.width++;
1329 NSDisableScreenUpdates();
1330 needEnableScreenUpdates = TRUE;
1332 ignore_windowResize = TRUE;
1333 [self setFrame:bogusFrame display:NO];
1334 ignore_windowResize = FALSE;
1337 [self setFrame:frame display:YES];
1338 if ([self preventResizing])
1340 [self setContentMinSize:contentRect.size];
1341 [self setContentMaxSize:contentRect.size];
1344 if (needEnableScreenUpdates)
1345 NSEnableScreenUpdates();
1348 [self updateColorSpace];
1350 if (!enteringFullScreen &&
1351 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1352 nonFullscreenFrame = frame;
1354 [self updateFullscreen];
1356 if ([self isOrderedIn])
1358 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1359 event. The back end will ignore it if nothing actually changed. */
1360 [self windowDidResize:nil];
1366 - (void) setMacDrvParentWindow:(WineWindow*)parent
1368 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1369 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1371 [oldParent removeChildWineWindow:self];
1372 [latentParentWindow removeChildWineWindow:self];
1373 if ([parent addChildWineWindow:self])
1374 [[WineApplicationController sharedController] adjustWindowLevels];
1378 - (void) setDisabled:(BOOL)newValue
1380 if (disabled != newValue)
1382 disabled = newValue;
1383 [self adjustFeaturesForState];
1387 - (BOOL) needsTransparency
1389 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1392 - (void) checkTransparency
1394 if (![self isOpaque] && !self.needsTransparency)
1396 self.shapeChangedSinceLastDraw = TRUE;
1397 [[self contentView] setNeedsDisplay:YES];
1398 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1399 [self setOpaque:YES];
1401 else if ([self isOpaque] && self.needsTransparency)
1403 self.shapeChangedSinceLastDraw = TRUE;
1404 [[self contentView] setNeedsDisplay:YES];
1405 [self setBackgroundColor:[NSColor clearColor]];
1406 [self setOpaque:NO];
1410 - (void) setShape:(NSBezierPath*)newShape
1412 if (shape == newShape) return;
1416 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1420 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1422 shape = [newShape copy];
1423 self.shapeChangedSinceLastDraw = TRUE;
1425 [self checkTransparency];
1428 - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1430 if (newTimer != liveResizeDisplayTimer)
1432 [liveResizeDisplayTimer invalidate];
1433 [liveResizeDisplayTimer release];
1434 liveResizeDisplayTimer = [newTimer retain];
1438 - (void) makeFocused:(BOOL)activate
1442 [[WineApplicationController sharedController] transformProcessToForeground];
1443 [NSApp activateIgnoringOtherApps:YES];
1446 causing_becomeKeyWindow = self;
1447 [self makeKeyWindow];
1448 causing_becomeKeyWindow = nil;
1450 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1451 event_mask_for_type(WINDOW_LOST_FOCUS)
1455 - (void) postKey:(uint16_t)keyCode
1456 pressed:(BOOL)pressed
1457 modifiers:(NSUInteger)modifiers
1458 event:(NSEvent*)theEvent
1460 macdrv_event* event;
1462 WineApplicationController* controller = [WineApplicationController sharedController];
1464 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1465 event->key.keycode = keyCode;
1466 event->key.modifiers = modifiers;
1467 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1469 if ((cgevent = [theEvent CGEvent]))
1471 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1472 kCGKeyboardEventKeyboardType);
1473 if (keyboardType != controller.keyboardType)
1475 controller.keyboardType = keyboardType;
1476 [controller keyboardSelectionDidChange];
1480 [queue postEvent:event];
1482 macdrv_release_event(event);
1484 [controller noteKey:keyCode pressed:pressed];
1487 - (void) postKeyEvent:(NSEvent *)theEvent
1489 [self flagsChanged:theEvent];
1490 [self postKey:[theEvent keyCode]
1491 pressed:[theEvent type] == NSKeyDown
1492 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1496 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1498 savedContentMinSize = minSize;
1499 savedContentMaxSize = maxSize;
1500 if (![self preventResizing])
1502 [self setContentMinSize:minSize];
1503 [self setContentMaxSize:maxSize];
1507 - (WineWindow*) ancestorWineWindow
1509 WineWindow* ancestor = self;
1512 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1513 if ([parent isKindOfClass:[WineWindow class]])
1521 - (void) postBroughtForwardEvent
1523 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1524 [queue postEvent:event];
1525 macdrv_release_event(event);
1528 - (void) updateForCursorClipping
1530 [self adjustFeaturesForState];
1535 * ---------- NSWindow method overrides ----------
1537 - (BOOL) canBecomeKeyWindow
1539 if (causing_becomeKeyWindow == self) return YES;
1540 if (self.disabled || self.noActivate) return NO;
1541 return [self isKeyWindow];
1544 - (BOOL) canBecomeMainWindow
1546 return [self canBecomeKeyWindow];
1549 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1551 // If a window is sized to completely cover a screen, then it's in
1552 // full-screen mode. In that case, we don't allow NSWindow to constrain
1554 NSArray* screens = [NSScreen screens];
1555 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1556 if (!screen_covered_by_rect(contentRect, screens) &&
1557 frame_intersects_screens(frameRect, screens))
1558 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1562 - (BOOL) isExcludedFromWindowsMenu
1564 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1567 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1569 BOOL ret = [super validateMenuItem:menuItem];
1571 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1572 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1573 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
1579 /* We don't call this. It's the action method of the items in the Window menu. */
1580 - (void) makeKeyAndOrderFront:(id)sender
1582 if ([self isMiniaturized])
1583 [self deminiaturize:nil];
1584 [self orderBelow:nil orAbove:nil activate:NO];
1585 [[self ancestorWineWindow] postBroughtForwardEvent];
1587 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1588 [[WineApplicationController sharedController] windowGotFocus:self];
1591 - (void) sendEvent:(NSEvent*)event
1593 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1594 interface control. For example, Control-Tab switches focus among
1595 views. We want to bypass that feature, so directly route key-down
1596 events to -keyDown:. */
1597 if ([event type] == NSKeyDown)
1598 [[self firstResponder] keyDown:event];
1600 [super sendEvent:event];
1603 - (void) miniaturize:(id)sender
1605 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1606 [queue postEvent:event];
1607 macdrv_release_event(event);
1610 - (void) toggleFullScreen:(id)sender
1612 if (!self.disabled && !maximized)
1613 [super toggleFullScreen:sender];
1616 - (NSArray*) childWineWindows
1618 NSArray* childWindows = self.childWindows;
1619 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
1620 return [child isKindOfClass:[WineWindow class]];
1622 return [childWindows objectsAtIndexes:indexes];
1625 // We normally use the generic/calibrated RGB color space for the window,
1626 // rather than the device color space, to avoid expensive color conversion
1627 // which slows down drawing. However, for windows displaying OpenGL, having
1628 // a different color space than the screen greatly reduces frame rates, often
1629 // limiting it to the display refresh rate.
1631 // To avoid this, we switch back to the screen color space whenever the
1632 // window is covered by a view with an attached OpenGL context.
1633 - (void) updateColorSpace
1635 NSRect contentRect = [[self contentView] frame];
1636 BOOL coveredByGLView = FALSE;
1637 for (WineContentView* view in [[self contentView] subviews])
1639 if ([view hasGLContext])
1641 NSRect frame = [view convertRect:[view bounds] toView:nil];
1642 if (NSContainsRect(frame, contentRect))
1644 coveredByGLView = TRUE;
1650 if (coveredByGLView)
1651 [self setColorSpace:nil];
1653 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1658 * ---------- NSResponder method overrides ----------
1660 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1662 - (void) flagsChanged:(NSEvent *)theEvent
1664 static const struct {
1668 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1669 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1670 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1671 { NX_DEVICELCTLKEYMASK, kVK_Control },
1672 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1673 { NX_DEVICELALTKEYMASK, kVK_Option },
1674 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1675 { NX_DEVICELCMDKEYMASK, kVK_Command },
1676 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1679 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1681 int i, last_changed;
1683 fix_device_modifiers_by_generic(&modifierFlags);
1684 changed = modifierFlags ^ lastModifierFlags;
1687 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1688 if (changed & modifiers[i].mask)
1691 for (i = 0; i <= last_changed; i++)
1693 if (changed & modifiers[i].mask)
1695 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1697 if (i == last_changed)
1698 lastModifierFlags = modifierFlags;
1701 lastModifierFlags ^= modifiers[i].mask;
1702 fix_generic_modifiers_by_device(&lastModifierFlags);
1705 // Caps lock generates one event for each press-release action.
1706 // We need to simulate a pair of events for each actual event.
1707 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1709 [self postKey:modifiers[i].keycode
1711 modifiers:lastModifierFlags
1712 event:(NSEvent*)theEvent];
1716 [self postKey:modifiers[i].keycode
1718 modifiers:lastModifierFlags
1719 event:(NSEvent*)theEvent];
1724 - (void) applicationWillHide
1726 savedVisibleState = [self isVisible];
1729 - (void) applicationDidUnhide
1731 if ([self isVisible])
1732 [self becameEligibleParentOrChild];
1737 * ---------- NSWindowDelegate methods ----------
1739 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1741 macdrv_query* query;
1744 query = macdrv_create_query();
1745 query->type = QUERY_MIN_MAX_INFO;
1746 query->window = (macdrv_window)[self retain];
1747 [self.queue query:query timeout:0.5];
1748 macdrv_release_query(query);
1750 size = [self contentMaxSize];
1751 if (proposedSize.width < size.width)
1752 size.width = proposedSize.width;
1753 if (proposedSize.height < size.height)
1754 size.height = proposedSize.height;
1758 - (void)windowDidBecomeKey:(NSNotification *)notification
1760 WineApplicationController* controller = [WineApplicationController sharedController];
1761 NSEvent* event = [controller lastFlagsChanged];
1763 [self flagsChanged:event];
1765 if (causing_becomeKeyWindow == self) return;
1767 [controller windowGotFocus:self];
1770 - (void)windowDidDeminiaturize:(NSNotification *)notification
1772 WineApplicationController* controller = [WineApplicationController sharedController];
1774 if (!ignore_windowDeminiaturize)
1775 [self postDidUnminimizeEvent];
1776 ignore_windowDeminiaturize = FALSE;
1778 [self becameEligibleParentOrChild];
1780 if (fullscreen && [self isOnActiveSpace])
1781 [controller updateFullscreenWindows];
1782 [controller adjustWindowLevels];
1784 if (![self parentWindow])
1785 [self postBroughtForwardEvent];
1787 if (!self.disabled && !self.noActivate)
1789 causing_becomeKeyWindow = self;
1790 [self makeKeyWindow];
1791 causing_becomeKeyWindow = nil;
1792 [controller windowGotFocus:self];
1795 [self windowDidResize:notification];
1798 - (void) windowDidEndLiveResize:(NSNotification *)notification
1800 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1801 [queue postEvent:event];
1802 macdrv_release_event(event);
1804 self.liveResizeDisplayTimer = nil;
1807 - (void) windowDidEnterFullScreen:(NSNotification*)notification
1809 enteringFullScreen = FALSE;
1810 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1813 - (void) windowDidExitFullScreen:(NSNotification*)notification
1815 exitingFullScreen = FALSE;
1816 [self setFrame:nonFullscreenFrame display:YES animate:NO];
1817 [self windowDidResize:nil];
1820 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1822 enteringFullScreen = FALSE;
1823 enteredFullScreenTime = 0;
1826 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1828 exitingFullScreen = FALSE;
1829 [self windowDidResize:nil];
1832 - (void)windowDidMiniaturize:(NSNotification *)notification
1834 if (fullscreen && [self isOnActiveSpace])
1835 [[WineApplicationController sharedController] updateFullscreenWindows];
1838 - (void)windowDidMove:(NSNotification *)notification
1840 [self windowDidResize:notification];
1843 - (void)windowDidResignKey:(NSNotification *)notification
1845 macdrv_event* event;
1847 if (causing_becomeKeyWindow) return;
1849 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1850 [queue postEvent:event];
1851 macdrv_release_event(event);
1854 - (void)windowDidResize:(NSNotification *)notification
1856 macdrv_event* event;
1857 NSRect frame = [self frame];
1859 if ([self inLiveResize])
1861 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1862 resizingFromLeft = TRUE;
1863 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1864 resizingFromTop = TRUE;
1867 frame = [self contentRectForFrameRect:frame];
1869 if (ignore_windowResize || exitingFullScreen) return;
1871 if ([self preventResizing])
1873 [self setContentMinSize:frame.size];
1874 [self setContentMaxSize:frame.size];
1877 [[WineApplicationController sharedController] flipRect:&frame];
1879 /* Coalesce events by discarding any previous ones still in the queue. */
1880 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1883 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1884 event->window_frame_changed.frame = NSRectToCGRect(frame);
1885 event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
1886 event->window_frame_changed.in_resize = [self inLiveResize];
1887 [queue postEvent:event];
1888 macdrv_release_event(event);
1890 [[[self contentView] inputContext] invalidateCharacterCoordinates];
1891 [self updateFullscreen];
1894 - (BOOL)windowShouldClose:(id)sender
1896 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1897 [queue postEvent:event];
1898 macdrv_release_event(event);
1902 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
1906 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1907 [queue postEvent:event];
1908 macdrv_release_event(event);
1911 else if (!resizable)
1913 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
1914 [queue postEvent:event];
1915 macdrv_release_event(event);
1922 - (void) windowWillClose:(NSNotification*)notification
1926 if (fakingClose) return;
1927 if (latentParentWindow)
1929 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
1930 self.latentParentWindow = nil;
1933 for (child in latentChildWindows)
1935 if (child.latentParentWindow == self)
1936 child.latentParentWindow = nil;
1938 [latentChildWindows removeAllObjects];
1941 - (void) windowWillEnterFullScreen:(NSNotification*)notification
1943 enteringFullScreen = TRUE;
1944 nonFullscreenFrame = [self frame];
1947 - (void) windowWillExitFullScreen:(NSNotification*)notification
1949 exitingFullScreen = TRUE;
1952 - (void)windowWillMiniaturize:(NSNotification *)notification
1954 [self becameIneligibleParentOrChild];
1957 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
1959 if ([self inLiveResize])
1962 macdrv_query* query;
1964 rect = [self frame];
1965 if (resizingFromLeft)
1966 rect.origin.x = NSMaxX(rect) - frameSize.width;
1967 if (!resizingFromTop)
1968 rect.origin.y = NSMaxY(rect) - frameSize.height;
1969 rect.size = frameSize;
1970 rect = [self contentRectForFrameRect:rect];
1971 [[WineApplicationController sharedController] flipRect:&rect];
1973 query = macdrv_create_query();
1974 query->type = QUERY_RESIZE_SIZE;
1975 query->window = (macdrv_window)[self retain];
1976 query->resize_size.rect = NSRectToCGRect(rect);
1977 query->resize_size.from_left = resizingFromLeft;
1978 query->resize_size.from_top = resizingFromTop;
1980 if ([self.queue query:query timeout:0.1])
1982 rect = NSRectFromCGRect(query->resize_size.rect);
1983 rect = [self frameRectForContentRect:rect];
1984 frameSize = rect.size;
1987 macdrv_release_query(query);
1993 - (void) windowWillStartLiveResize:(NSNotification *)notification
1995 macdrv_query* query = macdrv_create_query();
1996 query->type = QUERY_RESIZE_START;
1997 query->window = (macdrv_window)[self retain];
1999 [self.queue query:query timeout:0.3];
2000 macdrv_release_query(query);
2002 frameAtResizeStart = [self frame];
2003 resizingFromLeft = resizingFromTop = FALSE;
2005 // There's a strange restriction in window redrawing during Cocoa-
2006 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
2007 // that happen synchronously when Cocoa tells us that our window size
2008 // has changed or asynchronously in a short interval thereafter provoke
2009 // the window to redraw. Calls to those methods that happen asynchronously
2010 // a half second or more after the last change of the window size aren't
2011 // heeded until the next resize-related user event (e.g. mouse movement).
2013 // Wine often has a significant delay between when it's been told that
2014 // the window has changed size and when it can flush completed drawing.
2015 // So, our windows would get stuck with incomplete drawing for as long
2016 // as the user holds the mouse button down and doesn't move it.
2018 // We address this by "manually" asking our windows to check if they need
2019 // redrawing every so often (during live resize only).
2020 self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
2022 selector:@selector(displayIfNeeded)
2025 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
2026 forMode:NSRunLoopCommonModes];
2029 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2031 macdrv_query* query;
2032 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2035 query = macdrv_create_query();
2036 query->type = QUERY_MIN_MAX_INFO;
2037 query->window = (macdrv_window)[self retain];
2038 [self.queue query:query timeout:0.5];
2039 macdrv_release_query(query);
2041 currentContentRect = [self contentRectForFrameRect:[self frame]];
2042 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2044 maxSize = [self contentMaxSize];
2045 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2046 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2048 // Try to keep the top-left corner where it is.
2049 newContentRect.origin.x = NSMinX(currentContentRect);
2050 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2052 // If that pushes the bottom or right off the screen, pull it up and to the left.
2053 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2054 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2055 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2056 if (NSMinY(newContentRect) < NSMinY(screenRect))
2057 newContentRect.origin.y = NSMinY(screenRect);
2059 // If that pushes the top or left off the screen, push it down and the right
2060 // again. Do this last because the top-left corner is more important than the
2062 if (NSMinX(newContentRect) < NSMinX(screenRect))
2063 newContentRect.origin.x = NSMinX(screenRect);
2064 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2065 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2067 return [self frameRectForContentRect:newContentRect];
2072 * ---------- NSPasteboardOwner methods ----------
2074 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2076 macdrv_query* query = macdrv_create_query();
2077 query->type = QUERY_PASTEBOARD_DATA;
2078 query->window = (macdrv_window)[self retain];
2079 query->pasteboard_data.type = (CFStringRef)[type copy];
2081 [self.queue query:query timeout:3];
2082 macdrv_release_query(query);
2087 * ---------- NSDraggingDestination methods ----------
2089 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2091 return [self draggingUpdated:sender];
2094 - (void) draggingExited:(id <NSDraggingInfo>)sender
2096 // This isn't really a query. We don't need any response. However, it
2097 // has to be processed in a similar manner as the other drag-and-drop
2098 // queries in order to maintain the proper order of operations.
2099 macdrv_query* query = macdrv_create_query();
2100 query->type = QUERY_DRAG_EXITED;
2101 query->window = (macdrv_window)[self retain];
2103 [self.queue query:query timeout:0.1];
2104 macdrv_release_query(query);
2107 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2109 NSDragOperation ret;
2110 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2111 NSPasteboard* pb = [sender draggingPasteboard];
2113 macdrv_query* query = macdrv_create_query();
2114 query->type = QUERY_DRAG_OPERATION;
2115 query->window = (macdrv_window)[self retain];
2116 query->drag_operation.x = pt.x;
2117 query->drag_operation.y = pt.y;
2118 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2119 query->drag_operation.accepted_op = NSDragOperationNone;
2120 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2122 [self.queue query:query timeout:3];
2123 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2124 macdrv_release_query(query);
2129 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2132 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2133 NSPasteboard* pb = [sender draggingPasteboard];
2135 macdrv_query* query = macdrv_create_query();
2136 query->type = QUERY_DRAG_DROP;
2137 query->window = (macdrv_window)[self retain];
2138 query->drag_drop.x = pt.x;
2139 query->drag_drop.y = pt.y;
2140 query->drag_drop.op = [sender draggingSourceOperationMask];
2141 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2143 [self.queue query:query timeout:3 * 60 processEvents:YES];
2144 ret = query->status;
2145 macdrv_release_query(query);
2150 - (BOOL) wantsPeriodicDraggingUpdates
2158 /***********************************************************************
2159 * macdrv_create_cocoa_window
2161 * Create a Cocoa window with the given content frame and features (e.g.
2162 * title bar, close box, etc.).
2164 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2165 CGRect frame, void* hwnd, macdrv_event_queue queue)
2167 __block WineWindow* window;
2170 window = [[WineWindow createWindowWithFeatures:wf
2171 windowFrame:NSRectFromCGRect(frame)
2173 queue:(WineEventQueue*)queue] retain];
2176 return (macdrv_window)window;
2179 /***********************************************************************
2180 * macdrv_destroy_cocoa_window
2182 * Destroy a Cocoa window.
2184 void macdrv_destroy_cocoa_window(macdrv_window w)
2186 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2187 WineWindow* window = (WineWindow*)w;
2190 [window doOrderOut];
2193 [window.queue discardEventsMatchingMask:-1 forWindow:window];
2199 /***********************************************************************
2200 * macdrv_get_window_hwnd
2202 * Get the hwnd that was set for the window at creation.
2204 void* macdrv_get_window_hwnd(macdrv_window w)
2206 WineWindow* window = (WineWindow*)w;
2210 /***********************************************************************
2211 * macdrv_set_cocoa_window_features
2213 * Update a Cocoa window's features.
2215 void macdrv_set_cocoa_window_features(macdrv_window w,
2216 const struct macdrv_window_features* wf)
2218 WineWindow* window = (WineWindow*)w;
2221 [window setWindowFeatures:wf];
2225 /***********************************************************************
2226 * macdrv_set_cocoa_window_state
2228 * Update a Cocoa window's state.
2230 void macdrv_set_cocoa_window_state(macdrv_window w,
2231 const struct macdrv_window_state* state)
2233 WineWindow* window = (WineWindow*)w;
2236 [window setMacDrvState:state];
2240 /***********************************************************************
2241 * macdrv_set_cocoa_window_title
2243 * Set a Cocoa window's title.
2245 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2248 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2249 WineWindow* window = (WineWindow*)w;
2250 NSString* titleString;
2253 titleString = [NSString stringWithCharacters:title length:length];
2256 OnMainThreadAsync(^{
2257 [window setTitle:titleString];
2258 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2259 [NSApp changeWindowsItem:window title:titleString filename:NO];
2265 /***********************************************************************
2266 * macdrv_order_cocoa_window
2268 * Reorder a Cocoa window relative to other windows. If prev is
2269 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
2270 * it is ordered above that window. Otherwise, it is ordered to the
2273 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2274 macdrv_window n, int activate)
2276 WineWindow* window = (WineWindow*)w;
2277 WineWindow* prev = (WineWindow*)p;
2278 WineWindow* next = (WineWindow*)n;
2280 OnMainThreadAsync(^{
2281 [window orderBelow:prev
2285 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2287 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2291 /***********************************************************************
2292 * macdrv_hide_cocoa_window
2294 * Hides a Cocoa window.
2296 void macdrv_hide_cocoa_window(macdrv_window w)
2298 WineWindow* window = (WineWindow*)w;
2301 [window doOrderOut];
2305 /***********************************************************************
2306 * macdrv_set_cocoa_window_frame
2308 * Move a Cocoa window.
2310 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2312 WineWindow* window = (WineWindow*)w;
2315 [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2319 /***********************************************************************
2320 * macdrv_get_cocoa_window_frame
2322 * Gets the frame of a Cocoa window.
2324 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2326 WineWindow* window = (WineWindow*)w;
2331 frame = [window contentRectForFrameRect:[window frame]];
2332 [[WineApplicationController sharedController] flipRect:&frame];
2333 *out_frame = NSRectToCGRect(frame);
2337 /***********************************************************************
2338 * macdrv_set_cocoa_parent_window
2340 * Sets the parent window for a Cocoa window. If parent is NULL, clears
2341 * the parent window.
2343 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2345 WineWindow* window = (WineWindow*)w;
2348 [window setMacDrvParentWindow:(WineWindow*)parent];
2352 /***********************************************************************
2353 * macdrv_set_window_surface
2355 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2357 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2358 WineWindow* window = (WineWindow*)w;
2361 window.surface = surface;
2362 window.surface_mutex = mutex;
2368 /***********************************************************************
2369 * macdrv_window_needs_display
2371 * Mark a window as needing display in a specified rect (in non-client
2372 * area coordinates).
2374 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2376 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2377 WineWindow* window = (WineWindow*)w;
2379 OnMainThreadAsync(^{
2380 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2386 /***********************************************************************
2387 * macdrv_set_window_shape
2389 * Sets the shape of a Cocoa window from an array of rectangles. If
2390 * rects is NULL, resets the window's shape to its frame.
2392 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2394 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2395 WineWindow* window = (WineWindow*)w;
2398 if (!rects || !count)
2401 window.shapeData = nil;
2405 size_t length = sizeof(*rects) * count;
2406 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
2411 path = [NSBezierPath bezierPath];
2412 for (i = 0; i < count; i++)
2413 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2414 window.shape = path;
2415 window.shapeData = [NSData dataWithBytes:rects length:length];
2423 /***********************************************************************
2424 * macdrv_set_window_alpha
2426 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2428 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2429 WineWindow* window = (WineWindow*)w;
2431 [window setAlphaValue:alpha];
2436 /***********************************************************************
2437 * macdrv_set_window_color_key
2439 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2442 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2443 WineWindow* window = (WineWindow*)w;
2446 window.colorKeyed = TRUE;
2447 window.colorKeyRed = keyRed;
2448 window.colorKeyGreen = keyGreen;
2449 window.colorKeyBlue = keyBlue;
2450 [window checkTransparency];
2456 /***********************************************************************
2457 * macdrv_clear_window_color_key
2459 void macdrv_clear_window_color_key(macdrv_window w)
2461 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2462 WineWindow* window = (WineWindow*)w;
2465 window.colorKeyed = FALSE;
2466 [window checkTransparency];
2472 /***********************************************************************
2473 * macdrv_window_use_per_pixel_alpha
2475 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2477 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2478 WineWindow* window = (WineWindow*)w;
2481 window.usePerPixelAlpha = use_per_pixel_alpha;
2482 [window checkTransparency];
2488 /***********************************************************************
2489 * macdrv_give_cocoa_window_focus
2491 * Makes the Cocoa window "key" (gives it keyboard focus). This also
2492 * orders it front and, if its frame was not within the desktop bounds,
2493 * Cocoa will typically move it on-screen.
2495 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2497 WineWindow* window = (WineWindow*)w;
2500 [window makeFocused:activate];
2504 /***********************************************************************
2505 * macdrv_set_window_min_max_sizes
2507 * Sets the window's minimum and maximum content sizes.
2509 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2511 WineWindow* window = (WineWindow*)w;
2514 [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2518 /***********************************************************************
2519 * macdrv_create_view
2521 * Creates and returns a view in the specified rect of the window. The
2522 * caller is responsible for calling macdrv_dispose_view() on the view
2523 * when it is done with it.
2525 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2527 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2528 WineWindow* window = (WineWindow*)w;
2529 __block WineContentView* view;
2531 if (CGRectIsNull(rect)) rect = CGRectZero;
2534 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2536 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2537 [view setAutoresizesSubviews:NO];
2538 [nc addObserver:view
2539 selector:@selector(updateGLContexts)
2540 name:NSViewGlobalFrameDidChangeNotification
2542 [nc addObserver:view
2543 selector:@selector(updateGLContexts)
2544 name:NSApplicationDidChangeScreenParametersNotification
2546 [[window contentView] addSubview:view];
2547 [window updateColorSpace];
2551 return (macdrv_view)view;
2554 /***********************************************************************
2555 * macdrv_dispose_view
2557 * Destroys a view previously returned by macdrv_create_view.
2559 void macdrv_dispose_view(macdrv_view v)
2561 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2562 WineContentView* view = (WineContentView*)v;
2565 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2566 WineWindow* window = (WineWindow*)[view window];
2568 [nc removeObserver:view
2569 name:NSViewGlobalFrameDidChangeNotification
2571 [nc removeObserver:view
2572 name:NSApplicationDidChangeScreenParametersNotification
2574 [view removeFromSuperview];
2576 [window updateColorSpace];
2582 /***********************************************************************
2583 * macdrv_set_view_window_and_frame
2585 * Move a view to a new window and/or position within its window. If w
2586 * is NULL, leave the view in its current window and just change its
2589 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2591 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2592 WineContentView* view = (WineContentView*)v;
2593 WineWindow* window = (WineWindow*)w;
2595 if (CGRectIsNull(rect)) rect = CGRectZero;
2598 BOOL changedWindow = (window && window != [view window]);
2599 NSRect newFrame = NSRectFromCGRect(rect);
2600 NSRect oldFrame = [view frame];
2601 BOOL needUpdateWindowColorSpace = FALSE;
2605 WineWindow* oldWindow = (WineWindow*)[view window];
2606 [view removeFromSuperview];
2607 [oldWindow updateColorSpace];
2608 [[window contentView] addSubview:view];
2609 needUpdateWindowColorSpace = TRUE;
2612 if (!NSEqualRects(oldFrame, newFrame))
2615 [[view superview] setNeedsDisplayInRect:oldFrame];
2616 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2617 [view setFrameSize:newFrame.size];
2618 else if (NSEqualSizes(oldFrame.size, newFrame.size))
2619 [view setFrameOrigin:newFrame.origin];
2621 [view setFrame:newFrame];
2622 [view setNeedsDisplay:YES];
2623 needUpdateWindowColorSpace = TRUE;
2626 if (needUpdateWindowColorSpace)
2627 [(WineWindow*)[view window] updateColorSpace];
2633 /***********************************************************************
2634 * macdrv_add_view_opengl_context
2636 * Add an OpenGL context to the list being tracked for each view.
2638 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2640 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2641 WineContentView* view = (WineContentView*)v;
2642 WineOpenGLContext *context = (WineOpenGLContext*)c;
2645 [view addGLContext:context];
2651 /***********************************************************************
2652 * macdrv_remove_view_opengl_context
2654 * Add an OpenGL context to the list being tracked for each view.
2656 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2658 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2659 WineContentView* view = (WineContentView*)v;
2660 WineOpenGLContext *context = (WineOpenGLContext*)c;
2662 OnMainThreadAsync(^{
2663 [view removeGLContext:context];
2669 /***********************************************************************
2670 * macdrv_window_background_color
2672 * Returns the standard Mac window background color as a 32-bit value of
2673 * the form 0x00rrggbb.
2675 uint32_t macdrv_window_background_color(void)
2677 static uint32_t result;
2678 static dispatch_once_t once;
2680 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2681 // color spaces (RGB or grayscale). So, the only way to get RGB values out
2682 // of it is to draw with it.
2683 dispatch_once(&once, ^{
2685 unsigned char rgbx[4];
2686 unsigned char *planes = rgbx;
2687 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2694 colorSpaceName:NSCalibratedRGBColorSpace
2698 [NSGraphicsContext saveGraphicsState];
2699 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2700 [[NSColor windowBackgroundColor] set];
2701 NSRectFill(NSMakeRect(0, 0, 1, 1));
2702 [NSGraphicsContext restoreGraphicsState];
2704 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2711 /***********************************************************************
2712 * macdrv_send_text_input_event
2714 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2719 WineWindow* window = (WineWindow*)[NSApp keyWindow];
2720 if (![window isKindOfClass:[WineWindow class]])
2722 window = (WineWindow*)[NSApp mainWindow];
2723 if (![window isKindOfClass:[WineWindow class]])
2724 window = [[WineApplicationController sharedController] frontWineWindow];
2729 NSUInteger localFlags = flags;
2733 window.imeData = data;
2734 fix_device_modifiers_by_generic(&localFlags);
2736 // An NSEvent created with +keyEventWithType:... is internally marked
2737 // as synthetic and doesn't get sent through input methods. But one
2738 // created from a CGEvent doesn't have that problem.
2739 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2740 CGEventSetFlags(c, localFlags);
2741 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2742 event = [NSEvent eventWithCGEvent:c];
2745 window.commandDone = FALSE;
2746 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;