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 (nonatomic) BOOL shapeChangedSinceLastDraw;
186 @property (readonly, nonatomic) BOOL needsTransparency;
188 @property (nonatomic) BOOL colorKeyed;
189 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
190 @property (nonatomic) BOOL usePerPixelAlpha;
192 @property (assign, nonatomic) void* imeData;
193 @property (nonatomic) BOOL commandDone;
195 @property (retain, nonatomic) NSTimer* liveResizeDisplayTimer;
197 - (void) updateColorSpace;
199 - (BOOL) becameEligibleParentOrChild;
200 - (void) becameIneligibleChild;
205 @implementation WineContentView
209 [markedText release];
210 [glContexts release];
211 [pendingGlContexts release];
220 - (void) drawRect:(NSRect)rect
222 WineWindow* window = (WineWindow*)[self window];
224 for (WineOpenGLContext* context in pendingGlContexts)
226 if (!clearedGlSurface)
228 context.shouldClearToBlack = TRUE;
229 clearedGlSurface = TRUE;
231 context.needsUpdate = TRUE;
233 [glContexts addObjectsFromArray:pendingGlContexts];
234 [pendingGlContexts removeAllObjects];
236 if ([window contentView] != self)
239 if (window.surface && window.surface_mutex &&
240 !pthread_mutex_lock(window.surface_mutex))
245 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
247 CGContextRef context;
250 [window.shape addClip];
252 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
253 CGContextSetBlendMode(context, kCGBlendModeCopy);
255 for (i = 0; i < count; i++)
260 imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
261 image = create_surface_image(window.surface, &imageRect, FALSE);
265 if (window.colorKeyed)
267 CGImageRef maskedImage;
268 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
269 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
270 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
271 maskedImage = CGImageCreateWithMaskingColors(image, components);
274 CGImageRelease(image);
279 CGContextDrawImage(context, imageRect, image);
281 CGImageRelease(image);
286 pthread_mutex_unlock(window.surface_mutex);
289 // If the window may be transparent, then we have to invalidate the
290 // shadow every time we draw. Also, if this is the first time we've
291 // drawn since changing from transparent to opaque.
292 if (![window isOpaque] || window.shapeChangedSinceLastDraw)
294 window.shapeChangedSinceLastDraw = FALSE;
295 [window invalidateShadow];
299 - (void) addGLContext:(WineOpenGLContext*)context
302 glContexts = [[NSMutableArray alloc] init];
303 if (!pendingGlContexts)
304 pendingGlContexts = [[NSMutableArray alloc] init];
306 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
308 [glContexts addObject:context];
309 if (!clearedGlSurface)
311 context.shouldClearToBlack = TRUE;
312 clearedGlSurface = TRUE;
314 context.needsUpdate = TRUE;
318 [pendingGlContexts addObject:context];
319 [self setNeedsDisplay:YES];
322 [(WineWindow*)[self window] updateColorSpace];
325 - (void) removeGLContext:(WineOpenGLContext*)context
327 [glContexts removeObjectIdenticalTo:context];
328 [pendingGlContexts removeObjectIdenticalTo:context];
329 [(WineWindow*)[self window] updateColorSpace];
332 - (void) updateGLContexts
334 for (WineOpenGLContext* context in glContexts)
335 context.needsUpdate = TRUE;
338 - (BOOL) hasGLContext
340 return [glContexts count] || [pendingGlContexts count];
343 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
348 - (BOOL) preservesContentDuringLiveResize
350 // Returning YES from this tells Cocoa to keep our view's content during
351 // a Cocoa-driven resize. In theory, we're also supposed to override
352 // -setFrameSize: to mark exposed sections as needing redisplay, but
353 // user32 will take care of that in a roundabout way. This way, we don't
354 // redraw until the window surface is flushed.
356 // This doesn't do anything when we resize the window ourselves.
360 - (BOOL)acceptsFirstResponder
362 return [[self window] contentView] == self;
365 - (void) completeText:(NSString*)text
368 WineWindow* window = (WineWindow*)[self window];
370 event = macdrv_create_event(IM_SET_TEXT, window);
371 event->im_set_text.data = [window imeData];
372 event->im_set_text.text = (CFStringRef)[text copy];
373 event->im_set_text.complete = TRUE;
375 [[window queue] postEvent:event];
377 macdrv_release_event(event);
379 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
380 markedTextSelection = NSMakeRange(0, 0);
381 [[self inputContext] discardMarkedText];
384 - (NSFocusRingType) focusRingType
386 return NSFocusRingTypeNone;
390 * ---------- NSTextInputClient methods ----------
392 - (NSTextInputContext*) inputContext
395 markedText = [[NSMutableAttributedString alloc] init];
396 return [super inputContext];
399 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
401 if ([string isKindOfClass:[NSAttributedString class]])
402 string = [string string];
404 if ([string isKindOfClass:[NSString class]])
405 [self completeText:string];
408 - (void) doCommandBySelector:(SEL)aSelector
410 [(WineWindow*)[self window] setCommandDone:TRUE];
413 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
415 if ([string isKindOfClass:[NSAttributedString class]])
416 string = [string string];
418 if ([string isKindOfClass:[NSString class]])
421 WineWindow* window = (WineWindow*)[self window];
423 if (replacementRange.location == NSNotFound)
424 replacementRange = NSMakeRange(0, [markedText length]);
426 [markedText replaceCharactersInRange:replacementRange withString:string];
427 markedTextSelection = selectedRange;
428 markedTextSelection.location += replacementRange.location;
430 event = macdrv_create_event(IM_SET_TEXT, window);
431 event->im_set_text.data = [window imeData];
432 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
433 event->im_set_text.complete = FALSE;
434 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
436 [[window queue] postEvent:event];
438 macdrv_release_event(event);
440 [[self inputContext] invalidateCharacterCoordinates];
446 [self completeText:nil];
449 - (NSRange) selectedRange
451 return markedTextSelection;
454 - (NSRange) markedRange
456 NSRange range = NSMakeRange(0, [markedText length]);
458 range.location = NSNotFound;
462 - (BOOL) hasMarkedText
464 return [markedText length] > 0;
467 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
469 if (aRange.location >= [markedText length])
472 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
474 *actualRange = aRange;
475 return [markedText attributedSubstringFromRange:aRange];
478 - (NSArray*) validAttributesForMarkedText
480 return [NSArray array];
483 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
486 WineWindow* window = (WineWindow*)[self window];
489 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
491 query = macdrv_create_query();
492 query->type = QUERY_IME_CHAR_RECT;
493 query->window = (macdrv_window)[window retain];
494 query->ime_char_rect.data = [window imeData];
495 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
497 if ([window.queue query:query timeout:1])
499 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
500 ret = NSRectFromCGRect(query->ime_char_rect.rect);
501 [[WineApplicationController sharedController] flipRect:&ret];
504 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
506 macdrv_release_query(query);
509 *actualRange = aRange;
513 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
518 - (NSInteger) windowLevel
520 return [[self window] level];
526 @implementation WineWindow
528 static WineWindow* causing_becomeKeyWindow;
530 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
531 @synthesize surface, surface_mutex;
532 @synthesize shape, shapeChangedSinceLastDraw;
533 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
534 @synthesize usePerPixelAlpha;
535 @synthesize imeData, commandDone;
536 @synthesize liveResizeDisplayTimer;
538 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
539 windowFrame:(NSRect)window_frame
541 queue:(WineEventQueue*)queue
544 WineContentView* contentView;
545 NSTrackingArea* trackingArea;
546 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
548 [[WineApplicationController sharedController] flipRect:&window_frame];
550 window = [[[self alloc] initWithContentRect:window_frame
551 styleMask:style_mask_for_features(wf)
552 backing:NSBackingStoreBuffered
553 defer:YES] autorelease];
555 if (!window) return nil;
557 /* Standardize windows to eliminate differences between titled and
558 borderless windows and between NSWindow and NSPanel. */
559 [window setHidesOnDeactivate:NO];
560 [window setReleasedWhenClosed:NO];
562 [window setOneShot:YES];
563 [window disableCursorRects];
564 [window setShowsResizeIndicator:NO];
565 [window setHasShadow:wf->shadow];
566 [window setAcceptsMouseMovedEvents:YES];
567 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
568 [window setDelegate:window];
570 window.queue = queue;
571 window->savedContentMinSize = NSZeroSize;
572 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
573 window->resizable = wf->resizable;
575 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
576 (NSString*)kUTTypeContent,
579 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
582 [contentView setAutoresizesSubviews:NO];
584 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
585 because they give us mouse moves in the background. */
586 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
587 options:(NSTrackingMouseMoved |
588 NSTrackingActiveAlways |
589 NSTrackingInVisibleRect)
591 userInfo:nil] autorelease];
594 [contentView addTrackingArea:trackingArea];
596 [window setContentView:contentView];
597 [window setInitialFirstResponder:contentView];
599 [nc addObserver:window
600 selector:@selector(updateFullscreen)
601 name:NSApplicationDidChangeScreenParametersNotification
603 [window updateFullscreen];
605 [nc addObserver:window
606 selector:@selector(applicationWillHide)
607 name:NSApplicationWillHideNotification
609 [nc addObserver:window
610 selector:@selector(applicationDidUnhide)
611 name:NSApplicationDidUnhideNotification
619 [[NSNotificationCenter defaultCenter] removeObserver:self];
620 [liveResizeDisplayTimer invalidate];
621 [liveResizeDisplayTimer release];
623 [latentChildWindows release];
624 [latentParentWindow release];
629 - (BOOL) preventResizing
631 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
632 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || maximized || preventForClipping);
635 - (void) adjustFeaturesForState
637 NSUInteger style = [self styleMask];
639 if (style & NSClosableWindowMask)
640 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
641 if (style & NSMiniaturizableWindowMask)
642 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
643 if (style & NSResizableWindowMask)
644 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
645 if ([self respondsToSelector:@selector(toggleFullScreen:)])
647 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
648 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
651 if ([self preventResizing])
653 NSSize size = [self contentRectForFrameRect:[self frame]].size;
654 [self setContentMinSize:size];
655 [self setContentMaxSize:size];
659 [self setContentMaxSize:savedContentMaxSize];
660 [self setContentMinSize:savedContentMinSize];
663 if (allow_immovable_windows || cursor_clipping_locks_windows)
665 if (allow_immovable_windows && (disabled || maximized))
666 [self setMovable:NO];
667 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
668 [self setMovable:NO];
670 [self setMovable:YES];
674 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
676 if ([self respondsToSelector:@selector(toggleFullScreen:)])
678 NSUInteger style = [self styleMask];
680 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
681 style & NSResizableWindowMask && !(style & NSUtilityWindowMask))
683 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
684 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
688 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
689 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
690 if (style & NSFullScreenWindowMask)
691 [self toggleFullScreen:nil];
695 if (behavior != [self collectionBehavior])
697 [self setCollectionBehavior:behavior];
698 [self adjustFeaturesForState];
702 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
704 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
705 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
706 NSUInteger currentStyle = [self styleMask];
707 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
709 if (newStyle != currentStyle)
711 NSString* title = [[[self title] copy] autorelease];
712 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
713 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
714 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
716 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
717 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
718 // just NSTitledWindowMask, the window buttons should disappear rather
719 // than just being disabled. But they don't. Similarly in reverse.
720 // The workaround is to also toggle NSClosableWindowMask at the same time.
721 [self setStyleMask:newStyle ^ NSClosableWindowMask];
723 [self setStyleMask:newStyle];
725 // -setStyleMask: resets the firstResponder to the window. Set it
726 // back to the content view.
727 if ([[self contentView] acceptsFirstResponder])
728 [self makeFirstResponder:[self contentView]];
730 [self adjustFullScreenBehavior:[self collectionBehavior]];
732 if ([[self title] length] == 0 && [title length] > 0)
733 [self setTitle:title];
736 resizable = wf->resizable;
737 [self adjustFeaturesForState];
738 [self setHasShadow:wf->shadow];
741 // Indicates if the window would be visible if the app were not hidden.
742 - (BOOL) wouldBeVisible
744 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
749 return [self wouldBeVisible] || [self isMiniaturized];
752 - (NSInteger) minimumLevelForActive:(BOOL)active
756 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
757 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
758 level = NSFloatingWindowLevel;
760 level = NSNormalWindowLevel;
766 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
768 if (captured || fullscreen)
771 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
773 level = NSMainMenuWindowLevel + 1;
783 - (void) postDidUnminimizeEvent
787 /* Coalesce events by discarding any previous ones still in the queue. */
788 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
791 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
792 [queue postEvent:event];
793 macdrv_release_event(event);
796 - (void) setMacDrvState:(const struct macdrv_window_state*)state
798 NSWindowCollectionBehavior behavior;
800 self.disabled = state->disabled;
801 self.noActivate = state->no_activate;
803 if (self.floating != state->floating)
805 self.floating = state->floating;
808 // Became floating. If child of non-floating window, make that
809 // relationship latent.
810 WineWindow* parent = (WineWindow*)[self parentWindow];
811 if (parent && !parent.floating)
812 [self becameIneligibleChild];
816 // Became non-floating. If parent of floating children, make that
817 // relationship latent.
819 for (child in [[[self childWindows] copy] autorelease])
822 [child becameIneligibleChild];
826 // Check our latent relationships. If floating status was the only
827 // reason they were latent, then make them active.
828 if ([self isVisible])
829 [self becameEligibleParentOrChild];
831 [[WineApplicationController sharedController] adjustWindowLevels];
834 behavior = NSWindowCollectionBehaviorDefault;
835 if (state->excluded_by_expose)
836 behavior |= NSWindowCollectionBehaviorTransient;
838 behavior |= NSWindowCollectionBehaviorManaged;
839 if (state->excluded_by_cycle)
841 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
842 if ([self isOrderedIn])
843 [NSApp removeWindowsItem:self];
847 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
848 if ([self isOrderedIn])
849 [NSApp addWindowsItem:self title:[self title] filename:NO];
851 [self adjustFullScreenBehavior:behavior];
853 if (state->minimized_valid)
855 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
857 pendingMinimize = FALSE;
858 if (state->minimized && ![self isMiniaturized])
860 if ([self wouldBeVisible])
862 if ([self styleMask] & NSFullScreenWindowMask)
864 [self postDidUnminimizeEvent];
865 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
869 [super miniaturize:nil];
870 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
871 event_mask_for_type(WINDOW_GOT_FOCUS) |
872 event_mask_for_type(WINDOW_LOST_FOCUS);
876 pendingMinimize = TRUE;
878 else if (!state->minimized && [self isMiniaturized])
880 ignore_windowDeminiaturize = TRUE;
881 [self deminiaturize:nil];
882 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
886 [queue discardEventsMatchingMask:discard forWindow:self];
889 if (state->maximized != maximized)
891 maximized = state->maximized;
892 [self adjustFeaturesForState];
896 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
898 BOOL reordered = FALSE;
900 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
902 if ([self level] > [child level])
903 [child setLevel:[self level]];
904 [self addChildWindow:child ordered:NSWindowAbove];
905 [latentChildWindows removeObjectIdenticalTo:child];
906 child.latentParentWindow = nil;
911 if (!latentChildWindows)
912 latentChildWindows = [[NSMutableArray alloc] init];
913 if (![latentChildWindows containsObject:child])
914 [latentChildWindows addObject:child];
915 child.latentParentWindow = self;
921 - (BOOL) addChildWineWindow:(WineWindow*)child
923 return [self addChildWineWindow:child assumeVisible:FALSE];
926 - (void) removeChildWineWindow:(WineWindow*)child
928 [self removeChildWindow:child];
929 if (child.latentParentWindow == self)
930 child.latentParentWindow = nil;
931 [latentChildWindows removeObjectIdenticalTo:child];
934 - (BOOL) becameEligibleParentOrChild
936 BOOL reordered = FALSE;
939 if (latentParentWindow.floating || !self.floating)
941 // If we aren't visible currently, we assume that we should be and soon
942 // will be. So, if the latent parent is visible that's enough to assume
943 // we can establish the parent-child relationship in Cocoa. That will
944 // actually make us visible, which is fine.
945 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
949 // Here, though, we may not actually be visible yet and adding a child
950 // won't make us visible. The caller will have to call this method
951 // again after actually making us visible.
952 if ([self isVisible] && (count = [latentChildWindows count]))
954 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
957 for (i = 0; i < count; i++)
959 WineWindow* child = [latentChildWindows objectAtIndex:i];
960 if ([child isVisible] && (self.floating || !child.floating))
962 if (child.latentParentWindow == self)
964 if ([self level] > [child level])
965 [child setLevel:[self level]];
966 [self addChildWindow:child ordered:NSWindowAbove];
967 child.latentParentWindow = nil;
971 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
972 [indexesToRemove addIndex:i];
976 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
982 - (void) becameIneligibleChild
984 WineWindow* parent = (WineWindow*)[self parentWindow];
987 if (!parent->latentChildWindows)
988 parent->latentChildWindows = [[NSMutableArray alloc] init];
989 [parent->latentChildWindows insertObject:self atIndex:0];
990 self.latentParentWindow = parent;
991 [parent removeChildWindow:self];
995 - (void) becameIneligibleParentOrChild
997 NSArray* childWindows = [self childWindows];
999 [self becameIneligibleChild];
1001 if ([childWindows count])
1005 childWindows = [[childWindows copy] autorelease];
1006 for (child in childWindows)
1008 child.latentParentWindow = self;
1009 [self removeChildWindow:child];
1012 if (latentChildWindows)
1013 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1015 latentChildWindows = [childWindows mutableCopy];
1019 // Determine if, among Wine windows, this window is directly above or below
1020 // a given other Wine window with no other Wine window intervening.
1021 // Intervening non-Wine windows are ignored.
1022 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1024 NSNumber* windowNumber;
1025 NSNumber* otherWindowNumber;
1026 NSArray* windowNumbers;
1027 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1029 if (![self isVisible] || ![otherWindow isVisible])
1032 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1033 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1034 windowNumbers = [[self class] windowNumbersWithOptions:0];
1035 windowIndex = [windowNumbers indexOfObject:windowNumber];
1036 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1038 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1041 if (orderingMode == NSWindowAbove)
1043 lowIndex = windowIndex;
1044 highIndex = otherWindowIndex;
1046 else if (orderingMode == NSWindowBelow)
1048 lowIndex = otherWindowIndex;
1049 highIndex = windowIndex;
1054 if (highIndex <= lowIndex)
1057 for (i = lowIndex + 1; i < highIndex; i++)
1059 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1060 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1061 if ([interveningWindow isKindOfClass:[WineWindow class]])
1068 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1070 NSMutableArray* windowNumbers;
1071 NSNumber* childWindowNumber;
1072 NSUInteger otherIndex, limit;
1073 NSArray* origChildren;
1074 NSMutableArray* children;
1076 // Get the z-order from the window server and modify it to reflect the
1077 // requested window ordering.
1078 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1079 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1080 [windowNumbers removeObject:childWindowNumber];
1081 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1082 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1084 // Get our child windows and sort them in the reverse of the desired
1085 // z-order (back-to-front).
1086 origChildren = [self childWindows];
1087 children = [[origChildren mutableCopy] autorelease];
1088 [children sortWithOptions:NSSortStable
1089 usingComparator:^NSComparisonResult(id obj1, id obj2){
1090 NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1091 NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1092 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1093 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1094 if (index1 == NSNotFound)
1096 if (index2 == NSNotFound)
1097 return NSOrderedSame;
1099 return NSOrderedAscending;
1101 else if (index2 == NSNotFound)
1102 return NSOrderedDescending;
1103 else if (index1 < index2)
1104 return NSOrderedDescending;
1105 else if (index2 < index1)
1106 return NSOrderedAscending;
1108 return NSOrderedSame;
1111 // If the current and desired children arrays match up to a point, leave
1112 // those matching children alone.
1113 limit = MIN([origChildren count], [children count]);
1114 for (otherIndex = 0; otherIndex < limit; otherIndex++)
1116 if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1119 [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1121 // Remove all of the child windows and re-add them back-to-front so they
1122 // are in the desired order.
1123 for (other in children)
1124 [self removeChildWindow:other];
1125 for (other in children)
1126 [self addChildWindow:other ordered:NSWindowAbove];
1129 /* Returns whether or not the window was ordered in, which depends on if
1130 its frame intersects any screen. */
1131 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1133 WineApplicationController* controller = [WineApplicationController sharedController];
1134 if (![self isMiniaturized])
1136 BOOL needAdjustWindowLevels = FALSE;
1139 [controller transformProcessToForeground];
1141 wasVisible = [self isVisible];
1144 [NSApp activateIgnoringOtherApps:YES];
1146 NSDisableScreenUpdates();
1148 if ([self becameEligibleParentOrChild])
1149 needAdjustWindowLevels = TRUE;
1153 WineWindow* other = [prev isVisible] ? prev : next;
1154 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1156 if (![self isOrdered:orderingMode relativeTo:other])
1158 WineWindow* parent = (WineWindow*)[self parentWindow];
1159 WineWindow* otherParent = (WineWindow*)[other parentWindow];
1161 // This window level may not be right for this window based
1162 // on floating-ness, fullscreen-ness, etc. But we set it
1163 // temporarily to allow us to order the windows properly.
1164 // Then the levels get fixed by -adjustWindowLevels.
1165 if ([self level] != [other level])
1166 [self setLevel:[other level]];
1167 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1169 // The above call to -[NSWindow orderWindow:relativeTo:] won't
1170 // reorder windows which are both children of the same parent
1171 // relative to each other, so do that separately.
1172 if (parent && parent == otherParent)
1173 [parent order:orderingMode childWindow:self relativeTo:other];
1175 needAdjustWindowLevels = TRUE;
1180 // Again, temporarily set level to make sure we can order to
1182 next = [controller frontWineWindow];
1183 if (next && [self level] < [next level])
1184 [self setLevel:[next level]];
1185 [self orderFront:nil];
1186 needAdjustWindowLevels = TRUE;
1189 if ([self becameEligibleParentOrChild])
1190 needAdjustWindowLevels = TRUE;
1192 if (needAdjustWindowLevels)
1194 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1195 [controller updateFullscreenWindows];
1196 [controller adjustWindowLevels];
1199 if (pendingMinimize)
1201 [super miniaturize:nil];
1202 pendingMinimize = FALSE;
1205 NSEnableScreenUpdates();
1207 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1208 Generate a frame-changed event just in case. The back end will ignore
1209 it if nothing actually changed. */
1210 [self windowDidResize:nil];
1212 if (![self isExcludedFromWindowsMenu])
1213 [NSApp addWindowsItem:self title:[self title] filename:NO];
1219 WineApplicationController* controller = [WineApplicationController sharedController];
1220 BOOL wasVisible = [self isVisible];
1221 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1223 if ([self isMiniaturized])
1224 pendingMinimize = TRUE;
1226 [self becameIneligibleParentOrChild];
1227 if ([self isMiniaturized])
1231 fakingClose = FALSE;
1234 [self orderOut:nil];
1235 savedVisibleState = FALSE;
1236 if (wasVisible && wasOnActiveSpace && fullscreen)
1237 [controller updateFullscreenWindows];
1238 [controller adjustWindowLevels];
1239 [NSApp removeWindowsItem:self];
1241 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1242 event_mask_for_type(WINDOW_GOT_FOCUS) |
1243 event_mask_for_type(WINDOW_LOST_FOCUS) |
1244 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1245 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1246 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1250 - (void) updateFullscreen
1252 NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1253 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1255 if (nowFullscreen != fullscreen)
1257 WineApplicationController* controller = [WineApplicationController sharedController];
1259 fullscreen = nowFullscreen;
1260 if ([self isVisible] && [self isOnActiveSpace])
1261 [controller updateFullscreenWindows];
1263 [controller adjustWindowLevels];
1267 - (void) setFrameFromWine:(NSRect)contentRect
1269 /* Origin is (left, top) in a top-down space. Need to convert it to
1270 (left, bottom) in a bottom-up space. */
1271 [[WineApplicationController sharedController] flipRect:&contentRect];
1273 /* The back end is establishing a new window size and position. It's
1274 not interested in any stale events regarding those that may be sitting
1276 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1279 if (!NSIsEmptyRect(contentRect))
1281 NSRect frame, oldFrame;
1283 oldFrame = [self frame];
1284 frame = [self frameRectForContentRect:contentRect];
1285 if (!NSEqualRects(frame, oldFrame))
1287 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1288 BOOL needEnableScreenUpdates = FALSE;
1290 if ([self preventResizing])
1292 // Allow the following calls to -setFrame:display: to work even
1293 // if they would violate the content size constraints. This
1294 // shouldn't be necessary since the content size constraints are
1295 // documented to not constrain that method, but it seems to be.
1296 [self setContentMinSize:NSZeroSize];
1297 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1300 if (equalSizes && [[self childWindows] count])
1302 // If we change the window frame such that the origin moves
1303 // but the size doesn't change, then Cocoa moves child
1304 // windows with the parent. We don't want that so we fake
1305 // a change of the size and then change it back.
1306 NSRect bogusFrame = frame;
1307 bogusFrame.size.width++;
1309 NSDisableScreenUpdates();
1310 needEnableScreenUpdates = TRUE;
1312 ignore_windowResize = TRUE;
1313 [self setFrame:bogusFrame display:NO];
1314 ignore_windowResize = FALSE;
1317 [self setFrame:frame display:YES];
1318 if ([self preventResizing])
1320 [self setContentMinSize:contentRect.size];
1321 [self setContentMaxSize:contentRect.size];
1324 if (needEnableScreenUpdates)
1325 NSEnableScreenUpdates();
1328 [self updateColorSpace];
1330 if (!enteringFullScreen &&
1331 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1332 nonFullscreenFrame = frame;
1334 [self updateFullscreen];
1336 if ([self isOrderedIn])
1338 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1339 event. The back end will ignore it if nothing actually changed. */
1340 [self windowDidResize:nil];
1346 - (void) setMacDrvParentWindow:(WineWindow*)parent
1348 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1349 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1351 [oldParent removeChildWineWindow:self];
1352 [latentParentWindow removeChildWineWindow:self];
1353 if ([parent addChildWineWindow:self])
1354 [[WineApplicationController sharedController] adjustWindowLevels];
1358 - (void) setDisabled:(BOOL)newValue
1360 if (disabled != newValue)
1362 disabled = newValue;
1363 [self adjustFeaturesForState];
1367 - (BOOL) needsTransparency
1369 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1372 - (void) checkTransparency
1374 if (![self isOpaque] && !self.needsTransparency)
1376 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1377 [self setOpaque:YES];
1379 else if ([self isOpaque] && self.needsTransparency)
1381 [self setBackgroundColor:[NSColor clearColor]];
1382 [self setOpaque:NO];
1386 - (void) setShape:(NSBezierPath*)newShape
1388 if (shape == newShape) return;
1389 if (shape && newShape && [shape isEqual:newShape]) return;
1393 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1397 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1399 shape = [newShape copy];
1400 self.shapeChangedSinceLastDraw = TRUE;
1402 [self checkTransparency];
1405 - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1407 if (newTimer != liveResizeDisplayTimer)
1409 [liveResizeDisplayTimer invalidate];
1410 [liveResizeDisplayTimer release];
1411 liveResizeDisplayTimer = [newTimer retain];
1415 - (void) makeFocused:(BOOL)activate
1419 [[WineApplicationController sharedController] transformProcessToForeground];
1420 [NSApp activateIgnoringOtherApps:YES];
1423 causing_becomeKeyWindow = self;
1424 [self makeKeyWindow];
1425 causing_becomeKeyWindow = nil;
1427 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1428 event_mask_for_type(WINDOW_LOST_FOCUS)
1432 - (void) postKey:(uint16_t)keyCode
1433 pressed:(BOOL)pressed
1434 modifiers:(NSUInteger)modifiers
1435 event:(NSEvent*)theEvent
1437 macdrv_event* event;
1439 WineApplicationController* controller = [WineApplicationController sharedController];
1441 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1442 event->key.keycode = keyCode;
1443 event->key.modifiers = modifiers;
1444 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1446 if ((cgevent = [theEvent CGEvent]))
1448 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1449 kCGKeyboardEventKeyboardType);
1450 if (keyboardType != controller.keyboardType)
1452 controller.keyboardType = keyboardType;
1453 [controller keyboardSelectionDidChange];
1457 [queue postEvent:event];
1459 macdrv_release_event(event);
1461 [controller noteKey:keyCode pressed:pressed];
1464 - (void) postKeyEvent:(NSEvent *)theEvent
1466 [self flagsChanged:theEvent];
1467 [self postKey:[theEvent keyCode]
1468 pressed:[theEvent type] == NSKeyDown
1469 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1473 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1475 savedContentMinSize = minSize;
1476 savedContentMaxSize = maxSize;
1477 if (![self preventResizing])
1479 [self setContentMinSize:minSize];
1480 [self setContentMaxSize:maxSize];
1484 - (WineWindow*) ancestorWineWindow
1486 WineWindow* ancestor = self;
1489 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1490 if ([parent isKindOfClass:[WineWindow class]])
1498 - (void) postBroughtForwardEvent
1500 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1501 [queue postEvent:event];
1502 macdrv_release_event(event);
1505 - (void) updateForCursorClipping
1507 [self adjustFeaturesForState];
1512 * ---------- NSWindow method overrides ----------
1514 - (BOOL) canBecomeKeyWindow
1516 if (causing_becomeKeyWindow == self) return YES;
1517 if (self.disabled || self.noActivate) return NO;
1518 return [self isKeyWindow];
1521 - (BOOL) canBecomeMainWindow
1523 return [self canBecomeKeyWindow];
1526 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1528 // If a window is sized to completely cover a screen, then it's in
1529 // full-screen mode. In that case, we don't allow NSWindow to constrain
1531 NSArray* screens = [NSScreen screens];
1532 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1533 if (!screen_covered_by_rect(contentRect, screens) &&
1534 frame_intersects_screens(frameRect, screens))
1535 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1539 - (BOOL) isExcludedFromWindowsMenu
1541 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1544 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1546 BOOL ret = [super validateMenuItem:menuItem];
1548 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1549 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1550 if ([menuItem action] == @selector(toggleFullScreen:) && self.disabled)
1556 /* We don't call this. It's the action method of the items in the Window menu. */
1557 - (void) makeKeyAndOrderFront:(id)sender
1559 if ([self isMiniaturized])
1560 [self deminiaturize:nil];
1561 [self orderBelow:nil orAbove:nil activate:NO];
1562 [[self ancestorWineWindow] postBroughtForwardEvent];
1564 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1565 [[WineApplicationController sharedController] windowGotFocus:self];
1568 - (void) sendEvent:(NSEvent*)event
1570 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1571 interface control. For example, Control-Tab switches focus among
1572 views. We want to bypass that feature, so directly route key-down
1573 events to -keyDown:. */
1574 if ([event type] == NSKeyDown)
1575 [[self firstResponder] keyDown:event];
1577 [super sendEvent:event];
1580 - (void) miniaturize:(id)sender
1582 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1583 [queue postEvent:event];
1584 macdrv_release_event(event);
1587 - (void) toggleFullScreen:(id)sender
1590 [super toggleFullScreen:sender];
1593 // We normally use the generic/calibrated RGB color space for the window,
1594 // rather than the device color space, to avoid expensive color conversion
1595 // which slows down drawing. However, for windows displaying OpenGL, having
1596 // a different color space than the screen greatly reduces frame rates, often
1597 // limiting it to the display refresh rate.
1599 // To avoid this, we switch back to the screen color space whenever the
1600 // window is covered by a view with an attached OpenGL context.
1601 - (void) updateColorSpace
1603 NSRect contentRect = [[self contentView] frame];
1604 BOOL coveredByGLView = FALSE;
1605 for (WineContentView* view in [[self contentView] subviews])
1607 if ([view hasGLContext])
1609 NSRect frame = [view convertRect:[view bounds] toView:nil];
1610 if (NSContainsRect(frame, contentRect))
1612 coveredByGLView = TRUE;
1618 if (coveredByGLView)
1619 [self setColorSpace:nil];
1621 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1626 * ---------- NSResponder method overrides ----------
1628 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1630 - (void) flagsChanged:(NSEvent *)theEvent
1632 static const struct {
1636 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1637 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1638 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1639 { NX_DEVICELCTLKEYMASK, kVK_Control },
1640 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1641 { NX_DEVICELALTKEYMASK, kVK_Option },
1642 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1643 { NX_DEVICELCMDKEYMASK, kVK_Command },
1644 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1647 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1649 int i, last_changed;
1651 fix_device_modifiers_by_generic(&modifierFlags);
1652 changed = modifierFlags ^ lastModifierFlags;
1655 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1656 if (changed & modifiers[i].mask)
1659 for (i = 0; i <= last_changed; i++)
1661 if (changed & modifiers[i].mask)
1663 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1665 if (i == last_changed)
1666 lastModifierFlags = modifierFlags;
1669 lastModifierFlags ^= modifiers[i].mask;
1670 fix_generic_modifiers_by_device(&lastModifierFlags);
1673 // Caps lock generates one event for each press-release action.
1674 // We need to simulate a pair of events for each actual event.
1675 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1677 [self postKey:modifiers[i].keycode
1679 modifiers:lastModifierFlags
1680 event:(NSEvent*)theEvent];
1684 [self postKey:modifiers[i].keycode
1686 modifiers:lastModifierFlags
1687 event:(NSEvent*)theEvent];
1692 - (void) applicationWillHide
1694 savedVisibleState = [self isVisible];
1697 - (void) applicationDidUnhide
1699 if ([self isVisible])
1700 [self becameEligibleParentOrChild];
1705 * ---------- NSWindowDelegate methods ----------
1707 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1709 macdrv_query* query;
1712 query = macdrv_create_query();
1713 query->type = QUERY_MIN_MAX_INFO;
1714 query->window = (macdrv_window)[self retain];
1715 [self.queue query:query timeout:0.5];
1716 macdrv_release_query(query);
1718 size = [self contentMaxSize];
1719 if (proposedSize.width < size.width)
1720 size.width = proposedSize.width;
1721 if (proposedSize.height < size.height)
1722 size.height = proposedSize.height;
1726 - (void)windowDidBecomeKey:(NSNotification *)notification
1728 WineApplicationController* controller = [WineApplicationController sharedController];
1729 NSEvent* event = [controller lastFlagsChanged];
1731 [self flagsChanged:event];
1733 if (causing_becomeKeyWindow == self) return;
1735 [controller windowGotFocus:self];
1738 - (void)windowDidDeminiaturize:(NSNotification *)notification
1740 WineApplicationController* controller = [WineApplicationController sharedController];
1742 if (!ignore_windowDeminiaturize)
1743 [self postDidUnminimizeEvent];
1744 ignore_windowDeminiaturize = FALSE;
1746 [self becameEligibleParentOrChild];
1748 if (fullscreen && [self isOnActiveSpace])
1749 [controller updateFullscreenWindows];
1750 [controller adjustWindowLevels];
1752 if (![self parentWindow])
1753 [self postBroughtForwardEvent];
1755 if (!self.disabled && !self.noActivate)
1757 causing_becomeKeyWindow = self;
1758 [self makeKeyWindow];
1759 causing_becomeKeyWindow = nil;
1760 [controller windowGotFocus:self];
1763 [self windowDidResize:notification];
1766 - (void) windowDidEndLiveResize:(NSNotification *)notification
1768 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1769 [queue postEvent:event];
1770 macdrv_release_event(event);
1772 self.liveResizeDisplayTimer = nil;
1775 - (void) windowDidEnterFullScreen:(NSNotification*)notification
1777 enteringFullScreen = FALSE;
1778 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1781 - (void) windowDidExitFullScreen:(NSNotification*)notification
1783 exitingFullScreen = FALSE;
1784 [self setFrame:nonFullscreenFrame display:YES animate:NO];
1785 [self windowDidResize:nil];
1788 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1790 enteringFullScreen = FALSE;
1791 enteredFullScreenTime = 0;
1794 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1796 exitingFullScreen = FALSE;
1797 [self windowDidResize:nil];
1800 - (void)windowDidMiniaturize:(NSNotification *)notification
1802 if (fullscreen && [self isOnActiveSpace])
1803 [[WineApplicationController sharedController] updateFullscreenWindows];
1806 - (void)windowDidMove:(NSNotification *)notification
1808 [self windowDidResize:notification];
1811 - (void)windowDidResignKey:(NSNotification *)notification
1813 macdrv_event* event;
1815 if (causing_becomeKeyWindow) return;
1817 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1818 [queue postEvent:event];
1819 macdrv_release_event(event);
1822 - (void)windowDidResize:(NSNotification *)notification
1824 macdrv_event* event;
1825 NSRect frame = [self frame];
1827 if ([self inLiveResize])
1829 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1830 resizingFromLeft = TRUE;
1831 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1832 resizingFromTop = TRUE;
1835 frame = [self contentRectForFrameRect:frame];
1837 if (ignore_windowResize || exitingFullScreen) return;
1839 if ([self preventResizing])
1841 [self setContentMinSize:frame.size];
1842 [self setContentMaxSize:frame.size];
1845 [[WineApplicationController sharedController] flipRect:&frame];
1847 /* Coalesce events by discarding any previous ones still in the queue. */
1848 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1851 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1852 event->window_frame_changed.frame = NSRectToCGRect(frame);
1853 event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
1854 event->window_frame_changed.in_resize = [self inLiveResize];
1855 [queue postEvent:event];
1856 macdrv_release_event(event);
1858 [[[self contentView] inputContext] invalidateCharacterCoordinates];
1859 [self updateFullscreen];
1862 - (BOOL)windowShouldClose:(id)sender
1864 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1865 [queue postEvent:event];
1866 macdrv_release_event(event);
1870 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
1874 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1875 [queue postEvent:event];
1876 macdrv_release_event(event);
1879 else if (!resizable)
1881 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
1882 [queue postEvent:event];
1883 macdrv_release_event(event);
1890 - (void) windowWillClose:(NSNotification*)notification
1894 if (fakingClose) return;
1895 if (latentParentWindow)
1897 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
1898 self.latentParentWindow = nil;
1901 for (child in latentChildWindows)
1903 if (child.latentParentWindow == self)
1904 child.latentParentWindow = nil;
1906 [latentChildWindows removeAllObjects];
1909 - (void) windowWillEnterFullScreen:(NSNotification*)notification
1911 enteringFullScreen = TRUE;
1912 nonFullscreenFrame = [self frame];
1915 - (void) windowWillExitFullScreen:(NSNotification*)notification
1917 exitingFullScreen = TRUE;
1920 - (void)windowWillMiniaturize:(NSNotification *)notification
1922 [self becameIneligibleParentOrChild];
1925 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
1927 if ([self inLiveResize])
1930 macdrv_query* query;
1932 rect = [self frame];
1933 if (resizingFromLeft)
1934 rect.origin.x = NSMaxX(rect) - frameSize.width;
1935 if (!resizingFromTop)
1936 rect.origin.y = NSMaxY(rect) - frameSize.height;
1937 rect.size = frameSize;
1938 rect = [self contentRectForFrameRect:rect];
1939 [[WineApplicationController sharedController] flipRect:&rect];
1941 query = macdrv_create_query();
1942 query->type = QUERY_RESIZE_SIZE;
1943 query->window = (macdrv_window)[self retain];
1944 query->resize_size.rect = NSRectToCGRect(rect);
1945 query->resize_size.from_left = resizingFromLeft;
1946 query->resize_size.from_top = resizingFromTop;
1948 if ([self.queue query:query timeout:0.1])
1950 rect = NSRectFromCGRect(query->resize_size.rect);
1951 rect = [self frameRectForContentRect:rect];
1952 frameSize = rect.size;
1955 macdrv_release_query(query);
1961 - (void) windowWillStartLiveResize:(NSNotification *)notification
1963 macdrv_query* query = macdrv_create_query();
1964 query->type = QUERY_RESIZE_START;
1965 query->window = (macdrv_window)[self retain];
1967 [self.queue query:query timeout:0.3];
1968 macdrv_release_query(query);
1970 frameAtResizeStart = [self frame];
1971 resizingFromLeft = resizingFromTop = FALSE;
1973 // There's a strange restriction in window redrawing during Cocoa-
1974 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
1975 // that happen synchronously when Cocoa tells us that our window size
1976 // has changed or asynchronously in a short interval thereafter provoke
1977 // the window to redraw. Calls to those methods that happen asynchronously
1978 // a half second or more after the last change of the window size aren't
1979 // heeded until the next resize-related user event (e.g. mouse movement).
1981 // Wine often has a significant delay between when it's been told that
1982 // the window has changed size and when it can flush completed drawing.
1983 // So, our windows would get stuck with incomplete drawing for as long
1984 // as the user holds the mouse button down and doesn't move it.
1986 // We address this by "manually" asking our windows to check if they need
1987 // redrawing every so often (during live resize only).
1988 self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1990 selector:@selector(displayIfNeeded)
1993 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1994 forMode:NSRunLoopCommonModes];
1997 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
1999 macdrv_query* query;
2000 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2003 query = macdrv_create_query();
2004 query->type = QUERY_MIN_MAX_INFO;
2005 query->window = (macdrv_window)[self retain];
2006 [self.queue query:query timeout:0.5];
2007 macdrv_release_query(query);
2009 currentContentRect = [self contentRectForFrameRect:[self frame]];
2010 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2012 maxSize = [self contentMaxSize];
2013 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2014 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2016 // Try to keep the top-left corner where it is.
2017 newContentRect.origin.x = NSMinX(currentContentRect);
2018 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2020 // If that pushes the bottom or right off the screen, pull it up and to the left.
2021 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2022 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2023 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2024 if (NSMinY(newContentRect) < NSMinY(screenRect))
2025 newContentRect.origin.y = NSMinY(screenRect);
2027 // If that pushes the top or left off the screen, push it down and the right
2028 // again. Do this last because the top-left corner is more important than the
2030 if (NSMinX(newContentRect) < NSMinX(screenRect))
2031 newContentRect.origin.x = NSMinX(screenRect);
2032 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2033 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2035 return [self frameRectForContentRect:newContentRect];
2040 * ---------- NSPasteboardOwner methods ----------
2042 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2044 macdrv_query* query = macdrv_create_query();
2045 query->type = QUERY_PASTEBOARD_DATA;
2046 query->window = (macdrv_window)[self retain];
2047 query->pasteboard_data.type = (CFStringRef)[type copy];
2049 [self.queue query:query timeout:3];
2050 macdrv_release_query(query);
2055 * ---------- NSDraggingDestination methods ----------
2057 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2059 return [self draggingUpdated:sender];
2062 - (void) draggingExited:(id <NSDraggingInfo>)sender
2064 // This isn't really a query. We don't need any response. However, it
2065 // has to be processed in a similar manner as the other drag-and-drop
2066 // queries in order to maintain the proper order of operations.
2067 macdrv_query* query = macdrv_create_query();
2068 query->type = QUERY_DRAG_EXITED;
2069 query->window = (macdrv_window)[self retain];
2071 [self.queue query:query timeout:0.1];
2072 macdrv_release_query(query);
2075 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2077 NSDragOperation ret;
2078 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2079 NSPasteboard* pb = [sender draggingPasteboard];
2081 macdrv_query* query = macdrv_create_query();
2082 query->type = QUERY_DRAG_OPERATION;
2083 query->window = (macdrv_window)[self retain];
2084 query->drag_operation.x = pt.x;
2085 query->drag_operation.y = pt.y;
2086 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2087 query->drag_operation.accepted_op = NSDragOperationNone;
2088 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2090 [self.queue query:query timeout:3];
2091 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2092 macdrv_release_query(query);
2097 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2100 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2101 NSPasteboard* pb = [sender draggingPasteboard];
2103 macdrv_query* query = macdrv_create_query();
2104 query->type = QUERY_DRAG_DROP;
2105 query->window = (macdrv_window)[self retain];
2106 query->drag_drop.x = pt.x;
2107 query->drag_drop.y = pt.y;
2108 query->drag_drop.op = [sender draggingSourceOperationMask];
2109 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2111 [self.queue query:query timeout:3 * 60 processEvents:YES];
2112 ret = query->status;
2113 macdrv_release_query(query);
2118 - (BOOL) wantsPeriodicDraggingUpdates
2126 /***********************************************************************
2127 * macdrv_create_cocoa_window
2129 * Create a Cocoa window with the given content frame and features (e.g.
2130 * title bar, close box, etc.).
2132 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2133 CGRect frame, void* hwnd, macdrv_event_queue queue)
2135 __block WineWindow* window;
2138 window = [[WineWindow createWindowWithFeatures:wf
2139 windowFrame:NSRectFromCGRect(frame)
2141 queue:(WineEventQueue*)queue] retain];
2144 return (macdrv_window)window;
2147 /***********************************************************************
2148 * macdrv_destroy_cocoa_window
2150 * Destroy a Cocoa window.
2152 void macdrv_destroy_cocoa_window(macdrv_window w)
2154 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2155 WineWindow* window = (WineWindow*)w;
2158 [window doOrderOut];
2161 [window.queue discardEventsMatchingMask:-1 forWindow:window];
2167 /***********************************************************************
2168 * macdrv_get_window_hwnd
2170 * Get the hwnd that was set for the window at creation.
2172 void* macdrv_get_window_hwnd(macdrv_window w)
2174 WineWindow* window = (WineWindow*)w;
2178 /***********************************************************************
2179 * macdrv_set_cocoa_window_features
2181 * Update a Cocoa window's features.
2183 void macdrv_set_cocoa_window_features(macdrv_window w,
2184 const struct macdrv_window_features* wf)
2186 WineWindow* window = (WineWindow*)w;
2189 [window setWindowFeatures:wf];
2193 /***********************************************************************
2194 * macdrv_set_cocoa_window_state
2196 * Update a Cocoa window's state.
2198 void macdrv_set_cocoa_window_state(macdrv_window w,
2199 const struct macdrv_window_state* state)
2201 WineWindow* window = (WineWindow*)w;
2204 [window setMacDrvState:state];
2208 /***********************************************************************
2209 * macdrv_set_cocoa_window_title
2211 * Set a Cocoa window's title.
2213 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2216 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2217 WineWindow* window = (WineWindow*)w;
2218 NSString* titleString;
2221 titleString = [NSString stringWithCharacters:title length:length];
2224 OnMainThreadAsync(^{
2225 [window setTitle:titleString];
2226 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2227 [NSApp changeWindowsItem:window title:titleString filename:NO];
2233 /***********************************************************************
2234 * macdrv_order_cocoa_window
2236 * Reorder a Cocoa window relative to other windows. If prev is
2237 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
2238 * it is ordered above that window. Otherwise, it is ordered to the
2241 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2242 macdrv_window n, int activate)
2244 WineWindow* window = (WineWindow*)w;
2245 WineWindow* prev = (WineWindow*)p;
2246 WineWindow* next = (WineWindow*)n;
2248 OnMainThreadAsync(^{
2249 [window orderBelow:prev
2253 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2255 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2259 /***********************************************************************
2260 * macdrv_hide_cocoa_window
2262 * Hides a Cocoa window.
2264 void macdrv_hide_cocoa_window(macdrv_window w)
2266 WineWindow* window = (WineWindow*)w;
2269 [window doOrderOut];
2273 /***********************************************************************
2274 * macdrv_set_cocoa_window_frame
2276 * Move a Cocoa window.
2278 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2280 WineWindow* window = (WineWindow*)w;
2283 [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2287 /***********************************************************************
2288 * macdrv_get_cocoa_window_frame
2290 * Gets the frame of a Cocoa window.
2292 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2294 WineWindow* window = (WineWindow*)w;
2299 frame = [window contentRectForFrameRect:[window frame]];
2300 [[WineApplicationController sharedController] flipRect:&frame];
2301 *out_frame = NSRectToCGRect(frame);
2305 /***********************************************************************
2306 * macdrv_set_cocoa_parent_window
2308 * Sets the parent window for a Cocoa window. If parent is NULL, clears
2309 * the parent window.
2311 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2313 WineWindow* window = (WineWindow*)w;
2316 [window setMacDrvParentWindow:(WineWindow*)parent];
2320 /***********************************************************************
2321 * macdrv_set_window_surface
2323 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2325 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2326 WineWindow* window = (WineWindow*)w;
2329 window.surface = surface;
2330 window.surface_mutex = mutex;
2336 /***********************************************************************
2337 * macdrv_window_needs_display
2339 * Mark a window as needing display in a specified rect (in non-client
2340 * area coordinates).
2342 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2344 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2345 WineWindow* window = (WineWindow*)w;
2347 OnMainThreadAsync(^{
2348 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2354 /***********************************************************************
2355 * macdrv_set_window_shape
2357 * Sets the shape of a Cocoa window from an array of rectangles. If
2358 * rects is NULL, resets the window's shape to its frame.
2360 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2362 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2363 WineWindow* window = (WineWindow*)w;
2366 if (!rects || !count)
2373 path = [NSBezierPath bezierPath];
2374 for (i = 0; i < count; i++)
2375 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2376 window.shape = path;
2383 /***********************************************************************
2384 * macdrv_set_window_alpha
2386 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2388 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2389 WineWindow* window = (WineWindow*)w;
2391 [window setAlphaValue:alpha];
2396 /***********************************************************************
2397 * macdrv_set_window_color_key
2399 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2402 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2403 WineWindow* window = (WineWindow*)w;
2406 window.colorKeyed = TRUE;
2407 window.colorKeyRed = keyRed;
2408 window.colorKeyGreen = keyGreen;
2409 window.colorKeyBlue = keyBlue;
2410 [window checkTransparency];
2416 /***********************************************************************
2417 * macdrv_clear_window_color_key
2419 void macdrv_clear_window_color_key(macdrv_window w)
2421 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2422 WineWindow* window = (WineWindow*)w;
2425 window.colorKeyed = FALSE;
2426 [window checkTransparency];
2432 /***********************************************************************
2433 * macdrv_window_use_per_pixel_alpha
2435 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2437 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2438 WineWindow* window = (WineWindow*)w;
2441 window.usePerPixelAlpha = use_per_pixel_alpha;
2442 [window checkTransparency];
2448 /***********************************************************************
2449 * macdrv_give_cocoa_window_focus
2451 * Makes the Cocoa window "key" (gives it keyboard focus). This also
2452 * orders it front and, if its frame was not within the desktop bounds,
2453 * Cocoa will typically move it on-screen.
2455 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2457 WineWindow* window = (WineWindow*)w;
2460 [window makeFocused:activate];
2464 /***********************************************************************
2465 * macdrv_set_window_min_max_sizes
2467 * Sets the window's minimum and maximum content sizes.
2469 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2471 WineWindow* window = (WineWindow*)w;
2474 [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2478 /***********************************************************************
2479 * macdrv_create_view
2481 * Creates and returns a view in the specified rect of the window. The
2482 * caller is responsible for calling macdrv_dispose_view() on the view
2483 * when it is done with it.
2485 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2487 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2488 WineWindow* window = (WineWindow*)w;
2489 __block WineContentView* view;
2491 if (CGRectIsNull(rect)) rect = CGRectZero;
2494 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2496 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2497 [view setAutoresizesSubviews:NO];
2498 [nc addObserver:view
2499 selector:@selector(updateGLContexts)
2500 name:NSViewGlobalFrameDidChangeNotification
2502 [nc addObserver:view
2503 selector:@selector(updateGLContexts)
2504 name:NSApplicationDidChangeScreenParametersNotification
2506 [[window contentView] addSubview:view];
2507 [window updateColorSpace];
2511 return (macdrv_view)view;
2514 /***********************************************************************
2515 * macdrv_dispose_view
2517 * Destroys a view previously returned by macdrv_create_view.
2519 void macdrv_dispose_view(macdrv_view v)
2521 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2522 WineContentView* view = (WineContentView*)v;
2525 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2526 WineWindow* window = (WineWindow*)[view window];
2528 [nc removeObserver:view
2529 name:NSViewGlobalFrameDidChangeNotification
2531 [nc removeObserver:view
2532 name:NSApplicationDidChangeScreenParametersNotification
2534 [view removeFromSuperview];
2536 [window updateColorSpace];
2542 /***********************************************************************
2543 * macdrv_set_view_window_and_frame
2545 * Move a view to a new window and/or position within its window. If w
2546 * is NULL, leave the view in its current window and just change its
2549 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2551 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2552 WineContentView* view = (WineContentView*)v;
2553 WineWindow* window = (WineWindow*)w;
2555 if (CGRectIsNull(rect)) rect = CGRectZero;
2558 BOOL changedWindow = (window && window != [view window]);
2559 NSRect newFrame = NSRectFromCGRect(rect);
2560 NSRect oldFrame = [view frame];
2561 BOOL needUpdateWindowColorSpace = FALSE;
2565 WineWindow* oldWindow = (WineWindow*)[view window];
2566 [view removeFromSuperview];
2567 [oldWindow updateColorSpace];
2568 [[window contentView] addSubview:view];
2569 needUpdateWindowColorSpace = TRUE;
2572 if (!NSEqualRects(oldFrame, newFrame))
2575 [[view superview] setNeedsDisplayInRect:oldFrame];
2576 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2577 [view setFrameSize:newFrame.size];
2578 else if (NSEqualSizes(oldFrame.size, newFrame.size))
2579 [view setFrameOrigin:newFrame.origin];
2581 [view setFrame:newFrame];
2582 [view setNeedsDisplay:YES];
2583 needUpdateWindowColorSpace = TRUE;
2586 if (needUpdateWindowColorSpace)
2587 [(WineWindow*)[view window] updateColorSpace];
2593 /***********************************************************************
2594 * macdrv_add_view_opengl_context
2596 * Add an OpenGL context to the list being tracked for each view.
2598 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2600 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2601 WineContentView* view = (WineContentView*)v;
2602 WineOpenGLContext *context = (WineOpenGLContext*)c;
2605 [view addGLContext:context];
2611 /***********************************************************************
2612 * macdrv_remove_view_opengl_context
2614 * Add an OpenGL context to the list being tracked for each view.
2616 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2618 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2619 WineContentView* view = (WineContentView*)v;
2620 WineOpenGLContext *context = (WineOpenGLContext*)c;
2622 OnMainThreadAsync(^{
2623 [view removeGLContext:context];
2629 /***********************************************************************
2630 * macdrv_window_background_color
2632 * Returns the standard Mac window background color as a 32-bit value of
2633 * the form 0x00rrggbb.
2635 uint32_t macdrv_window_background_color(void)
2637 static uint32_t result;
2638 static dispatch_once_t once;
2640 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2641 // color spaces (RGB or grayscale). So, the only way to get RGB values out
2642 // of it is to draw with it.
2643 dispatch_once(&once, ^{
2645 unsigned char rgbx[4];
2646 unsigned char *planes = rgbx;
2647 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2654 colorSpaceName:NSCalibratedRGBColorSpace
2658 [NSGraphicsContext saveGraphicsState];
2659 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2660 [[NSColor windowBackgroundColor] set];
2661 NSRectFill(NSMakeRect(0, 0, 1, 1));
2662 [NSGraphicsContext restoreGraphicsState];
2664 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2671 /***********************************************************************
2672 * macdrv_send_text_input_event
2674 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2679 WineWindow* window = (WineWindow*)[NSApp keyWindow];
2680 if (![window isKindOfClass:[WineWindow class]])
2682 window = (WineWindow*)[NSApp mainWindow];
2683 if (![window isKindOfClass:[WineWindow class]])
2684 window = [[WineApplicationController sharedController] frontWineWindow];
2689 NSUInteger localFlags = flags;
2693 window.imeData = data;
2694 fix_device_modifiers_by_generic(&localFlags);
2696 // An NSEvent created with +keyEventWithType:... is internally marked
2697 // as synthetic and doesn't get sent through input methods. But one
2698 // created from a CGEvent doesn't have that problem.
2699 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2700 CGEventSetFlags(c, localFlags);
2701 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2702 event = [NSEvent eventWithCGEvent:c];
2705 window.commandDone = FALSE;
2706 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;