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;
201 - (void) updateForGLSubviews;
203 - (BOOL) becameEligibleParentOrChild;
204 - (void) becameIneligibleChild;
209 @implementation WineContentView
213 [markedText release];
214 [glContexts release];
215 [pendingGlContexts release];
224 - (void) drawRect:(NSRect)rect
226 WineWindow* window = (WineWindow*)[self window];
228 for (WineOpenGLContext* context in pendingGlContexts)
230 if (!clearedGlSurface)
232 context.shouldClearToBlack = TRUE;
233 clearedGlSurface = TRUE;
235 context.needsUpdate = TRUE;
237 [glContexts addObjectsFromArray:pendingGlContexts];
238 [pendingGlContexts removeAllObjects];
240 if ([window contentView] != self)
243 if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
245 [[NSColor clearColor] setFill];
248 [window.shape addClip];
250 [[NSColor windowBackgroundColor] setFill];
254 if (window.surface && window.surface_mutex &&
255 !pthread_mutex_lock(window.surface_mutex))
260 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
262 CGContextRef context;
265 [window.shape addClip];
267 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
268 CGContextSetBlendMode(context, kCGBlendModeCopy);
269 CGContextSetInterpolationQuality(context, kCGInterpolationNone);
271 for (i = 0; i < count; i++)
276 imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
277 image = create_surface_image(window.surface, &imageRect, FALSE);
281 if (window.colorKeyed)
283 CGImageRef maskedImage;
284 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
285 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
286 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
287 maskedImage = CGImageCreateWithMaskingColors(image, components);
290 CGImageRelease(image);
295 CGContextDrawImage(context, imageRect, image);
297 CGImageRelease(image);
302 pthread_mutex_unlock(window.surface_mutex);
305 // If the window may be transparent, then we have to invalidate the
306 // shadow every time we draw. Also, if this is the first time we've
307 // drawn since changing from transparent to opaque.
308 if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
310 window.shapeChangedSinceLastDraw = FALSE;
311 [window invalidateShadow];
315 - (void) addGLContext:(WineOpenGLContext*)context
318 glContexts = [[NSMutableArray alloc] init];
319 if (!pendingGlContexts)
320 pendingGlContexts = [[NSMutableArray alloc] init];
322 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
324 [glContexts addObject:context];
325 if (!clearedGlSurface)
327 context.shouldClearToBlack = TRUE;
328 clearedGlSurface = TRUE;
330 context.needsUpdate = TRUE;
334 [pendingGlContexts addObject:context];
335 [self setNeedsDisplay:YES];
338 [(WineWindow*)[self window] updateForGLSubviews];
341 - (void) removeGLContext:(WineOpenGLContext*)context
343 [glContexts removeObjectIdenticalTo:context];
344 [pendingGlContexts removeObjectIdenticalTo:context];
345 [(WineWindow*)[self window] updateForGLSubviews];
348 - (void) updateGLContexts
350 for (WineOpenGLContext* context in glContexts)
351 context.needsUpdate = TRUE;
354 - (BOOL) hasGLContext
356 return [glContexts count] || [pendingGlContexts count];
359 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
364 - (BOOL) preservesContentDuringLiveResize
366 // Returning YES from this tells Cocoa to keep our view's content during
367 // a Cocoa-driven resize. In theory, we're also supposed to override
368 // -setFrameSize: to mark exposed sections as needing redisplay, but
369 // user32 will take care of that in a roundabout way. This way, we don't
370 // redraw until the window surface is flushed.
372 // This doesn't do anything when we resize the window ourselves.
376 - (BOOL)acceptsFirstResponder
378 return [[self window] contentView] == self;
381 - (BOOL) mouseDownCanMoveWindow
386 - (void) completeText:(NSString*)text
389 WineWindow* window = (WineWindow*)[self window];
391 event = macdrv_create_event(IM_SET_TEXT, window);
392 event->im_set_text.data = [window imeData];
393 event->im_set_text.text = (CFStringRef)[text copy];
394 event->im_set_text.complete = TRUE;
396 [[window queue] postEvent:event];
398 macdrv_release_event(event);
400 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
401 markedTextSelection = NSMakeRange(0, 0);
402 [[self inputContext] discardMarkedText];
405 - (NSFocusRingType) focusRingType
407 return NSFocusRingTypeNone;
411 * ---------- NSTextInputClient methods ----------
413 - (NSTextInputContext*) inputContext
416 markedText = [[NSMutableAttributedString alloc] init];
417 return [super inputContext];
420 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
422 if ([string isKindOfClass:[NSAttributedString class]])
423 string = [string string];
425 if ([string isKindOfClass:[NSString class]])
426 [self completeText:string];
429 - (void) doCommandBySelector:(SEL)aSelector
431 [(WineWindow*)[self window] setCommandDone:TRUE];
434 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
436 if ([string isKindOfClass:[NSAttributedString class]])
437 string = [string string];
439 if ([string isKindOfClass:[NSString class]])
442 WineWindow* window = (WineWindow*)[self window];
444 if (replacementRange.location == NSNotFound)
445 replacementRange = NSMakeRange(0, [markedText length]);
447 [markedText replaceCharactersInRange:replacementRange withString:string];
448 markedTextSelection = selectedRange;
449 markedTextSelection.location += replacementRange.location;
451 event = macdrv_create_event(IM_SET_TEXT, window);
452 event->im_set_text.data = [window imeData];
453 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
454 event->im_set_text.complete = FALSE;
455 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
457 [[window queue] postEvent:event];
459 macdrv_release_event(event);
461 [[self inputContext] invalidateCharacterCoordinates];
467 [self completeText:nil];
470 - (NSRange) selectedRange
472 return markedTextSelection;
475 - (NSRange) markedRange
477 NSRange range = NSMakeRange(0, [markedText length]);
479 range.location = NSNotFound;
483 - (BOOL) hasMarkedText
485 return [markedText length] > 0;
488 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
490 if (aRange.location >= [markedText length])
493 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
495 *actualRange = aRange;
496 return [markedText attributedSubstringFromRange:aRange];
499 - (NSArray*) validAttributesForMarkedText
501 return [NSArray array];
504 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
507 WineWindow* window = (WineWindow*)[self window];
510 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
512 query = macdrv_create_query();
513 query->type = QUERY_IME_CHAR_RECT;
514 query->window = (macdrv_window)[window retain];
515 query->ime_char_rect.data = [window imeData];
516 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
518 if ([window.queue query:query timeout:1])
520 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
521 ret = NSRectFromCGRect(query->ime_char_rect.rect);
522 [[WineApplicationController sharedController] flipRect:&ret];
525 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
527 macdrv_release_query(query);
530 *actualRange = aRange;
534 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
539 - (NSInteger) windowLevel
541 return [[self window] level];
547 @implementation WineWindow
549 static WineWindow* causing_becomeKeyWindow;
551 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
552 @synthesize surface, surface_mutex;
553 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
554 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
555 @synthesize usePerPixelAlpha;
556 @synthesize imeData, commandDone;
557 @synthesize liveResizeDisplayTimer;
559 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
560 windowFrame:(NSRect)window_frame
562 queue:(WineEventQueue*)queue
565 WineContentView* contentView;
566 NSTrackingArea* trackingArea;
567 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
569 [[WineApplicationController sharedController] flipRect:&window_frame];
571 window = [[[self alloc] initWithContentRect:window_frame
572 styleMask:style_mask_for_features(wf)
573 backing:NSBackingStoreBuffered
574 defer:YES] autorelease];
576 if (!window) return nil;
578 /* Standardize windows to eliminate differences between titled and
579 borderless windows and between NSWindow and NSPanel. */
580 [window setHidesOnDeactivate:NO];
581 [window setReleasedWhenClosed:NO];
583 [window setOneShot:YES];
584 [window disableCursorRects];
585 [window setShowsResizeIndicator:NO];
586 [window setHasShadow:wf->shadow];
587 [window setAcceptsMouseMovedEvents:YES];
588 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
589 [window setDelegate:window];
591 window.queue = queue;
592 window->savedContentMinSize = NSZeroSize;
593 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
594 window->resizable = wf->resizable;
596 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
597 (NSString*)kUTTypeContent,
600 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
603 [contentView setAutoresizesSubviews:NO];
605 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
606 because they give us mouse moves in the background. */
607 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
608 options:(NSTrackingMouseMoved |
609 NSTrackingActiveAlways |
610 NSTrackingInVisibleRect)
612 userInfo:nil] autorelease];
615 [contentView addTrackingArea:trackingArea];
617 [window setContentView:contentView];
618 [window setInitialFirstResponder:contentView];
620 [nc addObserver:window
621 selector:@selector(updateFullscreen)
622 name:NSApplicationDidChangeScreenParametersNotification
624 [window updateFullscreen];
626 [nc addObserver:window
627 selector:@selector(applicationWillHide)
628 name:NSApplicationWillHideNotification
630 [nc addObserver:window
631 selector:@selector(applicationDidUnhide)
632 name:NSApplicationDidUnhideNotification
640 [[NSNotificationCenter defaultCenter] removeObserver:self];
641 [liveResizeDisplayTimer invalidate];
642 [liveResizeDisplayTimer release];
644 [latentChildWindows release];
645 [latentParentWindow release];
651 - (BOOL) preventResizing
653 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
654 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || preventForClipping);
657 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
659 if (allow_immovable_windows && (disabled || inMaximized))
661 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
667 - (void) adjustFeaturesForState
669 NSUInteger style = [self styleMask];
671 if (style & NSClosableWindowMask)
672 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
673 if (style & NSMiniaturizableWindowMask)
674 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
675 if (style & NSResizableWindowMask)
676 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
677 if ([self respondsToSelector:@selector(toggleFullScreen:)])
679 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
680 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
683 if ([self preventResizing])
685 NSSize size = [self contentRectForFrameRect:[self frame]].size;
686 [self setContentMinSize:size];
687 [self setContentMaxSize:size];
691 [self setContentMaxSize:savedContentMaxSize];
692 [self setContentMinSize:savedContentMinSize];
695 if (allow_immovable_windows || cursor_clipping_locks_windows)
696 [self setMovable:[self allowsMovingWithMaximized:maximized]];
699 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
701 if ([self respondsToSelector:@selector(toggleFullScreen:)])
703 NSUInteger style = [self styleMask];
705 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
706 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized)
708 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
709 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
713 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
714 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
715 if (style & NSFullScreenWindowMask)
716 [super toggleFullScreen:nil];
720 if (behavior != [self collectionBehavior])
722 [self setCollectionBehavior:behavior];
723 [self adjustFeaturesForState];
727 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
729 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
730 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
731 NSUInteger currentStyle = [self styleMask];
732 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
734 if (newStyle != currentStyle)
736 NSString* title = [[[self title] copy] autorelease];
737 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
738 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
739 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
741 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
742 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
743 // just NSTitledWindowMask, the window buttons should disappear rather
744 // than just being disabled. But they don't. Similarly in reverse.
745 // The workaround is to also toggle NSClosableWindowMask at the same time.
746 [self setStyleMask:newStyle ^ NSClosableWindowMask];
748 [self setStyleMask:newStyle];
750 // -setStyleMask: resets the firstResponder to the window. Set it
751 // back to the content view.
752 if ([[self contentView] acceptsFirstResponder])
753 [self makeFirstResponder:[self contentView]];
755 [self adjustFullScreenBehavior:[self collectionBehavior]];
757 if ([[self title] length] == 0 && [title length] > 0)
758 [self setTitle:title];
761 resizable = wf->resizable;
762 [self adjustFeaturesForState];
763 [self setHasShadow:wf->shadow];
766 // Indicates if the window would be visible if the app were not hidden.
767 - (BOOL) wouldBeVisible
769 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
774 return [self wouldBeVisible] || [self isMiniaturized];
777 - (NSInteger) minimumLevelForActive:(BOOL)active
781 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
782 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
783 level = NSFloatingWindowLevel;
785 level = NSNormalWindowLevel;
791 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
793 if (captured || fullscreen)
796 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
798 level = NSStatusWindowLevel + 1;
808 - (void) postDidUnminimizeEvent
812 /* Coalesce events by discarding any previous ones still in the queue. */
813 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
816 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
817 [queue postEvent:event];
818 macdrv_release_event(event);
821 - (void) sendResizeStartQuery
823 macdrv_query* query = macdrv_create_query();
824 query->type = QUERY_RESIZE_START;
825 query->window = (macdrv_window)[self retain];
827 [self.queue query:query timeout:0.3];
828 macdrv_release_query(query);
831 - (void) setMacDrvState:(const struct macdrv_window_state*)state
833 NSWindowCollectionBehavior behavior;
835 self.disabled = state->disabled;
836 self.noActivate = state->no_activate;
838 if (self.floating != state->floating)
840 self.floating = state->floating;
843 // Became floating. If child of non-floating window, make that
844 // relationship latent.
845 WineWindow* parent = (WineWindow*)[self parentWindow];
846 if (parent && !parent.floating)
847 [self becameIneligibleChild];
851 // Became non-floating. If parent of floating children, make that
852 // relationship latent.
854 for (child in [self childWineWindows])
857 [child becameIneligibleChild];
861 // Check our latent relationships. If floating status was the only
862 // reason they were latent, then make them active.
863 if ([self isVisible])
864 [self becameEligibleParentOrChild];
866 [[WineApplicationController sharedController] adjustWindowLevels];
869 if (state->minimized_valid)
871 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
873 pendingMinimize = FALSE;
874 if (state->minimized && ![self isMiniaturized])
876 if ([self wouldBeVisible])
878 if ([self styleMask] & NSFullScreenWindowMask)
880 [self postDidUnminimizeEvent];
881 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
885 [super miniaturize:nil];
886 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
887 event_mask_for_type(WINDOW_GOT_FOCUS) |
888 event_mask_for_type(WINDOW_LOST_FOCUS);
892 pendingMinimize = TRUE;
894 else if (!state->minimized && [self isMiniaturized])
896 ignore_windowDeminiaturize = TRUE;
897 [self deminiaturize:nil];
898 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
902 [queue discardEventsMatchingMask:discard forWindow:self];
905 if (state->maximized != maximized)
907 maximized = state->maximized;
908 [self adjustFeaturesForState];
910 if (!maximized && [self inLiveResize])
911 [self sendResizeStartQuery];
914 behavior = NSWindowCollectionBehaviorDefault;
915 if (state->excluded_by_expose)
916 behavior |= NSWindowCollectionBehaviorTransient;
918 behavior |= NSWindowCollectionBehaviorManaged;
919 if (state->excluded_by_cycle)
921 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
922 if ([self isOrderedIn])
923 [NSApp removeWindowsItem:self];
927 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
928 if ([self isOrderedIn])
929 [NSApp addWindowsItem:self title:[self title] filename:NO];
931 [self adjustFullScreenBehavior:behavior];
934 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
936 BOOL reordered = FALSE;
938 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
940 if ([self level] > [child level])
941 [child setLevel:[self level]];
942 [self addChildWindow:child ordered:NSWindowAbove];
943 [latentChildWindows removeObjectIdenticalTo:child];
944 child.latentParentWindow = nil;
949 if (!latentChildWindows)
950 latentChildWindows = [[NSMutableArray alloc] init];
951 if (![latentChildWindows containsObject:child])
952 [latentChildWindows addObject:child];
953 child.latentParentWindow = self;
959 - (BOOL) addChildWineWindow:(WineWindow*)child
961 return [self addChildWineWindow:child assumeVisible:FALSE];
964 - (void) removeChildWineWindow:(WineWindow*)child
966 [self removeChildWindow:child];
967 if (child.latentParentWindow == self)
968 child.latentParentWindow = nil;
969 [latentChildWindows removeObjectIdenticalTo:child];
972 - (BOOL) becameEligibleParentOrChild
974 BOOL reordered = FALSE;
977 if (latentParentWindow.floating || !self.floating)
979 // If we aren't visible currently, we assume that we should be and soon
980 // will be. So, if the latent parent is visible that's enough to assume
981 // we can establish the parent-child relationship in Cocoa. That will
982 // actually make us visible, which is fine.
983 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
987 // Here, though, we may not actually be visible yet and adding a child
988 // won't make us visible. The caller will have to call this method
989 // again after actually making us visible.
990 if ([self isVisible] && (count = [latentChildWindows count]))
992 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
995 for (i = 0; i < count; i++)
997 WineWindow* child = [latentChildWindows objectAtIndex:i];
998 if ([child isVisible] && (self.floating || !child.floating))
1000 if (child.latentParentWindow == self)
1002 if ([self level] > [child level])
1003 [child setLevel:[self level]];
1004 [self addChildWindow:child ordered:NSWindowAbove];
1005 child.latentParentWindow = nil;
1009 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1010 [indexesToRemove addIndex:i];
1014 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1020 - (void) becameIneligibleChild
1022 WineWindow* parent = (WineWindow*)[self parentWindow];
1025 if (!parent->latentChildWindows)
1026 parent->latentChildWindows = [[NSMutableArray alloc] init];
1027 [parent->latentChildWindows insertObject:self atIndex:0];
1028 self.latentParentWindow = parent;
1029 [parent removeChildWindow:self];
1033 - (void) becameIneligibleParentOrChild
1035 NSArray* childWindows = [self childWineWindows];
1037 [self becameIneligibleChild];
1039 if ([childWindows count])
1043 for (child in childWindows)
1045 child.latentParentWindow = self;
1046 [self removeChildWindow:child];
1049 if (latentChildWindows)
1050 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1052 latentChildWindows = [childWindows mutableCopy];
1056 // Determine if, among Wine windows, this window is directly above or below
1057 // a given other Wine window with no other Wine window intervening.
1058 // Intervening non-Wine windows are ignored.
1059 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1061 NSNumber* windowNumber;
1062 NSNumber* otherWindowNumber;
1063 NSArray* windowNumbers;
1064 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1066 if (![self isVisible] || ![otherWindow isVisible])
1069 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1070 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1071 windowNumbers = [[self class] windowNumbersWithOptions:0];
1072 windowIndex = [windowNumbers indexOfObject:windowNumber];
1073 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1075 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1078 if (orderingMode == NSWindowAbove)
1080 lowIndex = windowIndex;
1081 highIndex = otherWindowIndex;
1083 else if (orderingMode == NSWindowBelow)
1085 lowIndex = otherWindowIndex;
1086 highIndex = windowIndex;
1091 if (highIndex <= lowIndex)
1094 for (i = lowIndex + 1; i < highIndex; i++)
1096 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1097 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1098 if ([interveningWindow isKindOfClass:[WineWindow class]])
1105 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1107 NSMutableArray* windowNumbers;
1108 NSNumber* childWindowNumber;
1109 NSUInteger otherIndex, limit;
1110 NSArray* origChildren;
1111 NSMutableArray* children;
1113 // Get the z-order from the window server and modify it to reflect the
1114 // requested window ordering.
1115 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1116 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1117 [windowNumbers removeObject:childWindowNumber];
1118 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1119 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1121 // Get our child windows and sort them in the reverse of the desired
1122 // z-order (back-to-front).
1123 origChildren = [self childWineWindows];
1124 children = [[origChildren mutableCopy] autorelease];
1125 [children sortWithOptions:NSSortStable
1126 usingComparator:^NSComparisonResult(id obj1, id obj2){
1127 NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1128 NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1129 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1130 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1131 if (index1 == NSNotFound)
1133 if (index2 == NSNotFound)
1134 return NSOrderedSame;
1136 return NSOrderedAscending;
1138 else if (index2 == NSNotFound)
1139 return NSOrderedDescending;
1140 else if (index1 < index2)
1141 return NSOrderedDescending;
1142 else if (index2 < index1)
1143 return NSOrderedAscending;
1145 return NSOrderedSame;
1148 // If the current and desired children arrays match up to a point, leave
1149 // those matching children alone.
1150 limit = MIN([origChildren count], [children count]);
1151 for (otherIndex = 0; otherIndex < limit; otherIndex++)
1153 if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1156 [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1158 // Remove all of the child windows and re-add them back-to-front so they
1159 // are in the desired order.
1160 for (other in children)
1161 [self removeChildWindow:other];
1162 for (other in children)
1163 [self addChildWindow:other ordered:NSWindowAbove];
1166 /* Returns whether or not the window was ordered in, which depends on if
1167 its frame intersects any screen. */
1168 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1170 WineApplicationController* controller = [WineApplicationController sharedController];
1171 if (![self isMiniaturized])
1173 BOOL needAdjustWindowLevels = FALSE;
1176 [controller transformProcessToForeground];
1178 wasVisible = [self isVisible];
1181 [NSApp activateIgnoringOtherApps:YES];
1183 NSDisableScreenUpdates();
1185 if ([self becameEligibleParentOrChild])
1186 needAdjustWindowLevels = TRUE;
1190 WineWindow* other = [prev isVisible] ? prev : next;
1191 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1193 if (![self isOrdered:orderingMode relativeTo:other])
1195 WineWindow* parent = (WineWindow*)[self parentWindow];
1196 WineWindow* otherParent = (WineWindow*)[other parentWindow];
1198 // This window level may not be right for this window based
1199 // on floating-ness, fullscreen-ness, etc. But we set it
1200 // temporarily to allow us to order the windows properly.
1201 // Then the levels get fixed by -adjustWindowLevels.
1202 if ([self level] != [other level])
1203 [self setLevel:[other level]];
1204 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1206 // The above call to -[NSWindow orderWindow:relativeTo:] won't
1207 // reorder windows which are both children of the same parent
1208 // relative to each other, so do that separately.
1209 if (parent && parent == otherParent)
1210 [parent order:orderingMode childWindow:self relativeTo:other];
1212 needAdjustWindowLevels = TRUE;
1217 // Again, temporarily set level to make sure we can order to
1219 next = [controller frontWineWindow];
1220 if (next && [self level] < [next level])
1221 [self setLevel:[next level]];
1222 [self orderFront:nil];
1223 needAdjustWindowLevels = TRUE;
1226 if ([self becameEligibleParentOrChild])
1227 needAdjustWindowLevels = TRUE;
1229 if (needAdjustWindowLevels)
1231 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1232 [controller updateFullscreenWindows];
1233 [controller adjustWindowLevels];
1236 if (pendingMinimize)
1238 [super miniaturize:nil];
1239 pendingMinimize = FALSE;
1242 NSEnableScreenUpdates();
1244 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1245 Generate a frame-changed event just in case. The back end will ignore
1246 it if nothing actually changed. */
1247 [self windowDidResize:nil];
1249 if (![self isExcludedFromWindowsMenu])
1250 [NSApp addWindowsItem:self title:[self title] filename:NO];
1256 WineApplicationController* controller = [WineApplicationController sharedController];
1257 BOOL wasVisible = [self isVisible];
1258 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1260 if ([self isMiniaturized])
1261 pendingMinimize = TRUE;
1263 [self becameIneligibleParentOrChild];
1264 if ([self isMiniaturized])
1268 fakingClose = FALSE;
1271 [self orderOut:nil];
1272 savedVisibleState = FALSE;
1273 if (wasVisible && wasOnActiveSpace && fullscreen)
1274 [controller updateFullscreenWindows];
1275 [controller adjustWindowLevels];
1276 [NSApp removeWindowsItem:self];
1278 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1279 event_mask_for_type(WINDOW_GOT_FOCUS) |
1280 event_mask_for_type(WINDOW_LOST_FOCUS) |
1281 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1282 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1283 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1287 - (void) updateFullscreen
1289 NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1290 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1292 if (nowFullscreen != fullscreen)
1294 WineApplicationController* controller = [WineApplicationController sharedController];
1296 fullscreen = nowFullscreen;
1297 if ([self isVisible] && [self isOnActiveSpace])
1298 [controller updateFullscreenWindows];
1300 [controller adjustWindowLevels];
1304 - (void) setFrameFromWine:(NSRect)contentRect
1306 /* Origin is (left, top) in a top-down space. Need to convert it to
1307 (left, bottom) in a bottom-up space. */
1308 [[WineApplicationController sharedController] flipRect:&contentRect];
1310 /* The back end is establishing a new window size and position. It's
1311 not interested in any stale events regarding those that may be sitting
1313 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1316 if (!NSIsEmptyRect(contentRect))
1318 NSRect frame, oldFrame;
1320 oldFrame = [self frame];
1321 frame = [self frameRectForContentRect:contentRect];
1322 if (!NSEqualRects(frame, oldFrame))
1324 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1325 BOOL needEnableScreenUpdates = FALSE;
1327 if ([self preventResizing])
1329 // Allow the following calls to -setFrame:display: to work even
1330 // if they would violate the content size constraints. This
1331 // shouldn't be necessary since the content size constraints are
1332 // documented to not constrain that method, but it seems to be.
1333 [self setContentMinSize:NSZeroSize];
1334 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1337 if (equalSizes && [[self childWineWindows] count])
1339 // If we change the window frame such that the origin moves
1340 // but the size doesn't change, then Cocoa moves child
1341 // windows with the parent. We don't want that so we fake
1342 // a change of the size and then change it back.
1343 NSRect bogusFrame = frame;
1344 bogusFrame.size.width++;
1346 NSDisableScreenUpdates();
1347 needEnableScreenUpdates = TRUE;
1349 ignore_windowResize = TRUE;
1350 [self setFrame:bogusFrame display:NO];
1351 ignore_windowResize = FALSE;
1354 [self setFrame:frame display:YES];
1355 if ([self preventResizing])
1357 [self setContentMinSize:contentRect.size];
1358 [self setContentMaxSize:contentRect.size];
1361 if (needEnableScreenUpdates)
1362 NSEnableScreenUpdates();
1365 [self updateColorSpace];
1367 if (!enteringFullScreen &&
1368 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1369 nonFullscreenFrame = frame;
1371 [self updateFullscreen];
1373 if ([self isOrderedIn])
1375 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1376 event. The back end will ignore it if nothing actually changed. */
1377 [self windowDidResize:nil];
1383 - (void) setMacDrvParentWindow:(WineWindow*)parent
1385 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1386 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1388 [oldParent removeChildWineWindow:self];
1389 [latentParentWindow removeChildWineWindow:self];
1390 if ([parent addChildWineWindow:self])
1391 [[WineApplicationController sharedController] adjustWindowLevels];
1395 - (void) setDisabled:(BOOL)newValue
1397 if (disabled != newValue)
1399 disabled = newValue;
1400 [self adjustFeaturesForState];
1404 - (BOOL) needsTransparency
1406 return self.shape || self.colorKeyed || self.usePerPixelAlpha ||
1407 (gl_surface_mode == GL_SURFACE_BEHIND && [[self.contentView valueForKeyPath:@"subviews.@max.hasGLContext"] boolValue]);
1410 - (void) checkTransparency
1412 if (![self isOpaque] && !self.needsTransparency)
1414 self.shapeChangedSinceLastDraw = TRUE;
1415 [[self contentView] setNeedsDisplay:YES];
1416 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1417 [self setOpaque:YES];
1419 else if ([self isOpaque] && self.needsTransparency)
1421 self.shapeChangedSinceLastDraw = TRUE;
1422 [[self contentView] setNeedsDisplay:YES];
1423 [self setBackgroundColor:[NSColor clearColor]];
1424 [self setOpaque:NO];
1428 - (void) setShape:(NSBezierPath*)newShape
1430 if (shape == newShape) return;
1434 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1438 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1440 shape = [newShape copy];
1441 self.shapeChangedSinceLastDraw = TRUE;
1443 [self checkTransparency];
1446 - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1448 if (newTimer != liveResizeDisplayTimer)
1450 [liveResizeDisplayTimer invalidate];
1451 [liveResizeDisplayTimer release];
1452 liveResizeDisplayTimer = [newTimer retain];
1456 - (void) makeFocused:(BOOL)activate
1460 [[WineApplicationController sharedController] transformProcessToForeground];
1461 [NSApp activateIgnoringOtherApps:YES];
1464 causing_becomeKeyWindow = self;
1465 [self makeKeyWindow];
1466 causing_becomeKeyWindow = nil;
1468 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1469 event_mask_for_type(WINDOW_LOST_FOCUS)
1473 - (void) postKey:(uint16_t)keyCode
1474 pressed:(BOOL)pressed
1475 modifiers:(NSUInteger)modifiers
1476 event:(NSEvent*)theEvent
1478 macdrv_event* event;
1480 WineApplicationController* controller = [WineApplicationController sharedController];
1482 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1483 event->key.keycode = keyCode;
1484 event->key.modifiers = modifiers;
1485 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1487 if ((cgevent = [theEvent CGEvent]))
1489 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1490 kCGKeyboardEventKeyboardType);
1491 if (keyboardType != controller.keyboardType)
1493 controller.keyboardType = keyboardType;
1494 [controller keyboardSelectionDidChange];
1498 [queue postEvent:event];
1500 macdrv_release_event(event);
1502 [controller noteKey:keyCode pressed:pressed];
1505 - (void) postKeyEvent:(NSEvent *)theEvent
1507 [self flagsChanged:theEvent];
1508 [self postKey:[theEvent keyCode]
1509 pressed:[theEvent type] == NSKeyDown
1510 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1514 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1516 savedContentMinSize = minSize;
1517 savedContentMaxSize = maxSize;
1518 if (![self preventResizing])
1520 [self setContentMinSize:minSize];
1521 [self setContentMaxSize:maxSize];
1525 - (WineWindow*) ancestorWineWindow
1527 WineWindow* ancestor = self;
1530 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1531 if ([parent isKindOfClass:[WineWindow class]])
1539 - (void) postBroughtForwardEvent
1541 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1542 [queue postEvent:event];
1543 macdrv_release_event(event);
1546 - (void) updateForCursorClipping
1548 [self adjustFeaturesForState];
1551 - (void) endWindowDragging
1555 if (draggingPhase == 3)
1557 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
1558 [queue postEvent:event];
1559 macdrv_release_event(event);
1563 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
1569 * ---------- NSWindow method overrides ----------
1571 - (BOOL) canBecomeKeyWindow
1573 if (causing_becomeKeyWindow == self) return YES;
1574 if (self.disabled || self.noActivate) return NO;
1575 return [self isKeyWindow];
1578 - (BOOL) canBecomeMainWindow
1580 return [self canBecomeKeyWindow];
1583 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1585 // If a window is sized to completely cover a screen, then it's in
1586 // full-screen mode. In that case, we don't allow NSWindow to constrain
1588 NSArray* screens = [NSScreen screens];
1589 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1590 if (!screen_covered_by_rect(contentRect, screens) &&
1591 frame_intersects_screens(frameRect, screens))
1592 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1596 - (BOOL) isExcludedFromWindowsMenu
1598 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1601 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1603 BOOL ret = [super validateMenuItem:menuItem];
1605 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1606 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1607 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
1613 /* We don't call this. It's the action method of the items in the Window menu. */
1614 - (void) makeKeyAndOrderFront:(id)sender
1616 if ([self isMiniaturized])
1617 [self deminiaturize:nil];
1618 [self orderBelow:nil orAbove:nil activate:NO];
1619 [[self ancestorWineWindow] postBroughtForwardEvent];
1621 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1622 [[WineApplicationController sharedController] windowGotFocus:self];
1625 - (void) sendEvent:(NSEvent*)event
1627 NSEventType type = event.type;
1629 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1630 interface control. For example, Control-Tab switches focus among
1631 views. We want to bypass that feature, so directly route key-down
1632 events to -keyDown:. */
1633 if (type == NSKeyDown)
1634 [[self firstResponder] keyDown:event];
1637 if (!draggingPhase && maximized && ![self isMovable] &&
1638 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
1639 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
1641 NSRect titleBar = self.frame;
1642 NSRect contentRect = [self contentRectForFrameRect:titleBar];
1643 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
1644 titleBar.origin.y = NSMaxY(contentRect);
1646 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
1648 if (NSMouseInRect(dragStartPosition, titleBar, NO))
1650 static const NSWindowButton buttons[] = {
1651 NSWindowCloseButton,
1652 NSWindowMiniaturizeButton,
1654 NSWindowFullScreenButton,
1656 BOOL hitButton = NO;
1659 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
1663 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
1666 button = [self standardWindowButton:buttons[i]];
1667 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
1677 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
1678 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
1682 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
1684 if ([self isMovable])
1686 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
1687 NSPoint newTopLeft = dragWindowStartPosition;
1689 newTopLeft.x += point.x - dragStartPosition.x;
1690 newTopLeft.y += point.y - dragStartPosition.y;
1692 if (draggingPhase == 2)
1694 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
1695 [queue postEvent:event];
1696 macdrv_release_event(event);
1701 [self setFrameTopLeftPoint:newTopLeft];
1703 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
1705 macdrv_event* event;
1706 NSRect frame = [self contentRectForFrameRect:self.frame];
1708 [[WineApplicationController sharedController] flipRect:&frame];
1710 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1711 event->window_restore_requested.keep_frame = TRUE;
1712 event->window_restore_requested.frame = NSRectToCGRect(frame);
1713 [queue postEvent:event];
1714 macdrv_release_event(event);
1719 if (type == NSLeftMouseUp)
1720 [self endWindowDragging];
1723 [super sendEvent:event];
1727 - (void) miniaturize:(id)sender
1729 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1730 [queue postEvent:event];
1731 macdrv_release_event(event);
1734 - (void) toggleFullScreen:(id)sender
1736 if (!self.disabled && !maximized)
1737 [super toggleFullScreen:sender];
1740 - (NSArray*) childWineWindows
1742 NSArray* childWindows = self.childWindows;
1743 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
1744 return [child isKindOfClass:[WineWindow class]];
1746 return [childWindows objectsAtIndexes:indexes];
1749 // We normally use the generic/calibrated RGB color space for the window,
1750 // rather than the device color space, to avoid expensive color conversion
1751 // which slows down drawing. However, for windows displaying OpenGL, having
1752 // a different color space than the screen greatly reduces frame rates, often
1753 // limiting it to the display refresh rate.
1755 // To avoid this, we switch back to the screen color space whenever the
1756 // window is covered by a view with an attached OpenGL context.
1757 - (void) updateColorSpace
1759 NSRect contentRect = [[self contentView] frame];
1760 BOOL coveredByGLView = FALSE;
1761 for (WineContentView* view in [[self contentView] subviews])
1763 if ([view hasGLContext])
1765 NSRect frame = [view convertRect:[view bounds] toView:nil];
1766 if (NSContainsRect(frame, contentRect))
1768 coveredByGLView = TRUE;
1774 if (coveredByGLView)
1775 [self setColorSpace:nil];
1777 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1780 - (void) updateForGLSubviews
1782 [self updateColorSpace];
1783 if (gl_surface_mode == GL_SURFACE_BEHIND)
1784 [self checkTransparency];
1789 * ---------- NSResponder method overrides ----------
1791 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1793 - (void) flagsChanged:(NSEvent *)theEvent
1795 static const struct {
1799 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1800 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1801 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1802 { NX_DEVICELCTLKEYMASK, kVK_Control },
1803 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1804 { NX_DEVICELALTKEYMASK, kVK_Option },
1805 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1806 { NX_DEVICELCMDKEYMASK, kVK_Command },
1807 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1810 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1812 int i, last_changed;
1814 fix_device_modifiers_by_generic(&modifierFlags);
1815 changed = modifierFlags ^ lastModifierFlags;
1818 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1819 if (changed & modifiers[i].mask)
1822 for (i = 0; i <= last_changed; i++)
1824 if (changed & modifiers[i].mask)
1826 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1828 if (i == last_changed)
1829 lastModifierFlags = modifierFlags;
1832 lastModifierFlags ^= modifiers[i].mask;
1833 fix_generic_modifiers_by_device(&lastModifierFlags);
1836 // Caps lock generates one event for each press-release action.
1837 // We need to simulate a pair of events for each actual event.
1838 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1840 [self postKey:modifiers[i].keycode
1842 modifiers:lastModifierFlags
1843 event:(NSEvent*)theEvent];
1847 [self postKey:modifiers[i].keycode
1849 modifiers:lastModifierFlags
1850 event:(NSEvent*)theEvent];
1855 - (void) applicationWillHide
1857 savedVisibleState = [self isVisible];
1860 - (void) applicationDidUnhide
1862 if ([self isVisible])
1863 [self becameEligibleParentOrChild];
1868 * ---------- NSWindowDelegate methods ----------
1870 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1872 macdrv_query* query;
1875 query = macdrv_create_query();
1876 query->type = QUERY_MIN_MAX_INFO;
1877 query->window = (macdrv_window)[self retain];
1878 [self.queue query:query timeout:0.5];
1879 macdrv_release_query(query);
1881 size = [self contentMaxSize];
1882 if (proposedSize.width < size.width)
1883 size.width = proposedSize.width;
1884 if (proposedSize.height < size.height)
1885 size.height = proposedSize.height;
1889 - (void)windowDidBecomeKey:(NSNotification *)notification
1891 WineApplicationController* controller = [WineApplicationController sharedController];
1892 NSEvent* event = [controller lastFlagsChanged];
1894 [self flagsChanged:event];
1896 if (causing_becomeKeyWindow == self) return;
1898 [controller windowGotFocus:self];
1901 - (void)windowDidDeminiaturize:(NSNotification *)notification
1903 WineApplicationController* controller = [WineApplicationController sharedController];
1905 if (!ignore_windowDeminiaturize)
1906 [self postDidUnminimizeEvent];
1907 ignore_windowDeminiaturize = FALSE;
1909 [self becameEligibleParentOrChild];
1911 if (fullscreen && [self isOnActiveSpace])
1912 [controller updateFullscreenWindows];
1913 [controller adjustWindowLevels];
1915 if (![self parentWindow])
1916 [self postBroughtForwardEvent];
1918 if (!self.disabled && !self.noActivate)
1920 causing_becomeKeyWindow = self;
1921 [self makeKeyWindow];
1922 causing_becomeKeyWindow = nil;
1923 [controller windowGotFocus:self];
1926 [self windowDidResize:notification];
1929 - (void) windowDidEndLiveResize:(NSNotification *)notification
1933 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1934 [queue postEvent:event];
1935 macdrv_release_event(event);
1938 self.liveResizeDisplayTimer = nil;
1941 - (void) windowDidEnterFullScreen:(NSNotification*)notification
1943 enteringFullScreen = FALSE;
1944 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1947 - (void) windowDidExitFullScreen:(NSNotification*)notification
1949 exitingFullScreen = FALSE;
1950 [self setFrame:nonFullscreenFrame display:YES animate:NO];
1951 [self windowDidResize:nil];
1954 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1956 enteringFullScreen = FALSE;
1957 enteredFullScreenTime = 0;
1960 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1962 exitingFullScreen = FALSE;
1963 [self windowDidResize:nil];
1966 - (void)windowDidMiniaturize:(NSNotification *)notification
1968 if (fullscreen && [self isOnActiveSpace])
1969 [[WineApplicationController sharedController] updateFullscreenWindows];
1972 - (void)windowDidMove:(NSNotification *)notification
1974 [self windowDidResize:notification];
1977 - (void)windowDidResignKey:(NSNotification *)notification
1979 macdrv_event* event;
1981 if (causing_becomeKeyWindow) return;
1983 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1984 [queue postEvent:event];
1985 macdrv_release_event(event);
1988 - (void)windowDidResize:(NSNotification *)notification
1990 macdrv_event* event;
1991 NSRect frame = [self frame];
1993 if ([self inLiveResize])
1995 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1996 resizingFromLeft = TRUE;
1997 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1998 resizingFromTop = TRUE;
2001 frame = [self contentRectForFrameRect:frame];
2003 if (ignore_windowResize || exitingFullScreen) return;
2005 if ([self preventResizing])
2007 [self setContentMinSize:frame.size];
2008 [self setContentMaxSize:frame.size];
2011 [[WineApplicationController sharedController] flipRect:&frame];
2013 /* Coalesce events by discarding any previous ones still in the queue. */
2014 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2017 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2018 event->window_frame_changed.frame = NSRectToCGRect(frame);
2019 event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
2020 event->window_frame_changed.in_resize = [self inLiveResize];
2021 [queue postEvent:event];
2022 macdrv_release_event(event);
2024 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2025 [self updateFullscreen];
2028 - (BOOL)windowShouldClose:(id)sender
2030 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2031 [queue postEvent:event];
2032 macdrv_release_event(event);
2036 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2040 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2041 [queue postEvent:event];
2042 macdrv_release_event(event);
2045 else if (!resizable)
2047 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2048 [queue postEvent:event];
2049 macdrv_release_event(event);
2056 - (void) windowWillClose:(NSNotification*)notification
2060 if (fakingClose) return;
2061 if (latentParentWindow)
2063 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2064 self.latentParentWindow = nil;
2067 for (child in latentChildWindows)
2069 if (child.latentParentWindow == self)
2070 child.latentParentWindow = nil;
2072 [latentChildWindows removeAllObjects];
2075 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2077 enteringFullScreen = TRUE;
2078 nonFullscreenFrame = [self frame];
2081 - (void) windowWillExitFullScreen:(NSNotification*)notification
2083 exitingFullScreen = TRUE;
2086 - (void)windowWillMiniaturize:(NSNotification *)notification
2088 [self becameIneligibleParentOrChild];
2091 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2093 if ([self inLiveResize])
2096 return self.frame.size;
2099 macdrv_query* query;
2101 rect = [self frame];
2102 if (resizingFromLeft)
2103 rect.origin.x = NSMaxX(rect) - frameSize.width;
2104 if (!resizingFromTop)
2105 rect.origin.y = NSMaxY(rect) - frameSize.height;
2106 rect.size = frameSize;
2107 rect = [self contentRectForFrameRect:rect];
2108 [[WineApplicationController sharedController] flipRect:&rect];
2110 query = macdrv_create_query();
2111 query->type = QUERY_RESIZE_SIZE;
2112 query->window = (macdrv_window)[self retain];
2113 query->resize_size.rect = NSRectToCGRect(rect);
2114 query->resize_size.from_left = resizingFromLeft;
2115 query->resize_size.from_top = resizingFromTop;
2117 if ([self.queue query:query timeout:0.1])
2119 rect = NSRectFromCGRect(query->resize_size.rect);
2120 rect = [self frameRectForContentRect:rect];
2121 frameSize = rect.size;
2124 macdrv_release_query(query);
2130 - (void) windowWillStartLiveResize:(NSNotification *)notification
2132 [self endWindowDragging];
2136 macdrv_event* event;
2137 NSRect frame = [self contentRectForFrameRect:self.frame];
2139 [[WineApplicationController sharedController] flipRect:&frame];
2141 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2142 event->window_restore_requested.keep_frame = TRUE;
2143 event->window_restore_requested.frame = NSRectToCGRect(frame);
2144 [queue postEvent:event];
2145 macdrv_release_event(event);
2148 [self sendResizeStartQuery];
2150 frameAtResizeStart = [self frame];
2151 resizingFromLeft = resizingFromTop = FALSE;
2153 // There's a strange restriction in window redrawing during Cocoa-
2154 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
2155 // that happen synchronously when Cocoa tells us that our window size
2156 // has changed or asynchronously in a short interval thereafter provoke
2157 // the window to redraw. Calls to those methods that happen asynchronously
2158 // a half second or more after the last change of the window size aren't
2159 // heeded until the next resize-related user event (e.g. mouse movement).
2161 // Wine often has a significant delay between when it's been told that
2162 // the window has changed size and when it can flush completed drawing.
2163 // So, our windows would get stuck with incomplete drawing for as long
2164 // as the user holds the mouse button down and doesn't move it.
2166 // We address this by "manually" asking our windows to check if they need
2167 // redrawing every so often (during live resize only).
2168 self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
2170 selector:@selector(displayIfNeeded)
2173 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
2174 forMode:NSRunLoopCommonModes];
2177 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2179 macdrv_query* query;
2180 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2183 query = macdrv_create_query();
2184 query->type = QUERY_MIN_MAX_INFO;
2185 query->window = (macdrv_window)[self retain];
2186 [self.queue query:query timeout:0.5];
2187 macdrv_release_query(query);
2189 currentContentRect = [self contentRectForFrameRect:[self frame]];
2190 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2192 maxSize = [self contentMaxSize];
2193 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2194 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2196 // Try to keep the top-left corner where it is.
2197 newContentRect.origin.x = NSMinX(currentContentRect);
2198 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2200 // If that pushes the bottom or right off the screen, pull it up and to the left.
2201 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2202 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2203 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2204 if (NSMinY(newContentRect) < NSMinY(screenRect))
2205 newContentRect.origin.y = NSMinY(screenRect);
2207 // If that pushes the top or left off the screen, push it down and the right
2208 // again. Do this last because the top-left corner is more important than the
2210 if (NSMinX(newContentRect) < NSMinX(screenRect))
2211 newContentRect.origin.x = NSMinX(screenRect);
2212 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2213 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2215 return [self frameRectForContentRect:newContentRect];
2220 * ---------- NSPasteboardOwner methods ----------
2222 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2224 macdrv_query* query = macdrv_create_query();
2225 query->type = QUERY_PASTEBOARD_DATA;
2226 query->window = (macdrv_window)[self retain];
2227 query->pasteboard_data.type = (CFStringRef)[type copy];
2229 [self.queue query:query timeout:3];
2230 macdrv_release_query(query);
2235 * ---------- NSDraggingDestination methods ----------
2237 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2239 return [self draggingUpdated:sender];
2242 - (void) draggingExited:(id <NSDraggingInfo>)sender
2244 // This isn't really a query. We don't need any response. However, it
2245 // has to be processed in a similar manner as the other drag-and-drop
2246 // queries in order to maintain the proper order of operations.
2247 macdrv_query* query = macdrv_create_query();
2248 query->type = QUERY_DRAG_EXITED;
2249 query->window = (macdrv_window)[self retain];
2251 [self.queue query:query timeout:0.1];
2252 macdrv_release_query(query);
2255 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2257 NSDragOperation ret;
2258 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2259 NSPasteboard* pb = [sender draggingPasteboard];
2261 macdrv_query* query = macdrv_create_query();
2262 query->type = QUERY_DRAG_OPERATION;
2263 query->window = (macdrv_window)[self retain];
2264 query->drag_operation.x = pt.x;
2265 query->drag_operation.y = pt.y;
2266 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2267 query->drag_operation.accepted_op = NSDragOperationNone;
2268 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2270 [self.queue query:query timeout:3];
2271 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2272 macdrv_release_query(query);
2277 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2280 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2281 NSPasteboard* pb = [sender draggingPasteboard];
2283 macdrv_query* query = macdrv_create_query();
2284 query->type = QUERY_DRAG_DROP;
2285 query->window = (macdrv_window)[self retain];
2286 query->drag_drop.x = pt.x;
2287 query->drag_drop.y = pt.y;
2288 query->drag_drop.op = [sender draggingSourceOperationMask];
2289 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2291 [self.queue query:query timeout:3 * 60 processEvents:YES];
2292 ret = query->status;
2293 macdrv_release_query(query);
2298 - (BOOL) wantsPeriodicDraggingUpdates
2306 /***********************************************************************
2307 * macdrv_create_cocoa_window
2309 * Create a Cocoa window with the given content frame and features (e.g.
2310 * title bar, close box, etc.).
2312 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2313 CGRect frame, void* hwnd, macdrv_event_queue queue)
2315 __block WineWindow* window;
2318 window = [[WineWindow createWindowWithFeatures:wf
2319 windowFrame:NSRectFromCGRect(frame)
2321 queue:(WineEventQueue*)queue] retain];
2324 return (macdrv_window)window;
2327 /***********************************************************************
2328 * macdrv_destroy_cocoa_window
2330 * Destroy a Cocoa window.
2332 void macdrv_destroy_cocoa_window(macdrv_window w)
2334 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2335 WineWindow* window = (WineWindow*)w;
2338 [window doOrderOut];
2341 [window.queue discardEventsMatchingMask:-1 forWindow:window];
2347 /***********************************************************************
2348 * macdrv_get_window_hwnd
2350 * Get the hwnd that was set for the window at creation.
2352 void* macdrv_get_window_hwnd(macdrv_window w)
2354 WineWindow* window = (WineWindow*)w;
2358 /***********************************************************************
2359 * macdrv_set_cocoa_window_features
2361 * Update a Cocoa window's features.
2363 void macdrv_set_cocoa_window_features(macdrv_window w,
2364 const struct macdrv_window_features* wf)
2366 WineWindow* window = (WineWindow*)w;
2369 [window setWindowFeatures:wf];
2373 /***********************************************************************
2374 * macdrv_set_cocoa_window_state
2376 * Update a Cocoa window's state.
2378 void macdrv_set_cocoa_window_state(macdrv_window w,
2379 const struct macdrv_window_state* state)
2381 WineWindow* window = (WineWindow*)w;
2384 [window setMacDrvState:state];
2388 /***********************************************************************
2389 * macdrv_set_cocoa_window_title
2391 * Set a Cocoa window's title.
2393 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2396 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2397 WineWindow* window = (WineWindow*)w;
2398 NSString* titleString;
2401 titleString = [NSString stringWithCharacters:title length:length];
2404 OnMainThreadAsync(^{
2405 [window setTitle:titleString];
2406 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2407 [NSApp changeWindowsItem:window title:titleString filename:NO];
2413 /***********************************************************************
2414 * macdrv_order_cocoa_window
2416 * Reorder a Cocoa window relative to other windows. If prev is
2417 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
2418 * it is ordered above that window. Otherwise, it is ordered to the
2421 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2422 macdrv_window n, int activate)
2424 WineWindow* window = (WineWindow*)w;
2425 WineWindow* prev = (WineWindow*)p;
2426 WineWindow* next = (WineWindow*)n;
2428 OnMainThreadAsync(^{
2429 [window orderBelow:prev
2433 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2435 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2439 /***********************************************************************
2440 * macdrv_hide_cocoa_window
2442 * Hides a Cocoa window.
2444 void macdrv_hide_cocoa_window(macdrv_window w)
2446 WineWindow* window = (WineWindow*)w;
2449 [window doOrderOut];
2453 /***********************************************************************
2454 * macdrv_set_cocoa_window_frame
2456 * Move a Cocoa window.
2458 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2460 WineWindow* window = (WineWindow*)w;
2463 [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2467 /***********************************************************************
2468 * macdrv_get_cocoa_window_frame
2470 * Gets the frame of a Cocoa window.
2472 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2474 WineWindow* window = (WineWindow*)w;
2479 frame = [window contentRectForFrameRect:[window frame]];
2480 [[WineApplicationController sharedController] flipRect:&frame];
2481 *out_frame = NSRectToCGRect(frame);
2485 /***********************************************************************
2486 * macdrv_set_cocoa_parent_window
2488 * Sets the parent window for a Cocoa window. If parent is NULL, clears
2489 * the parent window.
2491 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2493 WineWindow* window = (WineWindow*)w;
2496 [window setMacDrvParentWindow:(WineWindow*)parent];
2500 /***********************************************************************
2501 * macdrv_set_window_surface
2503 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2505 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2506 WineWindow* window = (WineWindow*)w;
2509 window.surface = surface;
2510 window.surface_mutex = mutex;
2516 /***********************************************************************
2517 * macdrv_window_needs_display
2519 * Mark a window as needing display in a specified rect (in non-client
2520 * area coordinates).
2522 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2524 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2525 WineWindow* window = (WineWindow*)w;
2527 OnMainThreadAsync(^{
2528 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2534 /***********************************************************************
2535 * macdrv_set_window_shape
2537 * Sets the shape of a Cocoa window from an array of rectangles. If
2538 * rects is NULL, resets the window's shape to its frame.
2540 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2542 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2543 WineWindow* window = (WineWindow*)w;
2546 if (!rects || !count)
2549 window.shapeData = nil;
2553 size_t length = sizeof(*rects) * count;
2554 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
2559 path = [NSBezierPath bezierPath];
2560 for (i = 0; i < count; i++)
2561 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2562 window.shape = path;
2563 window.shapeData = [NSData dataWithBytes:rects length:length];
2571 /***********************************************************************
2572 * macdrv_set_window_alpha
2574 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2576 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2577 WineWindow* window = (WineWindow*)w;
2579 [window setAlphaValue:alpha];
2584 /***********************************************************************
2585 * macdrv_set_window_color_key
2587 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2590 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2591 WineWindow* window = (WineWindow*)w;
2594 window.colorKeyed = TRUE;
2595 window.colorKeyRed = keyRed;
2596 window.colorKeyGreen = keyGreen;
2597 window.colorKeyBlue = keyBlue;
2598 [window checkTransparency];
2604 /***********************************************************************
2605 * macdrv_clear_window_color_key
2607 void macdrv_clear_window_color_key(macdrv_window w)
2609 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2610 WineWindow* window = (WineWindow*)w;
2613 window.colorKeyed = FALSE;
2614 [window checkTransparency];
2620 /***********************************************************************
2621 * macdrv_window_use_per_pixel_alpha
2623 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2625 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2626 WineWindow* window = (WineWindow*)w;
2629 window.usePerPixelAlpha = use_per_pixel_alpha;
2630 [window checkTransparency];
2636 /***********************************************************************
2637 * macdrv_give_cocoa_window_focus
2639 * Makes the Cocoa window "key" (gives it keyboard focus). This also
2640 * orders it front and, if its frame was not within the desktop bounds,
2641 * Cocoa will typically move it on-screen.
2643 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2645 WineWindow* window = (WineWindow*)w;
2648 [window makeFocused:activate];
2652 /***********************************************************************
2653 * macdrv_set_window_min_max_sizes
2655 * Sets the window's minimum and maximum content sizes.
2657 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2659 WineWindow* window = (WineWindow*)w;
2662 [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2666 /***********************************************************************
2667 * macdrv_create_view
2669 * Creates and returns a view in the specified rect of the window. The
2670 * caller is responsible for calling macdrv_dispose_view() on the view
2671 * when it is done with it.
2673 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2675 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2676 WineWindow* window = (WineWindow*)w;
2677 __block WineContentView* view;
2679 if (CGRectIsNull(rect)) rect = CGRectZero;
2682 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2684 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2685 [view setAutoresizesSubviews:NO];
2686 [nc addObserver:view
2687 selector:@selector(updateGLContexts)
2688 name:NSViewGlobalFrameDidChangeNotification
2690 [nc addObserver:view
2691 selector:@selector(updateGLContexts)
2692 name:NSApplicationDidChangeScreenParametersNotification
2694 [[window contentView] addSubview:view];
2695 [window updateForGLSubviews];
2699 return (macdrv_view)view;
2702 /***********************************************************************
2703 * macdrv_dispose_view
2705 * Destroys a view previously returned by macdrv_create_view.
2707 void macdrv_dispose_view(macdrv_view v)
2709 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2710 WineContentView* view = (WineContentView*)v;
2713 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2714 WineWindow* window = (WineWindow*)[view window];
2716 [nc removeObserver:view
2717 name:NSViewGlobalFrameDidChangeNotification
2719 [nc removeObserver:view
2720 name:NSApplicationDidChangeScreenParametersNotification
2722 [view removeFromSuperview];
2724 [window updateForGLSubviews];
2730 /***********************************************************************
2731 * macdrv_set_view_window_and_frame
2733 * Move a view to a new window and/or position within its window. If w
2734 * is NULL, leave the view in its current window and just change its
2737 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2739 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2740 WineContentView* view = (WineContentView*)v;
2741 WineWindow* window = (WineWindow*)w;
2743 if (CGRectIsNull(rect)) rect = CGRectZero;
2746 BOOL changedWindow = (window && window != [view window]);
2747 NSRect newFrame = NSRectFromCGRect(rect);
2748 NSRect oldFrame = [view frame];
2749 BOOL needUpdateWindowForGLSubviews = FALSE;
2753 WineWindow* oldWindow = (WineWindow*)[view window];
2754 [view removeFromSuperview];
2755 [oldWindow updateForGLSubviews];
2756 [[window contentView] addSubview:view];
2757 needUpdateWindowForGLSubviews = TRUE;
2760 if (!NSEqualRects(oldFrame, newFrame))
2763 [[view superview] setNeedsDisplayInRect:oldFrame];
2764 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2765 [view setFrameSize:newFrame.size];
2766 else if (NSEqualSizes(oldFrame.size, newFrame.size))
2767 [view setFrameOrigin:newFrame.origin];
2769 [view setFrame:newFrame];
2770 [view setNeedsDisplay:YES];
2771 needUpdateWindowForGLSubviews = TRUE;
2774 if (needUpdateWindowForGLSubviews)
2775 [(WineWindow*)[view window] updateForGLSubviews];
2781 /***********************************************************************
2782 * macdrv_add_view_opengl_context
2784 * Add an OpenGL context to the list being tracked for each view.
2786 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2788 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2789 WineContentView* view = (WineContentView*)v;
2790 WineOpenGLContext *context = (WineOpenGLContext*)c;
2793 [view addGLContext:context];
2799 /***********************************************************************
2800 * macdrv_remove_view_opengl_context
2802 * Add an OpenGL context to the list being tracked for each view.
2804 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2806 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2807 WineContentView* view = (WineContentView*)v;
2808 WineOpenGLContext *context = (WineOpenGLContext*)c;
2810 OnMainThreadAsync(^{
2811 [view removeGLContext:context];
2817 /***********************************************************************
2818 * macdrv_window_background_color
2820 * Returns the standard Mac window background color as a 32-bit value of
2821 * the form 0x00rrggbb.
2823 uint32_t macdrv_window_background_color(void)
2825 static uint32_t result;
2826 static dispatch_once_t once;
2828 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2829 // color spaces (RGB or grayscale). So, the only way to get RGB values out
2830 // of it is to draw with it.
2831 dispatch_once(&once, ^{
2833 unsigned char rgbx[4];
2834 unsigned char *planes = rgbx;
2835 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2842 colorSpaceName:NSCalibratedRGBColorSpace
2846 [NSGraphicsContext saveGraphicsState];
2847 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2848 [[NSColor windowBackgroundColor] set];
2849 NSRectFill(NSMakeRect(0, 0, 1, 1));
2850 [NSGraphicsContext restoreGraphicsState];
2852 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2859 /***********************************************************************
2860 * macdrv_send_text_input_event
2862 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2867 WineWindow* window = (WineWindow*)[NSApp keyWindow];
2868 if (![window isKindOfClass:[WineWindow class]])
2870 window = (WineWindow*)[NSApp mainWindow];
2871 if (![window isKindOfClass:[WineWindow class]])
2872 window = [[WineApplicationController sharedController] frontWineWindow];
2877 NSUInteger localFlags = flags;
2881 window.imeData = data;
2882 fix_device_modifiers_by_generic(&localFlags);
2884 // An NSEvent created with +keyEventWithType:... is internally marked
2885 // as synthetic and doesn't get sent through input methods. But one
2886 // created from a CGEvent doesn't have that problem.
2887 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2888 CGEventSetFlags(c, localFlags);
2889 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2890 event = [NSEvent eventWithCGEvent:c];
2893 window.commandDone = FALSE;
2894 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;