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 - (BOOL) allowsMovingWithMaximized:(BOOL)inMaximized
658 if (allow_immovable_windows && (disabled || inMaximized))
660 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
666 - (void) adjustFeaturesForState
668 NSUInteger style = [self styleMask];
670 if (style & NSClosableWindowMask)
671 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
672 if (style & NSMiniaturizableWindowMask)
673 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
674 if (style & NSResizableWindowMask)
675 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
676 if ([self respondsToSelector:@selector(toggleFullScreen:)])
678 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
679 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
682 if ([self preventResizing])
684 NSSize size = [self contentRectForFrameRect:[self frame]].size;
685 [self setContentMinSize:size];
686 [self setContentMaxSize:size];
690 [self setContentMaxSize:savedContentMaxSize];
691 [self setContentMinSize:savedContentMinSize];
694 if (allow_immovable_windows || cursor_clipping_locks_windows)
695 [self setMovable:[self allowsMovingWithMaximized:maximized]];
698 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
700 if ([self respondsToSelector:@selector(toggleFullScreen:)])
702 NSUInteger style = [self styleMask];
704 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
705 style & NSResizableWindowMask && !(style & NSUtilityWindowMask) && !maximized)
707 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
708 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
712 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
713 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
714 if (style & NSFullScreenWindowMask)
715 [super toggleFullScreen:nil];
719 if (behavior != [self collectionBehavior])
721 [self setCollectionBehavior:behavior];
722 [self adjustFeaturesForState];
726 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
728 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
729 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
730 NSUInteger currentStyle = [self styleMask];
731 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
733 if (newStyle != currentStyle)
735 NSString* title = [[[self title] copy] autorelease];
736 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
737 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
738 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
740 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
741 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
742 // just NSTitledWindowMask, the window buttons should disappear rather
743 // than just being disabled. But they don't. Similarly in reverse.
744 // The workaround is to also toggle NSClosableWindowMask at the same time.
745 [self setStyleMask:newStyle ^ NSClosableWindowMask];
747 [self setStyleMask:newStyle];
749 // -setStyleMask: resets the firstResponder to the window. Set it
750 // back to the content view.
751 if ([[self contentView] acceptsFirstResponder])
752 [self makeFirstResponder:[self contentView]];
754 [self adjustFullScreenBehavior:[self collectionBehavior]];
756 if ([[self title] length] == 0 && [title length] > 0)
757 [self setTitle:title];
760 resizable = wf->resizable;
761 [self adjustFeaturesForState];
762 [self setHasShadow:wf->shadow];
765 // Indicates if the window would be visible if the app were not hidden.
766 - (BOOL) wouldBeVisible
768 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
773 return [self wouldBeVisible] || [self isMiniaturized];
776 - (NSInteger) minimumLevelForActive:(BOOL)active
780 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
781 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
782 level = NSFloatingWindowLevel;
784 level = NSNormalWindowLevel;
790 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
792 if (captured || fullscreen)
795 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
797 level = NSStatusWindowLevel + 1;
807 - (void) postDidUnminimizeEvent
811 /* Coalesce events by discarding any previous ones still in the queue. */
812 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
815 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
816 [queue postEvent:event];
817 macdrv_release_event(event);
820 - (void) sendResizeStartQuery
822 macdrv_query* query = macdrv_create_query();
823 query->type = QUERY_RESIZE_START;
824 query->window = (macdrv_window)[self retain];
826 [self.queue query:query timeout:0.3];
827 macdrv_release_query(query);
830 - (void) setMacDrvState:(const struct macdrv_window_state*)state
832 NSWindowCollectionBehavior behavior;
834 self.disabled = state->disabled;
835 self.noActivate = state->no_activate;
837 if (self.floating != state->floating)
839 self.floating = state->floating;
842 // Became floating. If child of non-floating window, make that
843 // relationship latent.
844 WineWindow* parent = (WineWindow*)[self parentWindow];
845 if (parent && !parent.floating)
846 [self becameIneligibleChild];
850 // Became non-floating. If parent of floating children, make that
851 // relationship latent.
853 for (child in [self childWineWindows])
856 [child becameIneligibleChild];
860 // Check our latent relationships. If floating status was the only
861 // reason they were latent, then make them active.
862 if ([self isVisible])
863 [self becameEligibleParentOrChild];
865 [[WineApplicationController sharedController] adjustWindowLevels];
868 if (state->minimized_valid)
870 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
872 pendingMinimize = FALSE;
873 if (state->minimized && ![self isMiniaturized])
875 if ([self wouldBeVisible])
877 if ([self styleMask] & NSFullScreenWindowMask)
879 [self postDidUnminimizeEvent];
880 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
884 [super miniaturize:nil];
885 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
886 event_mask_for_type(WINDOW_GOT_FOCUS) |
887 event_mask_for_type(WINDOW_LOST_FOCUS);
891 pendingMinimize = TRUE;
893 else if (!state->minimized && [self isMiniaturized])
895 ignore_windowDeminiaturize = TRUE;
896 [self deminiaturize:nil];
897 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
901 [queue discardEventsMatchingMask:discard forWindow:self];
904 if (state->maximized != maximized)
906 maximized = state->maximized;
907 [self adjustFeaturesForState];
909 if (!maximized && [self inLiveResize])
910 [self sendResizeStartQuery];
913 behavior = NSWindowCollectionBehaviorDefault;
914 if (state->excluded_by_expose)
915 behavior |= NSWindowCollectionBehaviorTransient;
917 behavior |= NSWindowCollectionBehaviorManaged;
918 if (state->excluded_by_cycle)
920 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
921 if ([self isOrderedIn])
922 [NSApp removeWindowsItem:self];
926 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
927 if ([self isOrderedIn])
928 [NSApp addWindowsItem:self title:[self title] filename:NO];
930 [self adjustFullScreenBehavior:behavior];
933 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
935 BOOL reordered = FALSE;
937 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
939 if ([self level] > [child level])
940 [child setLevel:[self level]];
941 [self addChildWindow:child ordered:NSWindowAbove];
942 [latentChildWindows removeObjectIdenticalTo:child];
943 child.latentParentWindow = nil;
948 if (!latentChildWindows)
949 latentChildWindows = [[NSMutableArray alloc] init];
950 if (![latentChildWindows containsObject:child])
951 [latentChildWindows addObject:child];
952 child.latentParentWindow = self;
958 - (BOOL) addChildWineWindow:(WineWindow*)child
960 return [self addChildWineWindow:child assumeVisible:FALSE];
963 - (void) removeChildWineWindow:(WineWindow*)child
965 [self removeChildWindow:child];
966 if (child.latentParentWindow == self)
967 child.latentParentWindow = nil;
968 [latentChildWindows removeObjectIdenticalTo:child];
971 - (BOOL) becameEligibleParentOrChild
973 BOOL reordered = FALSE;
976 if (latentParentWindow.floating || !self.floating)
978 // If we aren't visible currently, we assume that we should be and soon
979 // will be. So, if the latent parent is visible that's enough to assume
980 // we can establish the parent-child relationship in Cocoa. That will
981 // actually make us visible, which is fine.
982 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
986 // Here, though, we may not actually be visible yet and adding a child
987 // won't make us visible. The caller will have to call this method
988 // again after actually making us visible.
989 if ([self isVisible] && (count = [latentChildWindows count]))
991 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
994 for (i = 0; i < count; i++)
996 WineWindow* child = [latentChildWindows objectAtIndex:i];
997 if ([child isVisible] && (self.floating || !child.floating))
999 if (child.latentParentWindow == self)
1001 if ([self level] > [child level])
1002 [child setLevel:[self level]];
1003 [self addChildWindow:child ordered:NSWindowAbove];
1004 child.latentParentWindow = nil;
1008 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
1009 [indexesToRemove addIndex:i];
1013 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1019 - (void) becameIneligibleChild
1021 WineWindow* parent = (WineWindow*)[self parentWindow];
1024 if (!parent->latentChildWindows)
1025 parent->latentChildWindows = [[NSMutableArray alloc] init];
1026 [parent->latentChildWindows insertObject:self atIndex:0];
1027 self.latentParentWindow = parent;
1028 [parent removeChildWindow:self];
1032 - (void) becameIneligibleParentOrChild
1034 NSArray* childWindows = [self childWineWindows];
1036 [self becameIneligibleChild];
1038 if ([childWindows count])
1042 for (child in childWindows)
1044 child.latentParentWindow = self;
1045 [self removeChildWindow:child];
1048 if (latentChildWindows)
1049 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1051 latentChildWindows = [childWindows mutableCopy];
1055 // Determine if, among Wine windows, this window is directly above or below
1056 // a given other Wine window with no other Wine window intervening.
1057 // Intervening non-Wine windows are ignored.
1058 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1060 NSNumber* windowNumber;
1061 NSNumber* otherWindowNumber;
1062 NSArray* windowNumbers;
1063 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1065 if (![self isVisible] || ![otherWindow isVisible])
1068 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1069 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1070 windowNumbers = [[self class] windowNumbersWithOptions:0];
1071 windowIndex = [windowNumbers indexOfObject:windowNumber];
1072 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1074 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1077 if (orderingMode == NSWindowAbove)
1079 lowIndex = windowIndex;
1080 highIndex = otherWindowIndex;
1082 else if (orderingMode == NSWindowBelow)
1084 lowIndex = otherWindowIndex;
1085 highIndex = windowIndex;
1090 if (highIndex <= lowIndex)
1093 for (i = lowIndex + 1; i < highIndex; i++)
1095 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1096 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1097 if ([interveningWindow isKindOfClass:[WineWindow class]])
1104 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1106 NSMutableArray* windowNumbers;
1107 NSNumber* childWindowNumber;
1108 NSUInteger otherIndex, limit;
1109 NSArray* origChildren;
1110 NSMutableArray* children;
1112 // Get the z-order from the window server and modify it to reflect the
1113 // requested window ordering.
1114 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1115 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1116 [windowNumbers removeObject:childWindowNumber];
1117 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1118 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1120 // Get our child windows and sort them in the reverse of the desired
1121 // z-order (back-to-front).
1122 origChildren = [self childWineWindows];
1123 children = [[origChildren mutableCopy] autorelease];
1124 [children sortWithOptions:NSSortStable
1125 usingComparator:^NSComparisonResult(id obj1, id obj2){
1126 NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1127 NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1128 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1129 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1130 if (index1 == NSNotFound)
1132 if (index2 == NSNotFound)
1133 return NSOrderedSame;
1135 return NSOrderedAscending;
1137 else if (index2 == NSNotFound)
1138 return NSOrderedDescending;
1139 else if (index1 < index2)
1140 return NSOrderedDescending;
1141 else if (index2 < index1)
1142 return NSOrderedAscending;
1144 return NSOrderedSame;
1147 // If the current and desired children arrays match up to a point, leave
1148 // those matching children alone.
1149 limit = MIN([origChildren count], [children count]);
1150 for (otherIndex = 0; otherIndex < limit; otherIndex++)
1152 if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1155 [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1157 // Remove all of the child windows and re-add them back-to-front so they
1158 // are in the desired order.
1159 for (other in children)
1160 [self removeChildWindow:other];
1161 for (other in children)
1162 [self addChildWindow:other ordered:NSWindowAbove];
1165 /* Returns whether or not the window was ordered in, which depends on if
1166 its frame intersects any screen. */
1167 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1169 WineApplicationController* controller = [WineApplicationController sharedController];
1170 if (![self isMiniaturized])
1172 BOOL needAdjustWindowLevels = FALSE;
1175 [controller transformProcessToForeground];
1177 wasVisible = [self isVisible];
1180 [NSApp activateIgnoringOtherApps:YES];
1182 NSDisableScreenUpdates();
1184 if ([self becameEligibleParentOrChild])
1185 needAdjustWindowLevels = TRUE;
1189 WineWindow* other = [prev isVisible] ? prev : next;
1190 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1192 if (![self isOrdered:orderingMode relativeTo:other])
1194 WineWindow* parent = (WineWindow*)[self parentWindow];
1195 WineWindow* otherParent = (WineWindow*)[other parentWindow];
1197 // This window level may not be right for this window based
1198 // on floating-ness, fullscreen-ness, etc. But we set it
1199 // temporarily to allow us to order the windows properly.
1200 // Then the levels get fixed by -adjustWindowLevels.
1201 if ([self level] != [other level])
1202 [self setLevel:[other level]];
1203 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1205 // The above call to -[NSWindow orderWindow:relativeTo:] won't
1206 // reorder windows which are both children of the same parent
1207 // relative to each other, so do that separately.
1208 if (parent && parent == otherParent)
1209 [parent order:orderingMode childWindow:self relativeTo:other];
1211 needAdjustWindowLevels = TRUE;
1216 // Again, temporarily set level to make sure we can order to
1218 next = [controller frontWineWindow];
1219 if (next && [self level] < [next level])
1220 [self setLevel:[next level]];
1221 [self orderFront:nil];
1222 needAdjustWindowLevels = TRUE;
1225 if ([self becameEligibleParentOrChild])
1226 needAdjustWindowLevels = TRUE;
1228 if (needAdjustWindowLevels)
1230 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1231 [controller updateFullscreenWindows];
1232 [controller adjustWindowLevels];
1235 if (pendingMinimize)
1237 [super miniaturize:nil];
1238 pendingMinimize = FALSE;
1241 NSEnableScreenUpdates();
1243 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1244 Generate a frame-changed event just in case. The back end will ignore
1245 it if nothing actually changed. */
1246 [self windowDidResize:nil];
1248 if (![self isExcludedFromWindowsMenu])
1249 [NSApp addWindowsItem:self title:[self title] filename:NO];
1255 WineApplicationController* controller = [WineApplicationController sharedController];
1256 BOOL wasVisible = [self isVisible];
1257 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1259 if ([self isMiniaturized])
1260 pendingMinimize = TRUE;
1262 [self becameIneligibleParentOrChild];
1263 if ([self isMiniaturized])
1267 fakingClose = FALSE;
1270 [self orderOut:nil];
1271 savedVisibleState = FALSE;
1272 if (wasVisible && wasOnActiveSpace && fullscreen)
1273 [controller updateFullscreenWindows];
1274 [controller adjustWindowLevels];
1275 [NSApp removeWindowsItem:self];
1277 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1278 event_mask_for_type(WINDOW_GOT_FOCUS) |
1279 event_mask_for_type(WINDOW_LOST_FOCUS) |
1280 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1281 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1282 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1286 - (void) updateFullscreen
1288 NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1289 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1291 if (nowFullscreen != fullscreen)
1293 WineApplicationController* controller = [WineApplicationController sharedController];
1295 fullscreen = nowFullscreen;
1296 if ([self isVisible] && [self isOnActiveSpace])
1297 [controller updateFullscreenWindows];
1299 [controller adjustWindowLevels];
1303 - (void) setFrameFromWine:(NSRect)contentRect
1305 /* Origin is (left, top) in a top-down space. Need to convert it to
1306 (left, bottom) in a bottom-up space. */
1307 [[WineApplicationController sharedController] flipRect:&contentRect];
1309 /* The back end is establishing a new window size and position. It's
1310 not interested in any stale events regarding those that may be sitting
1312 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1315 if (!NSIsEmptyRect(contentRect))
1317 NSRect frame, oldFrame;
1319 oldFrame = [self frame];
1320 frame = [self frameRectForContentRect:contentRect];
1321 if (!NSEqualRects(frame, oldFrame))
1323 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1324 BOOL needEnableScreenUpdates = FALSE;
1326 if ([self preventResizing])
1328 // Allow the following calls to -setFrame:display: to work even
1329 // if they would violate the content size constraints. This
1330 // shouldn't be necessary since the content size constraints are
1331 // documented to not constrain that method, but it seems to be.
1332 [self setContentMinSize:NSZeroSize];
1333 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1336 if (equalSizes && [[self childWineWindows] count])
1338 // If we change the window frame such that the origin moves
1339 // but the size doesn't change, then Cocoa moves child
1340 // windows with the parent. We don't want that so we fake
1341 // a change of the size and then change it back.
1342 NSRect bogusFrame = frame;
1343 bogusFrame.size.width++;
1345 NSDisableScreenUpdates();
1346 needEnableScreenUpdates = TRUE;
1348 ignore_windowResize = TRUE;
1349 [self setFrame:bogusFrame display:NO];
1350 ignore_windowResize = FALSE;
1353 [self setFrame:frame display:YES];
1354 if ([self preventResizing])
1356 [self setContentMinSize:contentRect.size];
1357 [self setContentMaxSize:contentRect.size];
1360 if (needEnableScreenUpdates)
1361 NSEnableScreenUpdates();
1364 [self updateColorSpace];
1366 if (!enteringFullScreen &&
1367 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1368 nonFullscreenFrame = frame;
1370 [self updateFullscreen];
1372 if ([self isOrderedIn])
1374 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1375 event. The back end will ignore it if nothing actually changed. */
1376 [self windowDidResize:nil];
1382 - (void) setMacDrvParentWindow:(WineWindow*)parent
1384 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1385 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1387 [oldParent removeChildWineWindow:self];
1388 [latentParentWindow removeChildWineWindow:self];
1389 if ([parent addChildWineWindow:self])
1390 [[WineApplicationController sharedController] adjustWindowLevels];
1394 - (void) setDisabled:(BOOL)newValue
1396 if (disabled != newValue)
1398 disabled = newValue;
1399 [self adjustFeaturesForState];
1403 - (BOOL) needsTransparency
1405 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1408 - (void) checkTransparency
1410 if (![self isOpaque] && !self.needsTransparency)
1412 self.shapeChangedSinceLastDraw = TRUE;
1413 [[self contentView] setNeedsDisplay:YES];
1414 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1415 [self setOpaque:YES];
1417 else if ([self isOpaque] && self.needsTransparency)
1419 self.shapeChangedSinceLastDraw = TRUE;
1420 [[self contentView] setNeedsDisplay:YES];
1421 [self setBackgroundColor:[NSColor clearColor]];
1422 [self setOpaque:NO];
1426 - (void) setShape:(NSBezierPath*)newShape
1428 if (shape == newShape) return;
1432 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1436 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1438 shape = [newShape copy];
1439 self.shapeChangedSinceLastDraw = TRUE;
1441 [self checkTransparency];
1444 - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1446 if (newTimer != liveResizeDisplayTimer)
1448 [liveResizeDisplayTimer invalidate];
1449 [liveResizeDisplayTimer release];
1450 liveResizeDisplayTimer = [newTimer retain];
1454 - (void) makeFocused:(BOOL)activate
1458 [[WineApplicationController sharedController] transformProcessToForeground];
1459 [NSApp activateIgnoringOtherApps:YES];
1462 causing_becomeKeyWindow = self;
1463 [self makeKeyWindow];
1464 causing_becomeKeyWindow = nil;
1466 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1467 event_mask_for_type(WINDOW_LOST_FOCUS)
1471 - (void) postKey:(uint16_t)keyCode
1472 pressed:(BOOL)pressed
1473 modifiers:(NSUInteger)modifiers
1474 event:(NSEvent*)theEvent
1476 macdrv_event* event;
1478 WineApplicationController* controller = [WineApplicationController sharedController];
1480 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1481 event->key.keycode = keyCode;
1482 event->key.modifiers = modifiers;
1483 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1485 if ((cgevent = [theEvent CGEvent]))
1487 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1488 kCGKeyboardEventKeyboardType);
1489 if (keyboardType != controller.keyboardType)
1491 controller.keyboardType = keyboardType;
1492 [controller keyboardSelectionDidChange];
1496 [queue postEvent:event];
1498 macdrv_release_event(event);
1500 [controller noteKey:keyCode pressed:pressed];
1503 - (void) postKeyEvent:(NSEvent *)theEvent
1505 [self flagsChanged:theEvent];
1506 [self postKey:[theEvent keyCode]
1507 pressed:[theEvent type] == NSKeyDown
1508 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1512 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1514 savedContentMinSize = minSize;
1515 savedContentMaxSize = maxSize;
1516 if (![self preventResizing])
1518 [self setContentMinSize:minSize];
1519 [self setContentMaxSize:maxSize];
1523 - (WineWindow*) ancestorWineWindow
1525 WineWindow* ancestor = self;
1528 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1529 if ([parent isKindOfClass:[WineWindow class]])
1537 - (void) postBroughtForwardEvent
1539 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1540 [queue postEvent:event];
1541 macdrv_release_event(event);
1544 - (void) updateForCursorClipping
1546 [self adjustFeaturesForState];
1549 - (void) endWindowDragging
1553 if (draggingPhase == 3)
1555 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_END, self);
1556 [queue postEvent:event];
1557 macdrv_release_event(event);
1561 [[WineApplicationController sharedController] window:self isBeingDragged:NO];
1567 * ---------- NSWindow method overrides ----------
1569 - (BOOL) canBecomeKeyWindow
1571 if (causing_becomeKeyWindow == self) return YES;
1572 if (self.disabled || self.noActivate) return NO;
1573 return [self isKeyWindow];
1576 - (BOOL) canBecomeMainWindow
1578 return [self canBecomeKeyWindow];
1581 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1583 // If a window is sized to completely cover a screen, then it's in
1584 // full-screen mode. In that case, we don't allow NSWindow to constrain
1586 NSArray* screens = [NSScreen screens];
1587 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1588 if (!screen_covered_by_rect(contentRect, screens) &&
1589 frame_intersects_screens(frameRect, screens))
1590 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1594 - (BOOL) isExcludedFromWindowsMenu
1596 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1599 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1601 BOOL ret = [super validateMenuItem:menuItem];
1603 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1604 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1605 if ([menuItem action] == @selector(toggleFullScreen:) && (self.disabled || maximized))
1611 /* We don't call this. It's the action method of the items in the Window menu. */
1612 - (void) makeKeyAndOrderFront:(id)sender
1614 if ([self isMiniaturized])
1615 [self deminiaturize:nil];
1616 [self orderBelow:nil orAbove:nil activate:NO];
1617 [[self ancestorWineWindow] postBroughtForwardEvent];
1619 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1620 [[WineApplicationController sharedController] windowGotFocus:self];
1623 - (void) sendEvent:(NSEvent*)event
1625 NSEventType type = event.type;
1627 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1628 interface control. For example, Control-Tab switches focus among
1629 views. We want to bypass that feature, so directly route key-down
1630 events to -keyDown:. */
1631 if (type == NSKeyDown)
1632 [[self firstResponder] keyDown:event];
1635 if (!draggingPhase && maximized && ![self isMovable] &&
1636 ![self allowsMovingWithMaximized:YES] && [self allowsMovingWithMaximized:NO] &&
1637 type == NSLeftMouseDown && (self.styleMask & NSTitledWindowMask))
1639 NSRect titleBar = self.frame;
1640 NSRect contentRect = [self contentRectForFrameRect:titleBar];
1641 titleBar.size.height = NSMaxY(titleBar) - NSMaxY(contentRect);
1642 titleBar.origin.y = NSMaxY(contentRect);
1644 dragStartPosition = [self convertBaseToScreen:event.locationInWindow];
1646 if (NSMouseInRect(dragStartPosition, titleBar, NO))
1648 static const NSWindowButton buttons[] = {
1649 NSWindowCloseButton,
1650 NSWindowMiniaturizeButton,
1652 NSWindowFullScreenButton,
1654 BOOL hitButton = NO;
1657 for (i = 0; i < sizeof(buttons) / sizeof(buttons[0]); i++)
1661 if (buttons[i] == NSWindowFullScreenButton && ![self respondsToSelector:@selector(toggleFullScreen:)])
1664 button = [self standardWindowButton:buttons[i]];
1665 if ([button hitTest:[button.superview convertPoint:event.locationInWindow fromView:nil]])
1675 dragWindowStartPosition = NSMakePoint(NSMinX(titleBar), NSMaxY(titleBar));
1676 [[WineApplicationController sharedController] window:self isBeingDragged:YES];
1680 else if (draggingPhase && (type == NSLeftMouseDragged || type == NSLeftMouseUp))
1682 if ([self isMovable])
1684 NSPoint point = [self convertBaseToScreen:event.locationInWindow];
1685 NSPoint newTopLeft = dragWindowStartPosition;
1687 newTopLeft.x += point.x - dragStartPosition.x;
1688 newTopLeft.y += point.y - dragStartPosition.y;
1690 if (draggingPhase == 2)
1692 macdrv_event* event = macdrv_create_event(WINDOW_DRAG_BEGIN, self);
1693 [queue postEvent:event];
1694 macdrv_release_event(event);
1699 [self setFrameTopLeftPoint:newTopLeft];
1701 else if (draggingPhase == 1 && type == NSLeftMouseDragged)
1703 macdrv_event* event;
1704 NSRect frame = [self contentRectForFrameRect:self.frame];
1706 [[WineApplicationController sharedController] flipRect:&frame];
1708 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1709 event->window_restore_requested.keep_frame = TRUE;
1710 event->window_restore_requested.frame = NSRectToCGRect(frame);
1711 [queue postEvent:event];
1712 macdrv_release_event(event);
1717 if (type == NSLeftMouseUp)
1718 [self endWindowDragging];
1721 [super sendEvent:event];
1725 - (void) miniaturize:(id)sender
1727 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1728 [queue postEvent:event];
1729 macdrv_release_event(event);
1732 - (void) toggleFullScreen:(id)sender
1734 if (!self.disabled && !maximized)
1735 [super toggleFullScreen:sender];
1738 - (NSArray*) childWineWindows
1740 NSArray* childWindows = self.childWindows;
1741 NSIndexSet* indexes = [childWindows indexesOfObjectsPassingTest:^BOOL(id child, NSUInteger idx, BOOL *stop){
1742 return [child isKindOfClass:[WineWindow class]];
1744 return [childWindows objectsAtIndexes:indexes];
1747 // We normally use the generic/calibrated RGB color space for the window,
1748 // rather than the device color space, to avoid expensive color conversion
1749 // which slows down drawing. However, for windows displaying OpenGL, having
1750 // a different color space than the screen greatly reduces frame rates, often
1751 // limiting it to the display refresh rate.
1753 // To avoid this, we switch back to the screen color space whenever the
1754 // window is covered by a view with an attached OpenGL context.
1755 - (void) updateColorSpace
1757 NSRect contentRect = [[self contentView] frame];
1758 BOOL coveredByGLView = FALSE;
1759 for (WineContentView* view in [[self contentView] subviews])
1761 if ([view hasGLContext])
1763 NSRect frame = [view convertRect:[view bounds] toView:nil];
1764 if (NSContainsRect(frame, contentRect))
1766 coveredByGLView = TRUE;
1772 if (coveredByGLView)
1773 [self setColorSpace:nil];
1775 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1780 * ---------- NSResponder method overrides ----------
1782 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1784 - (void) flagsChanged:(NSEvent *)theEvent
1786 static const struct {
1790 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1791 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1792 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1793 { NX_DEVICELCTLKEYMASK, kVK_Control },
1794 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1795 { NX_DEVICELALTKEYMASK, kVK_Option },
1796 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1797 { NX_DEVICELCMDKEYMASK, kVK_Command },
1798 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1801 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1803 int i, last_changed;
1805 fix_device_modifiers_by_generic(&modifierFlags);
1806 changed = modifierFlags ^ lastModifierFlags;
1809 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1810 if (changed & modifiers[i].mask)
1813 for (i = 0; i <= last_changed; i++)
1815 if (changed & modifiers[i].mask)
1817 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1819 if (i == last_changed)
1820 lastModifierFlags = modifierFlags;
1823 lastModifierFlags ^= modifiers[i].mask;
1824 fix_generic_modifiers_by_device(&lastModifierFlags);
1827 // Caps lock generates one event for each press-release action.
1828 // We need to simulate a pair of events for each actual event.
1829 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1831 [self postKey:modifiers[i].keycode
1833 modifiers:lastModifierFlags
1834 event:(NSEvent*)theEvent];
1838 [self postKey:modifiers[i].keycode
1840 modifiers:lastModifierFlags
1841 event:(NSEvent*)theEvent];
1846 - (void) applicationWillHide
1848 savedVisibleState = [self isVisible];
1851 - (void) applicationDidUnhide
1853 if ([self isVisible])
1854 [self becameEligibleParentOrChild];
1859 * ---------- NSWindowDelegate methods ----------
1861 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1863 macdrv_query* query;
1866 query = macdrv_create_query();
1867 query->type = QUERY_MIN_MAX_INFO;
1868 query->window = (macdrv_window)[self retain];
1869 [self.queue query:query timeout:0.5];
1870 macdrv_release_query(query);
1872 size = [self contentMaxSize];
1873 if (proposedSize.width < size.width)
1874 size.width = proposedSize.width;
1875 if (proposedSize.height < size.height)
1876 size.height = proposedSize.height;
1880 - (void)windowDidBecomeKey:(NSNotification *)notification
1882 WineApplicationController* controller = [WineApplicationController sharedController];
1883 NSEvent* event = [controller lastFlagsChanged];
1885 [self flagsChanged:event];
1887 if (causing_becomeKeyWindow == self) return;
1889 [controller windowGotFocus:self];
1892 - (void)windowDidDeminiaturize:(NSNotification *)notification
1894 WineApplicationController* controller = [WineApplicationController sharedController];
1896 if (!ignore_windowDeminiaturize)
1897 [self postDidUnminimizeEvent];
1898 ignore_windowDeminiaturize = FALSE;
1900 [self becameEligibleParentOrChild];
1902 if (fullscreen && [self isOnActiveSpace])
1903 [controller updateFullscreenWindows];
1904 [controller adjustWindowLevels];
1906 if (![self parentWindow])
1907 [self postBroughtForwardEvent];
1909 if (!self.disabled && !self.noActivate)
1911 causing_becomeKeyWindow = self;
1912 [self makeKeyWindow];
1913 causing_becomeKeyWindow = nil;
1914 [controller windowGotFocus:self];
1917 [self windowDidResize:notification];
1920 - (void) windowDidEndLiveResize:(NSNotification *)notification
1924 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1925 [queue postEvent:event];
1926 macdrv_release_event(event);
1929 self.liveResizeDisplayTimer = nil;
1932 - (void) windowDidEnterFullScreen:(NSNotification*)notification
1934 enteringFullScreen = FALSE;
1935 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1938 - (void) windowDidExitFullScreen:(NSNotification*)notification
1940 exitingFullScreen = FALSE;
1941 [self setFrame:nonFullscreenFrame display:YES animate:NO];
1942 [self windowDidResize:nil];
1945 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1947 enteringFullScreen = FALSE;
1948 enteredFullScreenTime = 0;
1951 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1953 exitingFullScreen = FALSE;
1954 [self windowDidResize:nil];
1957 - (void)windowDidMiniaturize:(NSNotification *)notification
1959 if (fullscreen && [self isOnActiveSpace])
1960 [[WineApplicationController sharedController] updateFullscreenWindows];
1963 - (void)windowDidMove:(NSNotification *)notification
1965 [self windowDidResize:notification];
1968 - (void)windowDidResignKey:(NSNotification *)notification
1970 macdrv_event* event;
1972 if (causing_becomeKeyWindow) return;
1974 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1975 [queue postEvent:event];
1976 macdrv_release_event(event);
1979 - (void)windowDidResize:(NSNotification *)notification
1981 macdrv_event* event;
1982 NSRect frame = [self frame];
1984 if ([self inLiveResize])
1986 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1987 resizingFromLeft = TRUE;
1988 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1989 resizingFromTop = TRUE;
1992 frame = [self contentRectForFrameRect:frame];
1994 if (ignore_windowResize || exitingFullScreen) return;
1996 if ([self preventResizing])
1998 [self setContentMinSize:frame.size];
1999 [self setContentMaxSize:frame.size];
2002 [[WineApplicationController sharedController] flipRect:&frame];
2004 /* Coalesce events by discarding any previous ones still in the queue. */
2005 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
2008 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
2009 event->window_frame_changed.frame = NSRectToCGRect(frame);
2010 event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
2011 event->window_frame_changed.in_resize = [self inLiveResize];
2012 [queue postEvent:event];
2013 macdrv_release_event(event);
2015 [[[self contentView] inputContext] invalidateCharacterCoordinates];
2016 [self updateFullscreen];
2019 - (BOOL)windowShouldClose:(id)sender
2021 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
2022 [queue postEvent:event];
2023 macdrv_release_event(event);
2027 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
2031 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2032 [queue postEvent:event];
2033 macdrv_release_event(event);
2036 else if (!resizable)
2038 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
2039 [queue postEvent:event];
2040 macdrv_release_event(event);
2047 - (void) windowWillClose:(NSNotification*)notification
2051 if (fakingClose) return;
2052 if (latentParentWindow)
2054 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
2055 self.latentParentWindow = nil;
2058 for (child in latentChildWindows)
2060 if (child.latentParentWindow == self)
2061 child.latentParentWindow = nil;
2063 [latentChildWindows removeAllObjects];
2066 - (void) windowWillEnterFullScreen:(NSNotification*)notification
2068 enteringFullScreen = TRUE;
2069 nonFullscreenFrame = [self frame];
2072 - (void) windowWillExitFullScreen:(NSNotification*)notification
2074 exitingFullScreen = TRUE;
2077 - (void)windowWillMiniaturize:(NSNotification *)notification
2079 [self becameIneligibleParentOrChild];
2082 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
2084 if ([self inLiveResize])
2087 return self.frame.size;
2090 macdrv_query* query;
2092 rect = [self frame];
2093 if (resizingFromLeft)
2094 rect.origin.x = NSMaxX(rect) - frameSize.width;
2095 if (!resizingFromTop)
2096 rect.origin.y = NSMaxY(rect) - frameSize.height;
2097 rect.size = frameSize;
2098 rect = [self contentRectForFrameRect:rect];
2099 [[WineApplicationController sharedController] flipRect:&rect];
2101 query = macdrv_create_query();
2102 query->type = QUERY_RESIZE_SIZE;
2103 query->window = (macdrv_window)[self retain];
2104 query->resize_size.rect = NSRectToCGRect(rect);
2105 query->resize_size.from_left = resizingFromLeft;
2106 query->resize_size.from_top = resizingFromTop;
2108 if ([self.queue query:query timeout:0.1])
2110 rect = NSRectFromCGRect(query->resize_size.rect);
2111 rect = [self frameRectForContentRect:rect];
2112 frameSize = rect.size;
2115 macdrv_release_query(query);
2121 - (void) windowWillStartLiveResize:(NSNotification *)notification
2123 [self endWindowDragging];
2127 macdrv_event* event;
2128 NSRect frame = [self contentRectForFrameRect:self.frame];
2130 [[WineApplicationController sharedController] flipRect:&frame];
2132 event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
2133 event->window_restore_requested.keep_frame = TRUE;
2134 event->window_restore_requested.frame = NSRectToCGRect(frame);
2135 [queue postEvent:event];
2136 macdrv_release_event(event);
2139 [self sendResizeStartQuery];
2141 frameAtResizeStart = [self frame];
2142 resizingFromLeft = resizingFromTop = FALSE;
2144 // There's a strange restriction in window redrawing during Cocoa-
2145 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
2146 // that happen synchronously when Cocoa tells us that our window size
2147 // has changed or asynchronously in a short interval thereafter provoke
2148 // the window to redraw. Calls to those methods that happen asynchronously
2149 // a half second or more after the last change of the window size aren't
2150 // heeded until the next resize-related user event (e.g. mouse movement).
2152 // Wine often has a significant delay between when it's been told that
2153 // the window has changed size and when it can flush completed drawing.
2154 // So, our windows would get stuck with incomplete drawing for as long
2155 // as the user holds the mouse button down and doesn't move it.
2157 // We address this by "manually" asking our windows to check if they need
2158 // redrawing every so often (during live resize only).
2159 self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
2161 selector:@selector(displayIfNeeded)
2164 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
2165 forMode:NSRunLoopCommonModes];
2168 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2170 macdrv_query* query;
2171 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2174 query = macdrv_create_query();
2175 query->type = QUERY_MIN_MAX_INFO;
2176 query->window = (macdrv_window)[self retain];
2177 [self.queue query:query timeout:0.5];
2178 macdrv_release_query(query);
2180 currentContentRect = [self contentRectForFrameRect:[self frame]];
2181 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2183 maxSize = [self contentMaxSize];
2184 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2185 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2187 // Try to keep the top-left corner where it is.
2188 newContentRect.origin.x = NSMinX(currentContentRect);
2189 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2191 // If that pushes the bottom or right off the screen, pull it up and to the left.
2192 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2193 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2194 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2195 if (NSMinY(newContentRect) < NSMinY(screenRect))
2196 newContentRect.origin.y = NSMinY(screenRect);
2198 // If that pushes the top or left off the screen, push it down and the right
2199 // again. Do this last because the top-left corner is more important than the
2201 if (NSMinX(newContentRect) < NSMinX(screenRect))
2202 newContentRect.origin.x = NSMinX(screenRect);
2203 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2204 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2206 return [self frameRectForContentRect:newContentRect];
2211 * ---------- NSPasteboardOwner methods ----------
2213 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2215 macdrv_query* query = macdrv_create_query();
2216 query->type = QUERY_PASTEBOARD_DATA;
2217 query->window = (macdrv_window)[self retain];
2218 query->pasteboard_data.type = (CFStringRef)[type copy];
2220 [self.queue query:query timeout:3];
2221 macdrv_release_query(query);
2226 * ---------- NSDraggingDestination methods ----------
2228 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2230 return [self draggingUpdated:sender];
2233 - (void) draggingExited:(id <NSDraggingInfo>)sender
2235 // This isn't really a query. We don't need any response. However, it
2236 // has to be processed in a similar manner as the other drag-and-drop
2237 // queries in order to maintain the proper order of operations.
2238 macdrv_query* query = macdrv_create_query();
2239 query->type = QUERY_DRAG_EXITED;
2240 query->window = (macdrv_window)[self retain];
2242 [self.queue query:query timeout:0.1];
2243 macdrv_release_query(query);
2246 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2248 NSDragOperation ret;
2249 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2250 NSPasteboard* pb = [sender draggingPasteboard];
2252 macdrv_query* query = macdrv_create_query();
2253 query->type = QUERY_DRAG_OPERATION;
2254 query->window = (macdrv_window)[self retain];
2255 query->drag_operation.x = pt.x;
2256 query->drag_operation.y = pt.y;
2257 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2258 query->drag_operation.accepted_op = NSDragOperationNone;
2259 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2261 [self.queue query:query timeout:3];
2262 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2263 macdrv_release_query(query);
2268 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2271 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2272 NSPasteboard* pb = [sender draggingPasteboard];
2274 macdrv_query* query = macdrv_create_query();
2275 query->type = QUERY_DRAG_DROP;
2276 query->window = (macdrv_window)[self retain];
2277 query->drag_drop.x = pt.x;
2278 query->drag_drop.y = pt.y;
2279 query->drag_drop.op = [sender draggingSourceOperationMask];
2280 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2282 [self.queue query:query timeout:3 * 60 processEvents:YES];
2283 ret = query->status;
2284 macdrv_release_query(query);
2289 - (BOOL) wantsPeriodicDraggingUpdates
2297 /***********************************************************************
2298 * macdrv_create_cocoa_window
2300 * Create a Cocoa window with the given content frame and features (e.g.
2301 * title bar, close box, etc.).
2303 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2304 CGRect frame, void* hwnd, macdrv_event_queue queue)
2306 __block WineWindow* window;
2309 window = [[WineWindow createWindowWithFeatures:wf
2310 windowFrame:NSRectFromCGRect(frame)
2312 queue:(WineEventQueue*)queue] retain];
2315 return (macdrv_window)window;
2318 /***********************************************************************
2319 * macdrv_destroy_cocoa_window
2321 * Destroy a Cocoa window.
2323 void macdrv_destroy_cocoa_window(macdrv_window w)
2325 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2326 WineWindow* window = (WineWindow*)w;
2329 [window doOrderOut];
2332 [window.queue discardEventsMatchingMask:-1 forWindow:window];
2338 /***********************************************************************
2339 * macdrv_get_window_hwnd
2341 * Get the hwnd that was set for the window at creation.
2343 void* macdrv_get_window_hwnd(macdrv_window w)
2345 WineWindow* window = (WineWindow*)w;
2349 /***********************************************************************
2350 * macdrv_set_cocoa_window_features
2352 * Update a Cocoa window's features.
2354 void macdrv_set_cocoa_window_features(macdrv_window w,
2355 const struct macdrv_window_features* wf)
2357 WineWindow* window = (WineWindow*)w;
2360 [window setWindowFeatures:wf];
2364 /***********************************************************************
2365 * macdrv_set_cocoa_window_state
2367 * Update a Cocoa window's state.
2369 void macdrv_set_cocoa_window_state(macdrv_window w,
2370 const struct macdrv_window_state* state)
2372 WineWindow* window = (WineWindow*)w;
2375 [window setMacDrvState:state];
2379 /***********************************************************************
2380 * macdrv_set_cocoa_window_title
2382 * Set a Cocoa window's title.
2384 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2387 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2388 WineWindow* window = (WineWindow*)w;
2389 NSString* titleString;
2392 titleString = [NSString stringWithCharacters:title length:length];
2395 OnMainThreadAsync(^{
2396 [window setTitle:titleString];
2397 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2398 [NSApp changeWindowsItem:window title:titleString filename:NO];
2404 /***********************************************************************
2405 * macdrv_order_cocoa_window
2407 * Reorder a Cocoa window relative to other windows. If prev is
2408 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
2409 * it is ordered above that window. Otherwise, it is ordered to the
2412 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2413 macdrv_window n, int activate)
2415 WineWindow* window = (WineWindow*)w;
2416 WineWindow* prev = (WineWindow*)p;
2417 WineWindow* next = (WineWindow*)n;
2419 OnMainThreadAsync(^{
2420 [window orderBelow:prev
2424 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2426 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2430 /***********************************************************************
2431 * macdrv_hide_cocoa_window
2433 * Hides a Cocoa window.
2435 void macdrv_hide_cocoa_window(macdrv_window w)
2437 WineWindow* window = (WineWindow*)w;
2440 [window doOrderOut];
2444 /***********************************************************************
2445 * macdrv_set_cocoa_window_frame
2447 * Move a Cocoa window.
2449 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2451 WineWindow* window = (WineWindow*)w;
2454 [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2458 /***********************************************************************
2459 * macdrv_get_cocoa_window_frame
2461 * Gets the frame of a Cocoa window.
2463 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2465 WineWindow* window = (WineWindow*)w;
2470 frame = [window contentRectForFrameRect:[window frame]];
2471 [[WineApplicationController sharedController] flipRect:&frame];
2472 *out_frame = NSRectToCGRect(frame);
2476 /***********************************************************************
2477 * macdrv_set_cocoa_parent_window
2479 * Sets the parent window for a Cocoa window. If parent is NULL, clears
2480 * the parent window.
2482 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2484 WineWindow* window = (WineWindow*)w;
2487 [window setMacDrvParentWindow:(WineWindow*)parent];
2491 /***********************************************************************
2492 * macdrv_set_window_surface
2494 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2496 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2497 WineWindow* window = (WineWindow*)w;
2500 window.surface = surface;
2501 window.surface_mutex = mutex;
2507 /***********************************************************************
2508 * macdrv_window_needs_display
2510 * Mark a window as needing display in a specified rect (in non-client
2511 * area coordinates).
2513 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2515 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2516 WineWindow* window = (WineWindow*)w;
2518 OnMainThreadAsync(^{
2519 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2525 /***********************************************************************
2526 * macdrv_set_window_shape
2528 * Sets the shape of a Cocoa window from an array of rectangles. If
2529 * rects is NULL, resets the window's shape to its frame.
2531 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2533 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2534 WineWindow* window = (WineWindow*)w;
2537 if (!rects || !count)
2540 window.shapeData = nil;
2544 size_t length = sizeof(*rects) * count;
2545 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
2550 path = [NSBezierPath bezierPath];
2551 for (i = 0; i < count; i++)
2552 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2553 window.shape = path;
2554 window.shapeData = [NSData dataWithBytes:rects length:length];
2562 /***********************************************************************
2563 * macdrv_set_window_alpha
2565 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2567 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2568 WineWindow* window = (WineWindow*)w;
2570 [window setAlphaValue:alpha];
2575 /***********************************************************************
2576 * macdrv_set_window_color_key
2578 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2581 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2582 WineWindow* window = (WineWindow*)w;
2585 window.colorKeyed = TRUE;
2586 window.colorKeyRed = keyRed;
2587 window.colorKeyGreen = keyGreen;
2588 window.colorKeyBlue = keyBlue;
2589 [window checkTransparency];
2595 /***********************************************************************
2596 * macdrv_clear_window_color_key
2598 void macdrv_clear_window_color_key(macdrv_window w)
2600 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2601 WineWindow* window = (WineWindow*)w;
2604 window.colorKeyed = FALSE;
2605 [window checkTransparency];
2611 /***********************************************************************
2612 * macdrv_window_use_per_pixel_alpha
2614 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2616 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2617 WineWindow* window = (WineWindow*)w;
2620 window.usePerPixelAlpha = use_per_pixel_alpha;
2621 [window checkTransparency];
2627 /***********************************************************************
2628 * macdrv_give_cocoa_window_focus
2630 * Makes the Cocoa window "key" (gives it keyboard focus). This also
2631 * orders it front and, if its frame was not within the desktop bounds,
2632 * Cocoa will typically move it on-screen.
2634 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2636 WineWindow* window = (WineWindow*)w;
2639 [window makeFocused:activate];
2643 /***********************************************************************
2644 * macdrv_set_window_min_max_sizes
2646 * Sets the window's minimum and maximum content sizes.
2648 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2650 WineWindow* window = (WineWindow*)w;
2653 [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2657 /***********************************************************************
2658 * macdrv_create_view
2660 * Creates and returns a view in the specified rect of the window. The
2661 * caller is responsible for calling macdrv_dispose_view() on the view
2662 * when it is done with it.
2664 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2666 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2667 WineWindow* window = (WineWindow*)w;
2668 __block WineContentView* view;
2670 if (CGRectIsNull(rect)) rect = CGRectZero;
2673 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2675 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2676 [view setAutoresizesSubviews:NO];
2677 [nc addObserver:view
2678 selector:@selector(updateGLContexts)
2679 name:NSViewGlobalFrameDidChangeNotification
2681 [nc addObserver:view
2682 selector:@selector(updateGLContexts)
2683 name:NSApplicationDidChangeScreenParametersNotification
2685 [[window contentView] addSubview:view];
2686 [window updateColorSpace];
2690 return (macdrv_view)view;
2693 /***********************************************************************
2694 * macdrv_dispose_view
2696 * Destroys a view previously returned by macdrv_create_view.
2698 void macdrv_dispose_view(macdrv_view v)
2700 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2701 WineContentView* view = (WineContentView*)v;
2704 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2705 WineWindow* window = (WineWindow*)[view window];
2707 [nc removeObserver:view
2708 name:NSViewGlobalFrameDidChangeNotification
2710 [nc removeObserver:view
2711 name:NSApplicationDidChangeScreenParametersNotification
2713 [view removeFromSuperview];
2715 [window updateColorSpace];
2721 /***********************************************************************
2722 * macdrv_set_view_window_and_frame
2724 * Move a view to a new window and/or position within its window. If w
2725 * is NULL, leave the view in its current window and just change its
2728 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2730 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2731 WineContentView* view = (WineContentView*)v;
2732 WineWindow* window = (WineWindow*)w;
2734 if (CGRectIsNull(rect)) rect = CGRectZero;
2737 BOOL changedWindow = (window && window != [view window]);
2738 NSRect newFrame = NSRectFromCGRect(rect);
2739 NSRect oldFrame = [view frame];
2740 BOOL needUpdateWindowColorSpace = FALSE;
2744 WineWindow* oldWindow = (WineWindow*)[view window];
2745 [view removeFromSuperview];
2746 [oldWindow updateColorSpace];
2747 [[window contentView] addSubview:view];
2748 needUpdateWindowColorSpace = TRUE;
2751 if (!NSEqualRects(oldFrame, newFrame))
2754 [[view superview] setNeedsDisplayInRect:oldFrame];
2755 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2756 [view setFrameSize:newFrame.size];
2757 else if (NSEqualSizes(oldFrame.size, newFrame.size))
2758 [view setFrameOrigin:newFrame.origin];
2760 [view setFrame:newFrame];
2761 [view setNeedsDisplay:YES];
2762 needUpdateWindowColorSpace = TRUE;
2765 if (needUpdateWindowColorSpace)
2766 [(WineWindow*)[view window] updateColorSpace];
2772 /***********************************************************************
2773 * macdrv_add_view_opengl_context
2775 * Add an OpenGL context to the list being tracked for each view.
2777 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2779 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2780 WineContentView* view = (WineContentView*)v;
2781 WineOpenGLContext *context = (WineOpenGLContext*)c;
2784 [view addGLContext:context];
2790 /***********************************************************************
2791 * macdrv_remove_view_opengl_context
2793 * Add an OpenGL context to the list being tracked for each view.
2795 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2797 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2798 WineContentView* view = (WineContentView*)v;
2799 WineOpenGLContext *context = (WineOpenGLContext*)c;
2801 OnMainThreadAsync(^{
2802 [view removeGLContext:context];
2808 /***********************************************************************
2809 * macdrv_window_background_color
2811 * Returns the standard Mac window background color as a 32-bit value of
2812 * the form 0x00rrggbb.
2814 uint32_t macdrv_window_background_color(void)
2816 static uint32_t result;
2817 static dispatch_once_t once;
2819 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2820 // color spaces (RGB or grayscale). So, the only way to get RGB values out
2821 // of it is to draw with it.
2822 dispatch_once(&once, ^{
2824 unsigned char rgbx[4];
2825 unsigned char *planes = rgbx;
2826 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2833 colorSpaceName:NSCalibratedRGBColorSpace
2837 [NSGraphicsContext saveGraphicsState];
2838 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2839 [[NSColor windowBackgroundColor] set];
2840 NSRectFill(NSMakeRect(0, 0, 1, 1));
2841 [NSGraphicsContext restoreGraphicsState];
2843 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2850 /***********************************************************************
2851 * macdrv_send_text_input_event
2853 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2858 WineWindow* window = (WineWindow*)[NSApp keyWindow];
2859 if (![window isKindOfClass:[WineWindow class]])
2861 window = (WineWindow*)[NSApp mainWindow];
2862 if (![window isKindOfClass:[WineWindow class]])
2863 window = [[WineApplicationController sharedController] frontWineWindow];
2868 NSUInteger localFlags = flags;
2872 window.imeData = data;
2873 fix_device_modifiers_by_generic(&localFlags);
2875 // An NSEvent created with +keyEventWithType:... is internally marked
2876 // as synthetic and doesn't get sent through input methods. But one
2877 // created from a CGEvent doesn't have that problem.
2878 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2879 CGEventSetFlags(c, localFlags);
2880 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2881 event = [NSEvent eventWithCGEvent:c];
2884 window.commandDone = FALSE;
2885 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;