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 || 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) sendResizeStartQuery
819 macdrv_query* query = macdrv_create_query();
820 query->type = QUERY_RESIZE_START;
821 query->window = (macdrv_window)[self retain];
823 [self.queue query:query timeout:0.3];
824 macdrv_release_query(query);
827 - (void) setMacDrvState:(const struct macdrv_window_state*)state
829 NSWindowCollectionBehavior behavior;
831 self.disabled = state->disabled;
832 self.noActivate = state->no_activate;
834 if (self.floating != state->floating)
836 self.floating = state->floating;
839 // Became floating. If child of non-floating window, make that
840 // relationship latent.
841 WineWindow* parent = (WineWindow*)[self parentWindow];
842 if (parent && !parent.floating)
843 [self becameIneligibleChild];
847 // Became non-floating. If parent of floating children, make that
848 // relationship latent.
850 for (child in [self childWineWindows])
853 [child becameIneligibleChild];
857 // Check our latent relationships. If floating status was the only
858 // reason they were latent, then make them active.
859 if ([self isVisible])
860 [self becameEligibleParentOrChild];
862 [[WineApplicationController sharedController] adjustWindowLevels];
865 if (state->minimized_valid)
867 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
869 pendingMinimize = FALSE;
870 if (state->minimized && ![self isMiniaturized])
872 if ([self wouldBeVisible])
874 if ([self styleMask] & NSFullScreenWindowMask)
876 [self postDidUnminimizeEvent];
877 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
881 [super miniaturize:nil];
882 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
883 event_mask_for_type(WINDOW_GOT_FOCUS) |
884 event_mask_for_type(WINDOW_LOST_FOCUS);
888 pendingMinimize = TRUE;
890 else if (!state->minimized && [self isMiniaturized])
892 ignore_windowDeminiaturize = TRUE;
893 [self deminiaturize:nil];
894 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
898 [queue discardEventsMatchingMask:discard forWindow:self];
901 if (state->maximized != maximized)
903 maximized = state->maximized;
904 [self adjustFeaturesForState];
906 if (!maximized && [self inLiveResize])
907 [self sendResizeStartQuery];
910 behavior = NSWindowCollectionBehaviorDefault;
911 if (state->excluded_by_expose)
912 behavior |= NSWindowCollectionBehaviorTransient;
914 behavior |= NSWindowCollectionBehaviorManaged;
915 if (state->excluded_by_cycle)
917 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
918 if ([self isOrderedIn])
919 [NSApp removeWindowsItem:self];
923 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
924 if ([self isOrderedIn])
925 [NSApp addWindowsItem:self title:[self title] filename:NO];
927 [self adjustFullScreenBehavior:behavior];
930 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
932 BOOL reordered = FALSE;
934 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
936 if ([self level] > [child level])
937 [child setLevel:[self level]];
938 [self addChildWindow:child ordered:NSWindowAbove];
939 [latentChildWindows removeObjectIdenticalTo:child];
940 child.latentParentWindow = nil;
945 if (!latentChildWindows)
946 latentChildWindows = [[NSMutableArray alloc] init];
947 if (![latentChildWindows containsObject:child])
948 [latentChildWindows addObject:child];
949 child.latentParentWindow = self;
955 - (BOOL) addChildWineWindow:(WineWindow*)child
957 return [self addChildWineWindow:child assumeVisible:FALSE];
960 - (void) removeChildWineWindow:(WineWindow*)child
962 [self removeChildWindow:child];
963 if (child.latentParentWindow == self)
964 child.latentParentWindow = nil;
965 [latentChildWindows removeObjectIdenticalTo:child];
968 - (BOOL) becameEligibleParentOrChild
970 BOOL reordered = FALSE;
973 if (latentParentWindow.floating || !self.floating)
975 // If we aren't visible currently, we assume that we should be and soon
976 // will be. So, if the latent parent is visible that's enough to assume
977 // we can establish the parent-child relationship in Cocoa. That will
978 // actually make us visible, which is fine.
979 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
983 // Here, though, we may not actually be visible yet and adding a child
984 // won't make us visible. The caller will have to call this method
985 // again after actually making us visible.
986 if ([self isVisible] && (count = [latentChildWindows count]))
988 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
991 for (i = 0; i < count; i++)
993 WineWindow* child = [latentChildWindows objectAtIndex:i];
994 if ([child isVisible] && (self.floating || !child.floating))
996 if (child.latentParentWindow == self)
998 if ([self level] > [child level])
999 [child setLevel:[self level]];
1000 [self addChildWindow:child ordered:NSWindowAbove];
1001 child.latentParentWindow = nil;
1005 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1006 [indexesToRemove addIndex:i];
1010 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1016 - (void) becameIneligibleChild
1018 WineWindow* parent = (WineWindow*)[self parentWindow];
1021 if (!parent->latentChildWindows)
1022 parent->latentChildWindows = [[NSMutableArray alloc] init];
1023 [parent->latentChildWindows insertObject:self atIndex:0];
1024 self.latentParentWindow = parent;
1025 [parent removeChildWindow:self];
1029 - (void) becameIneligibleParentOrChild
1031 NSArray* childWindows = [self childWineWindows];
1033 [self becameIneligibleChild];
1035 if ([childWindows count])
1039 for (child in childWindows)
1041 child.latentParentWindow = self;
1042 [self removeChildWindow:child];
1045 if (latentChildWindows)
1046 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1048 latentChildWindows = [childWindows mutableCopy];
1052 // Determine if, among Wine windows, this window is directly above or below
1053 // a given other Wine window with no other Wine window intervening.
1054 // Intervening non-Wine windows are ignored.
1055 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1057 NSNumber* windowNumber;
1058 NSNumber* otherWindowNumber;
1059 NSArray* windowNumbers;
1060 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1062 if (![self isVisible] || ![otherWindow isVisible])
1065 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1066 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1067 windowNumbers = [[self class] windowNumbersWithOptions:0];
1068 windowIndex = [windowNumbers indexOfObject:windowNumber];
1069 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1071 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1074 if (orderingMode == NSWindowAbove)
1076 lowIndex = windowIndex;
1077 highIndex = otherWindowIndex;
1079 else if (orderingMode == NSWindowBelow)
1081 lowIndex = otherWindowIndex;
1082 highIndex = windowIndex;
1087 if (highIndex <= lowIndex)
1090 for (i = lowIndex + 1; i < highIndex; i++)
1092 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1093 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1094 if ([interveningWindow isKindOfClass:[WineWindow class]])
1101 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1103 NSMutableArray* windowNumbers;
1104 NSNumber* childWindowNumber;
1105 NSUInteger otherIndex, limit;
1106 NSArray* origChildren;
1107 NSMutableArray* children;
1109 // Get the z-order from the window server and modify it to reflect the
1110 // requested window ordering.
1111 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1112 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1113 [windowNumbers removeObject:childWindowNumber];
1114 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1115 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1117 // Get our child windows and sort them in the reverse of the desired
1118 // z-order (back-to-front).
1119 origChildren = [self childWineWindows];
1120 children = [[origChildren mutableCopy] autorelease];
1121 [children sortWithOptions:NSSortStable
1122 usingComparator:^NSComparisonResult(id obj1, id obj2){
1123 NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1124 NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1125 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1126 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1127 if (index1 == NSNotFound)
1129 if (index2 == NSNotFound)
1130 return NSOrderedSame;
1132 return NSOrderedAscending;
1134 else if (index2 == NSNotFound)
1135 return NSOrderedDescending;
1136 else if (index1 < index2)
1137 return NSOrderedDescending;
1138 else if (index2 < index1)
1139 return NSOrderedAscending;
1141 return NSOrderedSame;
1144 // If the current and desired children arrays match up to a point, leave
1145 // those matching children alone.
1146 limit = MIN([origChildren count], [children count]);
1147 for (otherIndex = 0; otherIndex < limit; otherIndex++)
1149 if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1152 [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1154 // Remove all of the child windows and re-add them back-to-front so they
1155 // are in the desired order.
1156 for (other in children)
1157 [self removeChildWindow:other];
1158 for (other in children)
1159 [self addChildWindow:other ordered:NSWindowAbove];
1162 /* Returns whether or not the window was ordered in, which depends on if
1163 its frame intersects any screen. */
1164 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1166 WineApplicationController* controller = [WineApplicationController sharedController];
1167 if (![self isMiniaturized])
1169 BOOL needAdjustWindowLevels = FALSE;
1172 [controller transformProcessToForeground];
1174 wasVisible = [self isVisible];
1177 [NSApp activateIgnoringOtherApps:YES];
1179 NSDisableScreenUpdates();
1181 if ([self becameEligibleParentOrChild])
1182 needAdjustWindowLevels = TRUE;
1186 WineWindow* other = [prev isVisible] ? prev : next;
1187 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1189 if (![self isOrdered:orderingMode relativeTo:other])
1191 WineWindow* parent = (WineWindow*)[self parentWindow];
1192 WineWindow* otherParent = (WineWindow*)[other parentWindow];
1194 // This window level may not be right for this window based
1195 // on floating-ness, fullscreen-ness, etc. But we set it
1196 // temporarily to allow us to order the windows properly.
1197 // Then the levels get fixed by -adjustWindowLevels.
1198 if ([self level] != [other level])
1199 [self setLevel:[other level]];
1200 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1202 // The above call to -[NSWindow orderWindow:relativeTo:] won't
1203 // reorder windows which are both children of the same parent
1204 // relative to each other, so do that separately.
1205 if (parent && parent == otherParent)
1206 [parent order:orderingMode childWindow:self relativeTo:other];
1208 needAdjustWindowLevels = TRUE;
1213 // Again, temporarily set level to make sure we can order to
1215 next = [controller frontWineWindow];
1216 if (next && [self level] < [next level])
1217 [self setLevel:[next level]];
1218 [self orderFront:nil];
1219 needAdjustWindowLevels = TRUE;
1222 if ([self becameEligibleParentOrChild])
1223 needAdjustWindowLevels = TRUE;
1225 if (needAdjustWindowLevels)
1227 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1228 [controller updateFullscreenWindows];
1229 [controller adjustWindowLevels];
1232 if (pendingMinimize)
1234 [super miniaturize:nil];
1235 pendingMinimize = FALSE;
1238 NSEnableScreenUpdates();
1240 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1241 Generate a frame-changed event just in case. The back end will ignore
1242 it if nothing actually changed. */
1243 [self windowDidResize:nil];
1245 if (![self isExcludedFromWindowsMenu])
1246 [NSApp addWindowsItem:self title:[self title] filename:NO];
1252 WineApplicationController* controller = [WineApplicationController sharedController];
1253 BOOL wasVisible = [self isVisible];
1254 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1256 if ([self isMiniaturized])
1257 pendingMinimize = TRUE;
1259 [self becameIneligibleParentOrChild];
1260 if ([self isMiniaturized])
1264 fakingClose = FALSE;
1267 [self orderOut:nil];
1268 savedVisibleState = FALSE;
1269 if (wasVisible && wasOnActiveSpace && fullscreen)
1270 [controller updateFullscreenWindows];
1271 [controller adjustWindowLevels];
1272 [NSApp removeWindowsItem:self];
1274 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1275 event_mask_for_type(WINDOW_GOT_FOCUS) |
1276 event_mask_for_type(WINDOW_LOST_FOCUS) |
1277 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1278 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1279 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1283 - (void) updateFullscreen
1285 NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1286 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1288 if (nowFullscreen != fullscreen)
1290 WineApplicationController* controller = [WineApplicationController sharedController];
1292 fullscreen = nowFullscreen;
1293 if ([self isVisible] && [self isOnActiveSpace])
1294 [controller updateFullscreenWindows];
1296 [controller adjustWindowLevels];
1300 - (void) setFrameFromWine:(NSRect)contentRect
1302 /* Origin is (left, top) in a top-down space. Need to convert it to
1303 (left, bottom) in a bottom-up space. */
1304 [[WineApplicationController sharedController] flipRect:&contentRect];
1306 /* The back end is establishing a new window size and position. It's
1307 not interested in any stale events regarding those that may be sitting
1309 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1312 if (!NSIsEmptyRect(contentRect))
1314 NSRect frame, oldFrame;
1316 oldFrame = [self frame];
1317 frame = [self frameRectForContentRect:contentRect];
1318 if (!NSEqualRects(frame, oldFrame))
1320 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1321 BOOL needEnableScreenUpdates = FALSE;
1323 if ([self preventResizing])
1325 // Allow the following calls to -setFrame:display: to work even
1326 // if they would violate the content size constraints. This
1327 // shouldn't be necessary since the content size constraints are
1328 // documented to not constrain that method, but it seems to be.
1329 [self setContentMinSize:NSZeroSize];
1330 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1333 if (equalSizes && [[self childWineWindows] count])
1335 // If we change the window frame such that the origin moves
1336 // but the size doesn't change, then Cocoa moves child
1337 // windows with the parent. We don't want that so we fake
1338 // a change of the size and then change it back.
1339 NSRect bogusFrame = frame;
1340 bogusFrame.size.width++;
1342 NSDisableScreenUpdates();
1343 needEnableScreenUpdates = TRUE;
1345 ignore_windowResize = TRUE;
1346 [self setFrame:bogusFrame display:NO];
1347 ignore_windowResize = FALSE;
1350 [self setFrame:frame display:YES];
1351 if ([self preventResizing])
1353 [self setContentMinSize:contentRect.size];
1354 [self setContentMaxSize:contentRect.size];
1357 if (needEnableScreenUpdates)
1358 NSEnableScreenUpdates();
1361 [self updateColorSpace];
1363 if (!enteringFullScreen &&
1364 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1365 nonFullscreenFrame = frame;
1367 [self updateFullscreen];
1369 if ([self isOrderedIn])
1371 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1372 event. The back end will ignore it if nothing actually changed. */
1373 [self windowDidResize:nil];
1379 - (void) setMacDrvParentWindow:(WineWindow*)parent
1381 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1382 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1384 [oldParent removeChildWineWindow:self];
1385 [latentParentWindow removeChildWineWindow:self];
1386 if ([parent addChildWineWindow:self])
1387 [[WineApplicationController sharedController] adjustWindowLevels];
1391 - (void) setDisabled:(BOOL)newValue
1393 if (disabled != newValue)
1395 disabled = newValue;
1396 [self adjustFeaturesForState];
1400 - (BOOL) needsTransparency
1402 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1405 - (void) checkTransparency
1407 if (![self isOpaque] && !self.needsTransparency)
1409 self.shapeChangedSinceLastDraw = TRUE;
1410 [[self contentView] setNeedsDisplay:YES];
1411 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1412 [self setOpaque:YES];
1414 else if ([self isOpaque] && self.needsTransparency)
1416 self.shapeChangedSinceLastDraw = TRUE;
1417 [[self contentView] setNeedsDisplay:YES];
1418 [self setBackgroundColor:[NSColor clearColor]];
1419 [self setOpaque:NO];
1423 - (void) setShape:(NSBezierPath*)newShape
1425 if (shape == newShape) return;
1429 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1433 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1435 shape = [newShape copy];
1436 self.shapeChangedSinceLastDraw = TRUE;
1438 [self checkTransparency];
1441 - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1443 if (newTimer != liveResizeDisplayTimer)
1445 [liveResizeDisplayTimer invalidate];
1446 [liveResizeDisplayTimer release];
1447 liveResizeDisplayTimer = [newTimer retain];
1451 - (void) makeFocused:(BOOL)activate
1455 [[WineApplicationController sharedController] transformProcessToForeground];
1456 [NSApp activateIgnoringOtherApps:YES];
1459 causing_becomeKeyWindow = self;
1460 [self makeKeyWindow];
1461 causing_becomeKeyWindow = nil;
1463 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1464 event_mask_for_type(WINDOW_LOST_FOCUS)
1468 - (void) postKey:(uint16_t)keyCode
1469 pressed:(BOOL)pressed
1470 modifiers:(NSUInteger)modifiers
1471 event:(NSEvent*)theEvent
1473 macdrv_event* event;
1475 WineApplicationController* controller = [WineApplicationController sharedController];
1477 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1478 event->key.keycode = keyCode;
1479 event->key.modifiers = modifiers;
1480 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1482 if ((cgevent = [theEvent CGEvent]))
1484 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1485 kCGKeyboardEventKeyboardType);
1486 if (keyboardType != controller.keyboardType)
1488 controller.keyboardType = keyboardType;
1489 [controller keyboardSelectionDidChange];
1493 [queue postEvent:event];
1495 macdrv_release_event(event);
1497 [controller noteKey:keyCode pressed:pressed];
1500 - (void) postKeyEvent:(NSEvent *)theEvent
1502 [self flagsChanged:theEvent];
1503 [self postKey:[theEvent keyCode]
1504 pressed:[theEvent type] == NSKeyDown
1505 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1509 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1511 savedContentMinSize = minSize;
1512 savedContentMaxSize = maxSize;
1513 if (![self preventResizing])
1515 [self setContentMinSize:minSize];
1516 [self setContentMaxSize:maxSize];
1520 - (WineWindow*) ancestorWineWindow
1522 WineWindow* ancestor = self;
1525 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1526 if ([parent isKindOfClass:[WineWindow class]])
1534 - (void) postBroughtForwardEvent
1536 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1537 [queue postEvent:event];
1538 macdrv_release_event(event);
1541 - (void) updateForCursorClipping
1543 [self adjustFeaturesForState];
1548 * ---------- NSWindow method overrides ----------
1550 - (BOOL) canBecomeKeyWindow
1552 if (causing_becomeKeyWindow == self) return YES;
1553 if (self.disabled || self.noActivate) return NO;
1554 return [self isKeyWindow];
1557 - (BOOL) canBecomeMainWindow
1559 return [self canBecomeKeyWindow];
1562 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1564 // If a window is sized to completely cover a screen, then it's in
1565 // full-screen mode. In that case, we don't allow NSWindow to constrain
1567 NSArray* screens = [NSScreen screens];
1568 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1569 if (!screen_covered_by_rect(contentRect, screens) &&
1570 frame_intersects_screens(frameRect, screens))
1571 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1575 - (BOOL) isExcludedFromWindowsMenu
1577 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1580 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1582 BOOL ret = [super validateMenuItem:menuItem];
1584 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1585 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1586 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
1592 /* We don't call this. It's the action method of the items in the Window menu. */
1593 - (void) makeKeyAndOrderFront:(id)sender
1595 if ([self isMiniaturized])
1596 [self deminiaturize:nil];
1597 [self orderBelow:nil orAbove:nil activate:NO];
1598 [[self ancestorWineWindow] postBroughtForwardEvent];
1600 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1601 [[WineApplicationController sharedController] windowGotFocus:self];
1604 - (void) sendEvent:(NSEvent*)event
1606 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1607 interface control. For example, Control-Tab switches focus among
1608 views. We want to bypass that feature, so directly route key-down
1609 events to -keyDown:. */
1610 if ([event type] == NSKeyDown)
1611 [[self firstResponder] keyDown:event];
1613 [super sendEvent:event];
1616 - (void) miniaturize:(id)sender
1618 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1619 [queue postEvent:event];
1620 macdrv_release_event(event);
1623 - (void) toggleFullScreen:(id)sender
1625 if (!self.disabled && !maximized)
1626 [super toggleFullScreen:sender];
1629 - (NSArray*) childWineWindows
1631 NSArray* childWindows = self.childWindows;
1632 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
1633 return [child isKindOfClass:[WineWindow class]];
1635 return [childWindows objectsAtIndexes:indexes];
1638 // We normally use the generic/calibrated RGB color space for the window,
1639 // rather than the device color space, to avoid expensive color conversion
1640 // which slows down drawing. However, for windows displaying OpenGL, having
1641 // a different color space than the screen greatly reduces frame rates, often
1642 // limiting it to the display refresh rate.
1644 // To avoid this, we switch back to the screen color space whenever the
1645 // window is covered by a view with an attached OpenGL context.
1646 - (void) updateColorSpace
1648 NSRect contentRect = [[self contentView] frame];
1649 BOOL coveredByGLView = FALSE;
1650 for (WineContentView* view in [[self contentView] subviews])
1652 if ([view hasGLContext])
1654 NSRect frame = [view convertRect:[view bounds] toView:nil];
1655 if (NSContainsRect(frame, contentRect))
1657 coveredByGLView = TRUE;
1663 if (coveredByGLView)
1664 [self setColorSpace:nil];
1666 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1671 * ---------- NSResponder method overrides ----------
1673 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1675 - (void) flagsChanged:(NSEvent *)theEvent
1677 static const struct {
1681 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1682 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1683 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1684 { NX_DEVICELCTLKEYMASK, kVK_Control },
1685 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1686 { NX_DEVICELALTKEYMASK, kVK_Option },
1687 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1688 { NX_DEVICELCMDKEYMASK, kVK_Command },
1689 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1692 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1694 int i, last_changed;
1696 fix_device_modifiers_by_generic(&modifierFlags);
1697 changed = modifierFlags ^ lastModifierFlags;
1700 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1701 if (changed & modifiers[i].mask)
1704 for (i = 0; i <= last_changed; i++)
1706 if (changed & modifiers[i].mask)
1708 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1710 if (i == last_changed)
1711 lastModifierFlags = modifierFlags;
1714 lastModifierFlags ^= modifiers[i].mask;
1715 fix_generic_modifiers_by_device(&lastModifierFlags);
1718 // Caps lock generates one event for each press-release action.
1719 // We need to simulate a pair of events for each actual event.
1720 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1722 [self postKey:modifiers[i].keycode
1724 modifiers:lastModifierFlags
1725 event:(NSEvent*)theEvent];
1729 [self postKey:modifiers[i].keycode
1731 modifiers:lastModifierFlags
1732 event:(NSEvent*)theEvent];
1737 - (void) applicationWillHide
1739 savedVisibleState = [self isVisible];
1742 - (void) applicationDidUnhide
1744 if ([self isVisible])
1745 [self becameEligibleParentOrChild];
1750 * ---------- NSWindowDelegate methods ----------
1752 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1754 macdrv_query* query;
1757 query = macdrv_create_query();
1758 query->type = QUERY_MIN_MAX_INFO;
1759 query->window = (macdrv_window)[self retain];
1760 [self.queue query:query timeout:0.5];
1761 macdrv_release_query(query);
1763 size = [self contentMaxSize];
1764 if (proposedSize.width < size.width)
1765 size.width = proposedSize.width;
1766 if (proposedSize.height < size.height)
1767 size.height = proposedSize.height;
1771 - (void)windowDidBecomeKey:(NSNotification *)notification
1773 WineApplicationController* controller = [WineApplicationController sharedController];
1774 NSEvent* event = [controller lastFlagsChanged];
1776 [self flagsChanged:event];
1778 if (causing_becomeKeyWindow == self) return;
1780 [controller windowGotFocus:self];
1783 - (void)windowDidDeminiaturize:(NSNotification *)notification
1785 WineApplicationController* controller = [WineApplicationController sharedController];
1787 if (!ignore_windowDeminiaturize)
1788 [self postDidUnminimizeEvent];
1789 ignore_windowDeminiaturize = FALSE;
1791 [self becameEligibleParentOrChild];
1793 if (fullscreen && [self isOnActiveSpace])
1794 [controller updateFullscreenWindows];
1795 [controller adjustWindowLevels];
1797 if (![self parentWindow])
1798 [self postBroughtForwardEvent];
1800 if (!self.disabled && !self.noActivate)
1802 causing_becomeKeyWindow = self;
1803 [self makeKeyWindow];
1804 causing_becomeKeyWindow = nil;
1805 [controller windowGotFocus:self];
1808 [self windowDidResize:notification];
1811 - (void) windowDidEndLiveResize:(NSNotification *)notification
1815 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1816 [queue postEvent:event];
1817 macdrv_release_event(event);
1820 self.liveResizeDisplayTimer = nil;
1823 - (void) windowDidEnterFullScreen:(NSNotification*)notification
1825 enteringFullScreen = FALSE;
1826 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1829 - (void) windowDidExitFullScreen:(NSNotification*)notification
1831 exitingFullScreen = FALSE;
1832 [self setFrame:nonFullscreenFrame display:YES animate:NO];
1833 [self windowDidResize:nil];
1836 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1838 enteringFullScreen = FALSE;
1839 enteredFullScreenTime = 0;
1842 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1844 exitingFullScreen = FALSE;
1845 [self windowDidResize:nil];
1848 - (void)windowDidMiniaturize:(NSNotification *)notification
1850 if (fullscreen && [self isOnActiveSpace])
1851 [[WineApplicationController sharedController] updateFullscreenWindows];
1854 - (void)windowDidMove:(NSNotification *)notification
1856 [self windowDidResize:notification];
1859 - (void)windowDidResignKey:(NSNotification *)notification
1861 macdrv_event* event;
1863 if (causing_becomeKeyWindow) return;
1865 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1866 [queue postEvent:event];
1867 macdrv_release_event(event);
1870 - (void)windowDidResize:(NSNotification *)notification
1872 macdrv_event* event;
1873 NSRect frame = [self frame];
1875 if ([self inLiveResize])
1877 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1878 resizingFromLeft = TRUE;
1879 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1880 resizingFromTop = TRUE;
1883 frame = [self contentRectForFrameRect:frame];
1885 if (ignore_windowResize || exitingFullScreen) return;
1887 if ([self preventResizing])
1889 [self setContentMinSize:frame.size];
1890 [self setContentMaxSize:frame.size];
1893 [[WineApplicationController sharedController] flipRect:&frame];
1895 /* Coalesce events by discarding any previous ones still in the queue. */
1896 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1899 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1900 event->window_frame_changed.frame = NSRectToCGRect(frame);
1901 event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
1902 event->window_frame_changed.in_resize = [self inLiveResize];
1903 [queue postEvent:event];
1904 macdrv_release_event(event);
1906 [[[self contentView] inputContext] invalidateCharacterCoordinates];
1907 [self updateFullscreen];
1910 - (BOOL)windowShouldClose:(id)sender
1912 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1913 [queue postEvent:event];
1914 macdrv_release_event(event);
1918 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
1922 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1923 [queue postEvent:event];
1924 macdrv_release_event(event);
1927 else if (!resizable)
1929 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
1930 [queue postEvent:event];
1931 macdrv_release_event(event);
1938 - (void) windowWillClose:(NSNotification*)notification
1942 if (fakingClose) return;
1943 if (latentParentWindow)
1945 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
1946 self.latentParentWindow = nil;
1949 for (child in latentChildWindows)
1951 if (child.latentParentWindow == self)
1952 child.latentParentWindow = nil;
1954 [latentChildWindows removeAllObjects];
1957 - (void) windowWillEnterFullScreen:(NSNotification*)notification
1959 enteringFullScreen = TRUE;
1960 nonFullscreenFrame = [self frame];
1963 - (void) windowWillExitFullScreen:(NSNotification*)notification
1965 exitingFullScreen = TRUE;
1968 - (void)windowWillMiniaturize:(NSNotification *)notification
1970 [self becameIneligibleParentOrChild];
1973 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
1975 if ([self inLiveResize])
1978 return self.frame.size;
1981 macdrv_query* query;
1983 rect = [self frame];
1984 if (resizingFromLeft)
1985 rect.origin.x = NSMaxX(rect) - frameSize.width;
1986 if (!resizingFromTop)
1987 rect.origin.y = NSMaxY(rect) - frameSize.height;
1988 rect.size = frameSize;
1989 rect = [self contentRectForFrameRect:rect];
1990 [[WineApplicationController sharedController] flipRect:&rect];
1992 query = macdrv_create_query();
1993 query->type = QUERY_RESIZE_SIZE;
1994 query->window = (macdrv_window)[self retain];
1995 query->resize_size.rect = NSRectToCGRect(rect);
1996 query->resize_size.from_left = resizingFromLeft;
1997 query->resize_size.from_top = resizingFromTop;
1999 if ([self.queue query:query timeout:0.1])
2001 rect = NSRectFromCGRect(query->resize_size.rect);
2002 rect = [self frameRectForContentRect:rect];
2003 frameSize = rect.size;
2006 macdrv_release_query(query);
2012 - (void) windowWillStartLiveResize:(NSNotification *)notification
2016 macdrv_event* event;
2017 NSRect frame = [self contentRectForFrameRect:self.frame];
2019 [[WineApplicationController sharedController] flipRect:&frame];
2021 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2022 event->window_restore_requested.keep_frame = TRUE;
2023 event->window_restore_requested.frame = NSRectToCGRect(frame);
2024 [queue postEvent:event];
2025 macdrv_release_event(event);
2028 [self sendResizeStartQuery];
2030 frameAtResizeStart = [self frame];
2031 resizingFromLeft = resizingFromTop = FALSE;
2033 // There's a strange restriction in window redrawing during Cocoa-
2034 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
2035 // that happen synchronously when Cocoa tells us that our window size
2036 // has changed or asynchronously in a short interval thereafter provoke
2037 // the window to redraw. Calls to those methods that happen asynchronously
2038 // a half second or more after the last change of the window size aren't
2039 // heeded until the next resize-related user event (e.g. mouse movement).
2041 // Wine often has a significant delay between when it's been told that
2042 // the window has changed size and when it can flush completed drawing.
2043 // So, our windows would get stuck with incomplete drawing for as long
2044 // as the user holds the mouse button down and doesn't move it.
2046 // We address this by "manually" asking our windows to check if they need
2047 // redrawing every so often (during live resize only).
2048 self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
2050 selector:@selector(displayIfNeeded)
2053 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
2054 forMode:NSRunLoopCommonModes];
2057 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2059 macdrv_query* query;
2060 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2063 query = macdrv_create_query();
2064 query->type = QUERY_MIN_MAX_INFO;
2065 query->window = (macdrv_window)[self retain];
2066 [self.queue query:query timeout:0.5];
2067 macdrv_release_query(query);
2069 currentContentRect = [self contentRectForFrameRect:[self frame]];
2070 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2072 maxSize = [self contentMaxSize];
2073 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2074 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2076 // Try to keep the top-left corner where it is.
2077 newContentRect.origin.x = NSMinX(currentContentRect);
2078 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2080 // If that pushes the bottom or right off the screen, pull it up and to the left.
2081 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2082 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2083 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2084 if (NSMinY(newContentRect) < NSMinY(screenRect))
2085 newContentRect.origin.y = NSMinY(screenRect);
2087 // If that pushes the top or left off the screen, push it down and the right
2088 // again. Do this last because the top-left corner is more important than the
2090 if (NSMinX(newContentRect) < NSMinX(screenRect))
2091 newContentRect.origin.x = NSMinX(screenRect);
2092 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2093 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2095 return [self frameRectForContentRect:newContentRect];
2100 * ---------- NSPasteboardOwner methods ----------
2102 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2104 macdrv_query* query = macdrv_create_query();
2105 query->type = QUERY_PASTEBOARD_DATA;
2106 query->window = (macdrv_window)[self retain];
2107 query->pasteboard_data.type = (CFStringRef)[type copy];
2109 [self.queue query:query timeout:3];
2110 macdrv_release_query(query);
2115 * ---------- NSDraggingDestination methods ----------
2117 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2119 return [self draggingUpdated:sender];
2122 - (void) draggingExited:(id <NSDraggingInfo>)sender
2124 // This isn't really a query. We don't need any response. However, it
2125 // has to be processed in a similar manner as the other drag-and-drop
2126 // queries in order to maintain the proper order of operations.
2127 macdrv_query* query = macdrv_create_query();
2128 query->type = QUERY_DRAG_EXITED;
2129 query->window = (macdrv_window)[self retain];
2131 [self.queue query:query timeout:0.1];
2132 macdrv_release_query(query);
2135 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2137 NSDragOperation ret;
2138 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2139 NSPasteboard* pb = [sender draggingPasteboard];
2141 macdrv_query* query = macdrv_create_query();
2142 query->type = QUERY_DRAG_OPERATION;
2143 query->window = (macdrv_window)[self retain];
2144 query->drag_operation.x = pt.x;
2145 query->drag_operation.y = pt.y;
2146 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2147 query->drag_operation.accepted_op = NSDragOperationNone;
2148 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2150 [self.queue query:query timeout:3];
2151 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2152 macdrv_release_query(query);
2157 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2160 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2161 NSPasteboard* pb = [sender draggingPasteboard];
2163 macdrv_query* query = macdrv_create_query();
2164 query->type = QUERY_DRAG_DROP;
2165 query->window = (macdrv_window)[self retain];
2166 query->drag_drop.x = pt.x;
2167 query->drag_drop.y = pt.y;
2168 query->drag_drop.op = [sender draggingSourceOperationMask];
2169 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2171 [self.queue query:query timeout:3 * 60 processEvents:YES];
2172 ret = query->status;
2173 macdrv_release_query(query);
2178 - (BOOL) wantsPeriodicDraggingUpdates
2186 /***********************************************************************
2187 * macdrv_create_cocoa_window
2189 * Create a Cocoa window with the given content frame and features (e.g.
2190 * title bar, close box, etc.).
2192 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2193 CGRect frame, void* hwnd, macdrv_event_queue queue)
2195 __block WineWindow* window;
2198 window = [[WineWindow createWindowWithFeatures:wf
2199 windowFrame:NSRectFromCGRect(frame)
2201 queue:(WineEventQueue*)queue] retain];
2204 return (macdrv_window)window;
2207 /***********************************************************************
2208 * macdrv_destroy_cocoa_window
2210 * Destroy a Cocoa window.
2212 void macdrv_destroy_cocoa_window(macdrv_window w)
2214 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2215 WineWindow* window = (WineWindow*)w;
2218 [window doOrderOut];
2221 [window.queue discardEventsMatchingMask:-1 forWindow:window];
2227 /***********************************************************************
2228 * macdrv_get_window_hwnd
2230 * Get the hwnd that was set for the window at creation.
2232 void* macdrv_get_window_hwnd(macdrv_window w)
2234 WineWindow* window = (WineWindow*)w;
2238 /***********************************************************************
2239 * macdrv_set_cocoa_window_features
2241 * Update a Cocoa window's features.
2243 void macdrv_set_cocoa_window_features(macdrv_window w,
2244 const struct macdrv_window_features* wf)
2246 WineWindow* window = (WineWindow*)w;
2249 [window setWindowFeatures:wf];
2253 /***********************************************************************
2254 * macdrv_set_cocoa_window_state
2256 * Update a Cocoa window's state.
2258 void macdrv_set_cocoa_window_state(macdrv_window w,
2259 const struct macdrv_window_state* state)
2261 WineWindow* window = (WineWindow*)w;
2264 [window setMacDrvState:state];
2268 /***********************************************************************
2269 * macdrv_set_cocoa_window_title
2271 * Set a Cocoa window's title.
2273 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2276 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2277 WineWindow* window = (WineWindow*)w;
2278 NSString* titleString;
2281 titleString = [NSString stringWithCharacters:title length:length];
2284 OnMainThreadAsync(^{
2285 [window setTitle:titleString];
2286 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2287 [NSApp changeWindowsItem:window title:titleString filename:NO];
2293 /***********************************************************************
2294 * macdrv_order_cocoa_window
2296 * Reorder a Cocoa window relative to other windows. If prev is
2297 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
2298 * it is ordered above that window. Otherwise, it is ordered to the
2301 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2302 macdrv_window n, int activate)
2304 WineWindow* window = (WineWindow*)w;
2305 WineWindow* prev = (WineWindow*)p;
2306 WineWindow* next = (WineWindow*)n;
2308 OnMainThreadAsync(^{
2309 [window orderBelow:prev
2313 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2315 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2319 /***********************************************************************
2320 * macdrv_hide_cocoa_window
2322 * Hides a Cocoa window.
2324 void macdrv_hide_cocoa_window(macdrv_window w)
2326 WineWindow* window = (WineWindow*)w;
2329 [window doOrderOut];
2333 /***********************************************************************
2334 * macdrv_set_cocoa_window_frame
2336 * Move a Cocoa window.
2338 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2340 WineWindow* window = (WineWindow*)w;
2343 [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2347 /***********************************************************************
2348 * macdrv_get_cocoa_window_frame
2350 * Gets the frame of a Cocoa window.
2352 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2354 WineWindow* window = (WineWindow*)w;
2359 frame = [window contentRectForFrameRect:[window frame]];
2360 [[WineApplicationController sharedController] flipRect:&frame];
2361 *out_frame = NSRectToCGRect(frame);
2365 /***********************************************************************
2366 * macdrv_set_cocoa_parent_window
2368 * Sets the parent window for a Cocoa window. If parent is NULL, clears
2369 * the parent window.
2371 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2373 WineWindow* window = (WineWindow*)w;
2376 [window setMacDrvParentWindow:(WineWindow*)parent];
2380 /***********************************************************************
2381 * macdrv_set_window_surface
2383 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2385 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2386 WineWindow* window = (WineWindow*)w;
2389 window.surface = surface;
2390 window.surface_mutex = mutex;
2396 /***********************************************************************
2397 * macdrv_window_needs_display
2399 * Mark a window as needing display in a specified rect (in non-client
2400 * area coordinates).
2402 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2404 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2405 WineWindow* window = (WineWindow*)w;
2407 OnMainThreadAsync(^{
2408 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2414 /***********************************************************************
2415 * macdrv_set_window_shape
2417 * Sets the shape of a Cocoa window from an array of rectangles. If
2418 * rects is NULL, resets the window's shape to its frame.
2420 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2422 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2423 WineWindow* window = (WineWindow*)w;
2426 if (!rects || !count)
2429 window.shapeData = nil;
2433 size_t length = sizeof(*rects) * count;
2434 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
2439 path = [NSBezierPath bezierPath];
2440 for (i = 0; i < count; i++)
2441 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2442 window.shape = path;
2443 window.shapeData = [NSData dataWithBytes:rects length:length];
2451 /***********************************************************************
2452 * macdrv_set_window_alpha
2454 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2456 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2457 WineWindow* window = (WineWindow*)w;
2459 [window setAlphaValue:alpha];
2464 /***********************************************************************
2465 * macdrv_set_window_color_key
2467 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2470 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2471 WineWindow* window = (WineWindow*)w;
2474 window.colorKeyed = TRUE;
2475 window.colorKeyRed = keyRed;
2476 window.colorKeyGreen = keyGreen;
2477 window.colorKeyBlue = keyBlue;
2478 [window checkTransparency];
2484 /***********************************************************************
2485 * macdrv_clear_window_color_key
2487 void macdrv_clear_window_color_key(macdrv_window w)
2489 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2490 WineWindow* window = (WineWindow*)w;
2493 window.colorKeyed = FALSE;
2494 [window checkTransparency];
2500 /***********************************************************************
2501 * macdrv_window_use_per_pixel_alpha
2503 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2505 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2506 WineWindow* window = (WineWindow*)w;
2509 window.usePerPixelAlpha = use_per_pixel_alpha;
2510 [window checkTransparency];
2516 /***********************************************************************
2517 * macdrv_give_cocoa_window_focus
2519 * Makes the Cocoa window "key" (gives it keyboard focus). This also
2520 * orders it front and, if its frame was not within the desktop bounds,
2521 * Cocoa will typically move it on-screen.
2523 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2525 WineWindow* window = (WineWindow*)w;
2528 [window makeFocused:activate];
2532 /***********************************************************************
2533 * macdrv_set_window_min_max_sizes
2535 * Sets the window's minimum and maximum content sizes.
2537 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2539 WineWindow* window = (WineWindow*)w;
2542 [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2546 /***********************************************************************
2547 * macdrv_create_view
2549 * Creates and returns a view in the specified rect of the window. The
2550 * caller is responsible for calling macdrv_dispose_view() on the view
2551 * when it is done with it.
2553 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2555 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2556 WineWindow* window = (WineWindow*)w;
2557 __block WineContentView* view;
2559 if (CGRectIsNull(rect)) rect = CGRectZero;
2562 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2564 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2565 [view setAutoresizesSubviews:NO];
2566 [nc addObserver:view
2567 selector:@selector(updateGLContexts)
2568 name:NSViewGlobalFrameDidChangeNotification
2570 [nc addObserver:view
2571 selector:@selector(updateGLContexts)
2572 name:NSApplicationDidChangeScreenParametersNotification
2574 [[window contentView] addSubview:view];
2575 [window updateColorSpace];
2579 return (macdrv_view)view;
2582 /***********************************************************************
2583 * macdrv_dispose_view
2585 * Destroys a view previously returned by macdrv_create_view.
2587 void macdrv_dispose_view(macdrv_view v)
2589 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2590 WineContentView* view = (WineContentView*)v;
2593 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2594 WineWindow* window = (WineWindow*)[view window];
2596 [nc removeObserver:view
2597 name:NSViewGlobalFrameDidChangeNotification
2599 [nc removeObserver:view
2600 name:NSApplicationDidChangeScreenParametersNotification
2602 [view removeFromSuperview];
2604 [window updateColorSpace];
2610 /***********************************************************************
2611 * macdrv_set_view_window_and_frame
2613 * Move a view to a new window and/or position within its window. If w
2614 * is NULL, leave the view in its current window and just change its
2617 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2619 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2620 WineContentView* view = (WineContentView*)v;
2621 WineWindow* window = (WineWindow*)w;
2623 if (CGRectIsNull(rect)) rect = CGRectZero;
2626 BOOL changedWindow = (window && window != [view window]);
2627 NSRect newFrame = NSRectFromCGRect(rect);
2628 NSRect oldFrame = [view frame];
2629 BOOL needUpdateWindowColorSpace = FALSE;
2633 WineWindow* oldWindow = (WineWindow*)[view window];
2634 [view removeFromSuperview];
2635 [oldWindow updateColorSpace];
2636 [[window contentView] addSubview:view];
2637 needUpdateWindowColorSpace = TRUE;
2640 if (!NSEqualRects(oldFrame, newFrame))
2643 [[view superview] setNeedsDisplayInRect:oldFrame];
2644 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2645 [view setFrameSize:newFrame.size];
2646 else if (NSEqualSizes(oldFrame.size, newFrame.size))
2647 [view setFrameOrigin:newFrame.origin];
2649 [view setFrame:newFrame];
2650 [view setNeedsDisplay:YES];
2651 needUpdateWindowColorSpace = TRUE;
2654 if (needUpdateWindowColorSpace)
2655 [(WineWindow*)[view window] updateColorSpace];
2661 /***********************************************************************
2662 * macdrv_add_view_opengl_context
2664 * Add an OpenGL context to the list being tracked for each view.
2666 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2668 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2669 WineContentView* view = (WineContentView*)v;
2670 WineOpenGLContext *context = (WineOpenGLContext*)c;
2673 [view addGLContext:context];
2679 /***********************************************************************
2680 * macdrv_remove_view_opengl_context
2682 * Add an OpenGL context to the list being tracked for each view.
2684 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2686 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2687 WineContentView* view = (WineContentView*)v;
2688 WineOpenGLContext *context = (WineOpenGLContext*)c;
2690 OnMainThreadAsync(^{
2691 [view removeGLContext:context];
2697 /***********************************************************************
2698 * macdrv_window_background_color
2700 * Returns the standard Mac window background color as a 32-bit value of
2701 * the form 0x00rrggbb.
2703 uint32_t macdrv_window_background_color(void)
2705 static uint32_t result;
2706 static dispatch_once_t once;
2708 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2709 // color spaces (RGB or grayscale). So, the only way to get RGB values out
2710 // of it is to draw with it.
2711 dispatch_once(&once, ^{
2713 unsigned char rgbx[4];
2714 unsigned char *planes = rgbx;
2715 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2722 colorSpaceName:NSCalibratedRGBColorSpace
2726 [NSGraphicsContext saveGraphicsState];
2727 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2728 [[NSColor windowBackgroundColor] set];
2729 NSRectFill(NSMakeRect(0, 0, 1, 1));
2730 [NSGraphicsContext restoreGraphicsState];
2732 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2739 /***********************************************************************
2740 * macdrv_send_text_input_event
2742 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2747 WineWindow* window = (WineWindow*)[NSApp keyWindow];
2748 if (![window isKindOfClass:[WineWindow class]])
2750 window = (WineWindow*)[NSApp mainWindow];
2751 if (![window isKindOfClass:[WineWindow class]])
2752 window = [[WineApplicationController sharedController] frontWineWindow];
2757 NSUInteger localFlags = flags;
2761 window.imeData = data;
2762 fix_device_modifiers_by_generic(&localFlags);
2764 // An NSEvent created with +keyEventWithType:... is internally marked
2765 // as synthetic and doesn't get sent through input methods. But one
2766 // created from a CGEvent doesn't have that problem.
2767 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2768 CGEventSetFlags(c, localFlags);
2769 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2770 event = [NSEvent eventWithCGEvent:c];
2773 window.commandDone = FALSE;
2774 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;