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 - (void) updateColorSpace;
200 - (BOOL) becameEligibleParentOrChild;
201 - (void) becameIneligibleChild;
206 @implementation WineContentView
210 [markedText release];
211 [glContexts release];
212 [pendingGlContexts release];
221 - (void) drawRect:(NSRect)rect
223 WineWindow* window = (WineWindow*)[self window];
225 for (WineOpenGLContext* context in pendingGlContexts)
227 if (!clearedGlSurface)
229 context.shouldClearToBlack = TRUE;
230 clearedGlSurface = TRUE;
232 context.needsUpdate = TRUE;
234 [glContexts addObjectsFromArray:pendingGlContexts];
235 [pendingGlContexts removeAllObjects];
237 if ([window contentView] != self)
240 if (window.shapeChangedSinceLastDraw && window.shape && !window.colorKeyed && !window.usePerPixelAlpha)
242 [[NSColor clearColor] setFill];
245 [window.shape addClip];
247 [[NSColor windowBackgroundColor] setFill];
251 if (window.surface && window.surface_mutex &&
252 !pthread_mutex_lock(window.surface_mutex))
257 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
259 CGContextRef context;
262 [window.shape addClip];
264 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
265 CGContextSetBlendMode(context, kCGBlendModeCopy);
267 for (i = 0; i < count; i++)
272 imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
273 image = create_surface_image(window.surface, &imageRect, FALSE);
277 if (window.colorKeyed)
279 CGImageRef maskedImage;
280 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
281 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
282 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
283 maskedImage = CGImageCreateWithMaskingColors(image, components);
286 CGImageRelease(image);
291 CGContextDrawImage(context, imageRect, image);
293 CGImageRelease(image);
298 pthread_mutex_unlock(window.surface_mutex);
301 // If the window may be transparent, then we have to invalidate the
302 // shadow every time we draw. Also, if this is the first time we've
303 // drawn since changing from transparent to opaque.
304 if (window.colorKeyed || window.usePerPixelAlpha || window.shapeChangedSinceLastDraw)
306 window.shapeChangedSinceLastDraw = FALSE;
307 [window invalidateShadow];
311 - (void) addGLContext:(WineOpenGLContext*)context
314 glContexts = [[NSMutableArray alloc] init];
315 if (!pendingGlContexts)
316 pendingGlContexts = [[NSMutableArray alloc] init];
318 if ([[self window] windowNumber] > 0 && !NSIsEmptyRect([self visibleRect]))
320 [glContexts addObject:context];
321 if (!clearedGlSurface)
323 context.shouldClearToBlack = TRUE;
324 clearedGlSurface = TRUE;
326 context.needsUpdate = TRUE;
330 [pendingGlContexts addObject:context];
331 [self setNeedsDisplay:YES];
334 [(WineWindow*)[self window] updateColorSpace];
337 - (void) removeGLContext:(WineOpenGLContext*)context
339 [glContexts removeObjectIdenticalTo:context];
340 [pendingGlContexts removeObjectIdenticalTo:context];
341 [(WineWindow*)[self window] updateColorSpace];
344 - (void) updateGLContexts
346 for (WineOpenGLContext* context in glContexts)
347 context.needsUpdate = TRUE;
350 - (BOOL) hasGLContext
352 return [glContexts count] || [pendingGlContexts count];
355 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
360 - (BOOL) preservesContentDuringLiveResize
362 // Returning YES from this tells Cocoa to keep our view's content during
363 // a Cocoa-driven resize. In theory, we're also supposed to override
364 // -setFrameSize: to mark exposed sections as needing redisplay, but
365 // user32 will take care of that in a roundabout way. This way, we don't
366 // redraw until the window surface is flushed.
368 // This doesn't do anything when we resize the window ourselves.
372 - (BOOL)acceptsFirstResponder
374 return [[self window] contentView] == self;
377 - (BOOL) mouseDownCanMoveWindow
382 - (void) completeText:(NSString*)text
385 WineWindow* window = (WineWindow*)[self window];
387 event = macdrv_create_event(IM_SET_TEXT, window);
388 event->im_set_text.data = [window imeData];
389 event->im_set_text.text = (CFStringRef)[text copy];
390 event->im_set_text.complete = TRUE;
392 [[window queue] postEvent:event];
394 macdrv_release_event(event);
396 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
397 markedTextSelection = NSMakeRange(0, 0);
398 [[self inputContext] discardMarkedText];
401 - (NSFocusRingType) focusRingType
403 return NSFocusRingTypeNone;
407 * ---------- NSTextInputClient methods ----------
409 - (NSTextInputContext*) inputContext
412 markedText = [[NSMutableAttributedString alloc] init];
413 return [super inputContext];
416 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
418 if ([string isKindOfClass:[NSAttributedString class]])
419 string = [string string];
421 if ([string isKindOfClass:[NSString class]])
422 [self completeText:string];
425 - (void) doCommandBySelector:(SEL)aSelector
427 [(WineWindow*)[self window] setCommandDone:TRUE];
430 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
432 if ([string isKindOfClass:[NSAttributedString class]])
433 string = [string string];
435 if ([string isKindOfClass:[NSString class]])
438 WineWindow* window = (WineWindow*)[self window];
440 if (replacementRange.location == NSNotFound)
441 replacementRange = NSMakeRange(0, [markedText length]);
443 [markedText replaceCharactersInRange:replacementRange withString:string];
444 markedTextSelection = selectedRange;
445 markedTextSelection.location += replacementRange.location;
447 event = macdrv_create_event(IM_SET_TEXT, window);
448 event->im_set_text.data = [window imeData];
449 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
450 event->im_set_text.complete = FALSE;
451 event->im_set_text.cursor_pos = markedTextSelection.location + markedTextSelection.length;
453 [[window queue] postEvent:event];
455 macdrv_release_event(event);
457 [[self inputContext] invalidateCharacterCoordinates];
463 [self completeText:nil];
466 - (NSRange) selectedRange
468 return markedTextSelection;
471 - (NSRange) markedRange
473 NSRange range = NSMakeRange(0, [markedText length]);
475 range.location = NSNotFound;
479 - (BOOL) hasMarkedText
481 return [markedText length] > 0;
484 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
486 if (aRange.location >= [markedText length])
489 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
491 *actualRange = aRange;
492 return [markedText attributedSubstringFromRange:aRange];
495 - (NSArray*) validAttributesForMarkedText
497 return [NSArray array];
500 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
503 WineWindow* window = (WineWindow*)[self window];
506 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
508 query = macdrv_create_query();
509 query->type = QUERY_IME_CHAR_RECT;
510 query->window = (macdrv_window)[window retain];
511 query->ime_char_rect.data = [window imeData];
512 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
514 if ([window.queue query:query timeout:1])
516 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
517 ret = NSRectFromCGRect(query->ime_char_rect.rect);
518 [[WineApplicationController sharedController] flipRect:&ret];
521 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
523 macdrv_release_query(query);
526 *actualRange = aRange;
530 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
535 - (NSInteger) windowLevel
537 return [[self window] level];
543 @implementation WineWindow
545 static WineWindow* causing_becomeKeyWindow;
547 @synthesize disabled, noActivate, floating, fullscreen, fakingClose, latentParentWindow, hwnd, queue;
548 @synthesize surface, surface_mutex;
549 @synthesize shape, shapeData, shapeChangedSinceLastDraw;
550 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
551 @synthesize usePerPixelAlpha;
552 @synthesize imeData, commandDone;
553 @synthesize liveResizeDisplayTimer;
555 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
556 windowFrame:(NSRect)window_frame
558 queue:(WineEventQueue*)queue
561 WineContentView* contentView;
562 NSTrackingArea* trackingArea;
563 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
565 [[WineApplicationController sharedController] flipRect:&window_frame];
567 window = [[[self alloc] initWithContentRect:window_frame
568 styleMask:style_mask_for_features(wf)
569 backing:NSBackingStoreBuffered
570 defer:YES] autorelease];
572 if (!window) return nil;
574 /* Standardize windows to eliminate differences between titled and
575 borderless windows and between NSWindow and NSPanel. */
576 [window setHidesOnDeactivate:NO];
577 [window setReleasedWhenClosed:NO];
579 [window setOneShot:YES];
580 [window disableCursorRects];
581 [window setShowsResizeIndicator:NO];
582 [window setHasShadow:wf->shadow];
583 [window setAcceptsMouseMovedEvents:YES];
584 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
585 [window setDelegate:window];
587 window.queue = queue;
588 window->savedContentMinSize = NSZeroSize;
589 window->savedContentMaxSize = NSMakeSize(FLT_MAX, FLT_MAX);
590 window->resizable = wf->resizable;
592 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
593 (NSString*)kUTTypeContent,
596 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
599 [contentView setAutoresizesSubviews:NO];
601 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
602 because they give us mouse moves in the background. */
603 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
604 options:(NSTrackingMouseMoved |
605 NSTrackingActiveAlways |
606 NSTrackingInVisibleRect)
608 userInfo:nil] autorelease];
611 [contentView addTrackingArea:trackingArea];
613 [window setContentView:contentView];
614 [window setInitialFirstResponder:contentView];
616 [nc addObserver:window
617 selector:@selector(updateFullscreen)
618 name:NSApplicationDidChangeScreenParametersNotification
620 [window updateFullscreen];
622 [nc addObserver:window
623 selector:@selector(applicationWillHide)
624 name:NSApplicationWillHideNotification
626 [nc addObserver:window
627 selector:@selector(applicationDidUnhide)
628 name:NSApplicationDidUnhideNotification
636 [[NSNotificationCenter defaultCenter] removeObserver:self];
637 [liveResizeDisplayTimer invalidate];
638 [liveResizeDisplayTimer release];
640 [latentChildWindows release];
641 [latentParentWindow release];
647 - (BOOL) preventResizing
649 BOOL preventForClipping = cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor];
650 return ([self styleMask] & NSResizableWindowMask) && (disabled || !resizable || maximized || preventForClipping);
653 - (void) adjustFeaturesForState
655 NSUInteger style = [self styleMask];
657 if (style & NSClosableWindowMask)
658 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
659 if (style & NSMiniaturizableWindowMask)
660 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
661 if (style & NSResizableWindowMask)
662 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
663 if ([self respondsToSelector:@selector(toggleFullScreen:)])
665 if ([self collectionBehavior] & NSWindowCollectionBehaviorFullScreenPrimary)
666 [[self standardWindowButton:NSWindowFullScreenButton] setEnabled:!self.disabled];
669 if ([self preventResizing])
671 NSSize size = [self contentRectForFrameRect:[self frame]].size;
672 [self setContentMinSize:size];
673 [self setContentMaxSize:size];
677 [self setContentMaxSize:savedContentMaxSize];
678 [self setContentMinSize:savedContentMinSize];
681 if (allow_immovable_windows || cursor_clipping_locks_windows)
683 if (allow_immovable_windows && (disabled || maximized))
684 [self setMovable:NO];
685 else if (cursor_clipping_locks_windows && [[WineApplicationController sharedController] clippingCursor])
686 [self setMovable:NO];
688 [self setMovable:YES];
692 - (void) adjustFullScreenBehavior:(NSWindowCollectionBehavior)behavior
694 if ([self respondsToSelector:@selector(toggleFullScreen:)])
696 NSUInteger style = [self styleMask];
698 if (behavior & NSWindowCollectionBehaviorParticipatesInCycle &&
699 style & NSResizableWindowMask && !(style & NSUtilityWindowMask))
701 behavior |= NSWindowCollectionBehaviorFullScreenPrimary;
702 behavior &= ~NSWindowCollectionBehaviorFullScreenAuxiliary;
706 behavior &= ~NSWindowCollectionBehaviorFullScreenPrimary;
707 behavior |= NSWindowCollectionBehaviorFullScreenAuxiliary;
708 if (style & NSFullScreenWindowMask)
709 [self toggleFullScreen:nil];
713 if (behavior != [self collectionBehavior])
715 [self setCollectionBehavior:behavior];
716 [self adjustFeaturesForState];
720 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
722 static const NSUInteger usedStyles = NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask |
723 NSResizableWindowMask | NSUtilityWindowMask | NSBorderlessWindowMask;
724 NSUInteger currentStyle = [self styleMask];
725 NSUInteger newStyle = style_mask_for_features(wf) | (currentStyle & ~usedStyles);
727 if (newStyle != currentStyle)
729 NSString* title = [[[self title] copy] autorelease];
730 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
731 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
732 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
734 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
735 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
736 // just NSTitledWindowMask, the window buttons should disappear rather
737 // than just being disabled. But they don't. Similarly in reverse.
738 // The workaround is to also toggle NSClosableWindowMask at the same time.
739 [self setStyleMask:newStyle ^ NSClosableWindowMask];
741 [self setStyleMask:newStyle];
743 // -setStyleMask: resets the firstResponder to the window. Set it
744 // back to the content view.
745 if ([[self contentView] acceptsFirstResponder])
746 [self makeFirstResponder:[self contentView]];
748 [self adjustFullScreenBehavior:[self collectionBehavior]];
750 if ([[self title] length] == 0 && [title length] > 0)
751 [self setTitle:title];
754 resizable = wf->resizable;
755 [self adjustFeaturesForState];
756 [self setHasShadow:wf->shadow];
759 // Indicates if the window would be visible if the app were not hidden.
760 - (BOOL) wouldBeVisible
762 return [NSApp isHidden] ? savedVisibleState : [self isVisible];
767 return [self wouldBeVisible] || [self isMiniaturized];
770 - (NSInteger) minimumLevelForActive:(BOOL)active
774 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
775 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
776 level = NSFloatingWindowLevel;
778 level = NSNormalWindowLevel;
784 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
786 if (captured || fullscreen)
789 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
791 level = NSMainMenuWindowLevel + 1;
801 - (void) postDidUnminimizeEvent
805 /* Coalesce events by discarding any previous ones still in the queue. */
806 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_UNMINIMIZE)
809 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
810 [queue postEvent:event];
811 macdrv_release_event(event);
814 - (void) setMacDrvState:(const struct macdrv_window_state*)state
816 NSWindowCollectionBehavior behavior;
818 self.disabled = state->disabled;
819 self.noActivate = state->no_activate;
821 if (self.floating != state->floating)
823 self.floating = state->floating;
826 // Became floating. If child of non-floating window, make that
827 // relationship latent.
828 WineWindow* parent = (WineWindow*)[self parentWindow];
829 if (parent && !parent.floating)
830 [self becameIneligibleChild];
834 // Became non-floating. If parent of floating children, make that
835 // relationship latent.
837 for (child in [[[self childWindows] copy] autorelease])
840 [child becameIneligibleChild];
844 // Check our latent relationships. If floating status was the only
845 // reason they were latent, then make them active.
846 if ([self isVisible])
847 [self becameEligibleParentOrChild];
849 [[WineApplicationController sharedController] adjustWindowLevels];
852 behavior = NSWindowCollectionBehaviorDefault;
853 if (state->excluded_by_expose)
854 behavior |= NSWindowCollectionBehaviorTransient;
856 behavior |= NSWindowCollectionBehaviorManaged;
857 if (state->excluded_by_cycle)
859 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
860 if ([self isOrderedIn])
861 [NSApp removeWindowsItem:self];
865 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
866 if ([self isOrderedIn])
867 [NSApp addWindowsItem:self title:[self title] filename:NO];
869 [self adjustFullScreenBehavior:behavior];
871 if (state->minimized_valid)
873 macdrv_event_mask discard = event_mask_for_type(WINDOW_DID_UNMINIMIZE);
875 pendingMinimize = FALSE;
876 if (state->minimized && ![self isMiniaturized])
878 if ([self wouldBeVisible])
880 if ([self styleMask] & NSFullScreenWindowMask)
882 [self postDidUnminimizeEvent];
883 discard &= ~event_mask_for_type(WINDOW_DID_UNMINIMIZE);
887 [super miniaturize:nil];
888 discard |= event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
889 event_mask_for_type(WINDOW_GOT_FOCUS) |
890 event_mask_for_type(WINDOW_LOST_FOCUS);
894 pendingMinimize = TRUE;
896 else if (!state->minimized && [self isMiniaturized])
898 ignore_windowDeminiaturize = TRUE;
899 [self deminiaturize:nil];
900 discard |= event_mask_for_type(WINDOW_LOST_FOCUS);
904 [queue discardEventsMatchingMask:discard forWindow:self];
907 if (state->maximized != maximized)
909 maximized = state->maximized;
910 [self adjustFeaturesForState];
914 - (BOOL) addChildWineWindow:(WineWindow*)child assumeVisible:(BOOL)assumeVisible
916 BOOL reordered = FALSE;
918 if ([self isVisible] && (assumeVisible || [child isVisible]) && (self.floating || !child.floating))
920 if ([self level] > [child level])
921 [child setLevel:[self level]];
922 [self addChildWindow:child ordered:NSWindowAbove];
923 [latentChildWindows removeObjectIdenticalTo:child];
924 child.latentParentWindow = nil;
929 if (!latentChildWindows)
930 latentChildWindows = [[NSMutableArray alloc] init];
931 if (![latentChildWindows containsObject:child])
932 [latentChildWindows addObject:child];
933 child.latentParentWindow = self;
939 - (BOOL) addChildWineWindow:(WineWindow*)child
941 return [self addChildWineWindow:child assumeVisible:FALSE];
944 - (void) removeChildWineWindow:(WineWindow*)child
946 [self removeChildWindow:child];
947 if (child.latentParentWindow == self)
948 child.latentParentWindow = nil;
949 [latentChildWindows removeObjectIdenticalTo:child];
952 - (BOOL) becameEligibleParentOrChild
954 BOOL reordered = FALSE;
957 if (latentParentWindow.floating || !self.floating)
959 // If we aren't visible currently, we assume that we should be and soon
960 // will be. So, if the latent parent is visible that's enough to assume
961 // we can establish the parent-child relationship in Cocoa. That will
962 // actually make us visible, which is fine.
963 if ([latentParentWindow addChildWineWindow:self assumeVisible:TRUE])
967 // Here, though, we may not actually be visible yet and adding a child
968 // won't make us visible. The caller will have to call this method
969 // again after actually making us visible.
970 if ([self isVisible] && (count = [latentChildWindows count]))
972 NSMutableIndexSet* indexesToRemove = [NSMutableIndexSet indexSet];
975 for (i = 0; i < count; i++)
977 WineWindow* child = [latentChildWindows objectAtIndex:i];
978 if ([child isVisible] && (self.floating || !child.floating))
980 if (child.latentParentWindow == self)
982 if ([self level] > [child level])
983 [child setLevel:[self level]];
984 [self addChildWindow:child ordered:NSWindowAbove];
985 child.latentParentWindow = nil;
989 ERR(@"shouldn't happen: %@ thinks %@ is a latent child, but it doesn't agree\n", self, child);
990 [indexesToRemove addIndex:i];
994 [latentChildWindows removeObjectsAtIndexes:indexesToRemove];
1000 - (void) becameIneligibleChild
1002 WineWindow* parent = (WineWindow*)[self parentWindow];
1005 if (!parent->latentChildWindows)
1006 parent->latentChildWindows = [[NSMutableArray alloc] init];
1007 [parent->latentChildWindows insertObject:self atIndex:0];
1008 self.latentParentWindow = parent;
1009 [parent removeChildWindow:self];
1013 - (void) becameIneligibleParentOrChild
1015 NSArray* childWindows = [self childWindows];
1017 [self becameIneligibleChild];
1019 if ([childWindows count])
1023 childWindows = [[childWindows copy] autorelease];
1024 for (child in childWindows)
1026 child.latentParentWindow = self;
1027 [self removeChildWindow:child];
1030 if (latentChildWindows)
1031 [latentChildWindows replaceObjectsInRange:NSMakeRange(0, 0) withObjectsFromArray:childWindows];
1033 latentChildWindows = [childWindows mutableCopy];
1037 // Determine if, among Wine windows, this window is directly above or below
1038 // a given other Wine window with no other Wine window intervening.
1039 // Intervening non-Wine windows are ignored.
1040 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
1042 NSNumber* windowNumber;
1043 NSNumber* otherWindowNumber;
1044 NSArray* windowNumbers;
1045 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
1047 if (![self isVisible] || ![otherWindow isVisible])
1050 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
1051 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
1052 windowNumbers = [[self class] windowNumbersWithOptions:0];
1053 windowIndex = [windowNumbers indexOfObject:windowNumber];
1054 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
1056 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
1059 if (orderingMode == NSWindowAbove)
1061 lowIndex = windowIndex;
1062 highIndex = otherWindowIndex;
1064 else if (orderingMode == NSWindowBelow)
1066 lowIndex = otherWindowIndex;
1067 highIndex = windowIndex;
1072 if (highIndex <= lowIndex)
1075 for (i = lowIndex + 1; i < highIndex; i++)
1077 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
1078 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
1079 if ([interveningWindow isKindOfClass:[WineWindow class]])
1086 - (void) order:(NSWindowOrderingMode)mode childWindow:(WineWindow*)child relativeTo:(WineWindow*)other
1088 NSMutableArray* windowNumbers;
1089 NSNumber* childWindowNumber;
1090 NSUInteger otherIndex, limit;
1091 NSArray* origChildren;
1092 NSMutableArray* children;
1094 // Get the z-order from the window server and modify it to reflect the
1095 // requested window ordering.
1096 windowNumbers = [[[[self class] windowNumbersWithOptions:NSWindowNumberListAllSpaces] mutableCopy] autorelease];
1097 childWindowNumber = [NSNumber numberWithInteger:[child windowNumber]];
1098 [windowNumbers removeObject:childWindowNumber];
1099 otherIndex = [windowNumbers indexOfObject:[NSNumber numberWithInteger:[other windowNumber]]];
1100 [windowNumbers insertObject:childWindowNumber atIndex:otherIndex + (mode == NSWindowAbove ? 0 : 1)];
1102 // Get our child windows and sort them in the reverse of the desired
1103 // z-order (back-to-front).
1104 origChildren = [self childWindows];
1105 children = [[origChildren mutableCopy] autorelease];
1106 [children sortWithOptions:NSSortStable
1107 usingComparator:^NSComparisonResult(id obj1, id obj2){
1108 NSNumber* window1Number = [NSNumber numberWithInteger:[obj1 windowNumber]];
1109 NSNumber* window2Number = [NSNumber numberWithInteger:[obj2 windowNumber]];
1110 NSUInteger index1 = [windowNumbers indexOfObject:window1Number];
1111 NSUInteger index2 = [windowNumbers indexOfObject:window2Number];
1112 if (index1 == NSNotFound)
1114 if (index2 == NSNotFound)
1115 return NSOrderedSame;
1117 return NSOrderedAscending;
1119 else if (index2 == NSNotFound)
1120 return NSOrderedDescending;
1121 else if (index1 < index2)
1122 return NSOrderedDescending;
1123 else if (index2 < index1)
1124 return NSOrderedAscending;
1126 return NSOrderedSame;
1129 // If the current and desired children arrays match up to a point, leave
1130 // those matching children alone.
1131 limit = MIN([origChildren count], [children count]);
1132 for (otherIndex = 0; otherIndex < limit; otherIndex++)
1134 if ([origChildren objectAtIndex:otherIndex] != [children objectAtIndex:otherIndex])
1137 [children removeObjectsInRange:NSMakeRange(0, otherIndex)];
1139 // Remove all of the child windows and re-add them back-to-front so they
1140 // are in the desired order.
1141 for (other in children)
1142 [self removeChildWindow:other];
1143 for (other in children)
1144 [self addChildWindow:other ordered:NSWindowAbove];
1147 /* Returns whether or not the window was ordered in, which depends on if
1148 its frame intersects any screen. */
1149 - (void) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next activate:(BOOL)activate
1151 WineApplicationController* controller = [WineApplicationController sharedController];
1152 if (![self isMiniaturized])
1154 BOOL needAdjustWindowLevels = FALSE;
1157 [controller transformProcessToForeground];
1159 wasVisible = [self isVisible];
1162 [NSApp activateIgnoringOtherApps:YES];
1164 NSDisableScreenUpdates();
1166 if ([self becameEligibleParentOrChild])
1167 needAdjustWindowLevels = TRUE;
1171 WineWindow* other = [prev isVisible] ? prev : next;
1172 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
1174 if (![self isOrdered:orderingMode relativeTo:other])
1176 WineWindow* parent = (WineWindow*)[self parentWindow];
1177 WineWindow* otherParent = (WineWindow*)[other parentWindow];
1179 // This window level may not be right for this window based
1180 // on floating-ness, fullscreen-ness, etc. But we set it
1181 // temporarily to allow us to order the windows properly.
1182 // Then the levels get fixed by -adjustWindowLevels.
1183 if ([self level] != [other level])
1184 [self setLevel:[other level]];
1185 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
1187 // The above call to -[NSWindow orderWindow:relativeTo:] won't
1188 // reorder windows which are both children of the same parent
1189 // relative to each other, so do that separately.
1190 if (parent && parent == otherParent)
1191 [parent order:orderingMode childWindow:self relativeTo:other];
1193 needAdjustWindowLevels = TRUE;
1198 // Again, temporarily set level to make sure we can order to
1200 next = [controller frontWineWindow];
1201 if (next && [self level] < [next level])
1202 [self setLevel:[next level]];
1203 [self orderFront:nil];
1204 needAdjustWindowLevels = TRUE;
1207 if ([self becameEligibleParentOrChild])
1208 needAdjustWindowLevels = TRUE;
1210 if (needAdjustWindowLevels)
1212 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1213 [controller updateFullscreenWindows];
1214 [controller adjustWindowLevels];
1217 if (pendingMinimize)
1219 [super miniaturize:nil];
1220 pendingMinimize = FALSE;
1223 NSEnableScreenUpdates();
1225 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1226 Generate a frame-changed event just in case. The back end will ignore
1227 it if nothing actually changed. */
1228 [self windowDidResize:nil];
1230 if (![self isExcludedFromWindowsMenu])
1231 [NSApp addWindowsItem:self title:[self title] filename:NO];
1237 WineApplicationController* controller = [WineApplicationController sharedController];
1238 BOOL wasVisible = [self isVisible];
1239 BOOL wasOnActiveSpace = [self isOnActiveSpace];
1241 if ([self isMiniaturized])
1242 pendingMinimize = TRUE;
1244 [self becameIneligibleParentOrChild];
1245 if ([self isMiniaturized])
1249 fakingClose = FALSE;
1252 [self orderOut:nil];
1253 savedVisibleState = FALSE;
1254 if (wasVisible && wasOnActiveSpace && fullscreen)
1255 [controller updateFullscreenWindows];
1256 [controller adjustWindowLevels];
1257 [NSApp removeWindowsItem:self];
1259 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD) |
1260 event_mask_for_type(WINDOW_GOT_FOCUS) |
1261 event_mask_for_type(WINDOW_LOST_FOCUS) |
1262 event_mask_for_type(WINDOW_MAXIMIZE_REQUESTED) |
1263 event_mask_for_type(WINDOW_MINIMIZE_REQUESTED) |
1264 event_mask_for_type(WINDOW_RESTORE_REQUESTED)
1268 - (void) updateFullscreen
1270 NSRect contentRect = [self contentRectForFrameRect:[self frame]];
1271 BOOL nowFullscreen = !([self styleMask] & NSFullScreenWindowMask) && screen_covered_by_rect(contentRect, [NSScreen screens]);
1273 if (nowFullscreen != fullscreen)
1275 WineApplicationController* controller = [WineApplicationController sharedController];
1277 fullscreen = nowFullscreen;
1278 if ([self isVisible] && [self isOnActiveSpace])
1279 [controller updateFullscreenWindows];
1281 [controller adjustWindowLevels];
1285 - (void) setFrameFromWine:(NSRect)contentRect
1287 /* Origin is (left, top) in a top-down space. Need to convert it to
1288 (left, bottom) in a bottom-up space. */
1289 [[WineApplicationController sharedController] flipRect:&contentRect];
1291 /* The back end is establishing a new window size and position. It's
1292 not interested in any stale events regarding those that may be sitting
1294 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1297 if (!NSIsEmptyRect(contentRect))
1299 NSRect frame, oldFrame;
1301 oldFrame = [self frame];
1302 frame = [self frameRectForContentRect:contentRect];
1303 if (!NSEqualRects(frame, oldFrame))
1305 BOOL equalSizes = NSEqualSizes(frame.size, oldFrame.size);
1306 BOOL needEnableScreenUpdates = FALSE;
1308 if ([self preventResizing])
1310 // Allow the following calls to -setFrame:display: to work even
1311 // if they would violate the content size constraints. This
1312 // shouldn't be necessary since the content size constraints are
1313 // documented to not constrain that method, but it seems to be.
1314 [self setContentMinSize:NSZeroSize];
1315 [self setContentMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
1318 if (equalSizes && [[self childWindows] count])
1320 // If we change the window frame such that the origin moves
1321 // but the size doesn't change, then Cocoa moves child
1322 // windows with the parent. We don't want that so we fake
1323 // a change of the size and then change it back.
1324 NSRect bogusFrame = frame;
1325 bogusFrame.size.width++;
1327 NSDisableScreenUpdates();
1328 needEnableScreenUpdates = TRUE;
1330 ignore_windowResize = TRUE;
1331 [self setFrame:bogusFrame display:NO];
1332 ignore_windowResize = FALSE;
1335 [self setFrame:frame display:YES];
1336 if ([self preventResizing])
1338 [self setContentMinSize:contentRect.size];
1339 [self setContentMaxSize:contentRect.size];
1342 if (needEnableScreenUpdates)
1343 NSEnableScreenUpdates();
1346 [self updateColorSpace];
1348 if (!enteringFullScreen &&
1349 [[NSProcessInfo processInfo] systemUptime] - enteredFullScreenTime > 1.0)
1350 nonFullscreenFrame = frame;
1352 [self updateFullscreen];
1354 if ([self isOrderedIn])
1356 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
1357 event. The back end will ignore it if nothing actually changed. */
1358 [self windowDidResize:nil];
1364 - (void) setMacDrvParentWindow:(WineWindow*)parent
1366 WineWindow* oldParent = (WineWindow*)[self parentWindow];
1367 if ((oldParent && oldParent != parent) || (!oldParent && latentParentWindow != parent))
1369 [oldParent removeChildWineWindow:self];
1370 [latentParentWindow removeChildWineWindow:self];
1371 if ([parent addChildWineWindow:self])
1372 [[WineApplicationController sharedController] adjustWindowLevels];
1376 - (void) setDisabled:(BOOL)newValue
1378 if (disabled != newValue)
1380 disabled = newValue;
1381 [self adjustFeaturesForState];
1385 - (BOOL) needsTransparency
1387 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
1390 - (void) checkTransparency
1392 if (![self isOpaque] && !self.needsTransparency)
1394 self.shapeChangedSinceLastDraw = TRUE;
1395 [[self contentView] setNeedsDisplay:YES];
1396 [self setBackgroundColor:[NSColor windowBackgroundColor]];
1397 [self setOpaque:YES];
1399 else if ([self isOpaque] && self.needsTransparency)
1401 self.shapeChangedSinceLastDraw = TRUE;
1402 [[self contentView] setNeedsDisplay:YES];
1403 [self setBackgroundColor:[NSColor clearColor]];
1404 [self setOpaque:NO];
1408 - (void) setShape:(NSBezierPath*)newShape
1410 if (shape == newShape) return;
1414 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
1418 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
1420 shape = [newShape copy];
1421 self.shapeChangedSinceLastDraw = TRUE;
1423 [self checkTransparency];
1426 - (void) setLiveResizeDisplayTimer:(NSTimer*)newTimer
1428 if (newTimer != liveResizeDisplayTimer)
1430 [liveResizeDisplayTimer invalidate];
1431 [liveResizeDisplayTimer release];
1432 liveResizeDisplayTimer = [newTimer retain];
1436 - (void) makeFocused:(BOOL)activate
1440 [[WineApplicationController sharedController] transformProcessToForeground];
1441 [NSApp activateIgnoringOtherApps:YES];
1444 causing_becomeKeyWindow = self;
1445 [self makeKeyWindow];
1446 causing_becomeKeyWindow = nil;
1448 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_GOT_FOCUS) |
1449 event_mask_for_type(WINDOW_LOST_FOCUS)
1453 - (void) postKey:(uint16_t)keyCode
1454 pressed:(BOOL)pressed
1455 modifiers:(NSUInteger)modifiers
1456 event:(NSEvent*)theEvent
1458 macdrv_event* event;
1460 WineApplicationController* controller = [WineApplicationController sharedController];
1462 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1463 event->key.keycode = keyCode;
1464 event->key.modifiers = modifiers;
1465 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1467 if ((cgevent = [theEvent CGEvent]))
1469 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1470 kCGKeyboardEventKeyboardType);
1471 if (keyboardType != controller.keyboardType)
1473 controller.keyboardType = keyboardType;
1474 [controller keyboardSelectionDidChange];
1478 [queue postEvent:event];
1480 macdrv_release_event(event);
1482 [controller noteKey:keyCode pressed:pressed];
1485 - (void) postKeyEvent:(NSEvent *)theEvent
1487 [self flagsChanged:theEvent];
1488 [self postKey:[theEvent keyCode]
1489 pressed:[theEvent type] == NSKeyDown
1490 modifiers:adjusted_modifiers_for_option_behavior([theEvent modifierFlags])
1494 - (void) setWineMinSize:(NSSize)minSize maxSize:(NSSize)maxSize
1496 savedContentMinSize = minSize;
1497 savedContentMaxSize = maxSize;
1498 if (![self preventResizing])
1500 [self setContentMinSize:minSize];
1501 [self setContentMaxSize:maxSize];
1505 - (WineWindow*) ancestorWineWindow
1507 WineWindow* ancestor = self;
1510 WineWindow* parent = (WineWindow*)[ancestor parentWindow];
1511 if ([parent isKindOfClass:[WineWindow class]])
1519 - (void) postBroughtForwardEvent
1521 macdrv_event* event = macdrv_create_event(WINDOW_BROUGHT_FORWARD, self);
1522 [queue postEvent:event];
1523 macdrv_release_event(event);
1526 - (void) updateForCursorClipping
1528 [self adjustFeaturesForState];
1533 * ---------- NSWindow method overrides ----------
1535 - (BOOL) canBecomeKeyWindow
1537 if (causing_becomeKeyWindow == self) return YES;
1538 if (self.disabled || self.noActivate) return NO;
1539 return [self isKeyWindow];
1542 - (BOOL) canBecomeMainWindow
1544 return [self canBecomeKeyWindow];
1547 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1549 // If a window is sized to completely cover a screen, then it's in
1550 // full-screen mode. In that case, we don't allow NSWindow to constrain
1552 NSArray* screens = [NSScreen screens];
1553 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1554 if (!screen_covered_by_rect(contentRect, screens) &&
1555 frame_intersects_screens(frameRect, screens))
1556 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1560 - (BOOL) isExcludedFromWindowsMenu
1562 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1565 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1567 BOOL ret = [super validateMenuItem:menuItem];
1569 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1570 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1571 if ([menuItem action] == @selector(toggleFullScreen:) && self.disabled)
1577 /* We don't call this. It's the action method of the items in the Window menu. */
1578 - (void) makeKeyAndOrderFront:(id)sender
1580 if ([self isMiniaturized])
1581 [self deminiaturize:nil];
1582 [self orderBelow:nil orAbove:nil activate:NO];
1583 [[self ancestorWineWindow] postBroughtForwardEvent];
1585 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1586 [[WineApplicationController sharedController] windowGotFocus:self];
1589 - (void) sendEvent:(NSEvent*)event
1591 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1592 interface control. For example, Control-Tab switches focus among
1593 views. We want to bypass that feature, so directly route key-down
1594 events to -keyDown:. */
1595 if ([event type] == NSKeyDown)
1596 [[self firstResponder] keyDown:event];
1598 [super sendEvent:event];
1601 - (void) miniaturize:(id)sender
1603 macdrv_event* event = macdrv_create_event(WINDOW_MINIMIZE_REQUESTED, self);
1604 [queue postEvent:event];
1605 macdrv_release_event(event);
1608 - (void) toggleFullScreen:(id)sender
1611 [super toggleFullScreen:sender];
1614 // We normally use the generic/calibrated RGB color space for the window,
1615 // rather than the device color space, to avoid expensive color conversion
1616 // which slows down drawing. However, for windows displaying OpenGL, having
1617 // a different color space than the screen greatly reduces frame rates, often
1618 // limiting it to the display refresh rate.
1620 // To avoid this, we switch back to the screen color space whenever the
1621 // window is covered by a view with an attached OpenGL context.
1622 - (void) updateColorSpace
1624 NSRect contentRect = [[self contentView] frame];
1625 BOOL coveredByGLView = FALSE;
1626 for (WineContentView* view in [[self contentView] subviews])
1628 if ([view hasGLContext])
1630 NSRect frame = [view convertRect:[view bounds] toView:nil];
1631 if (NSContainsRect(frame, contentRect))
1633 coveredByGLView = TRUE;
1639 if (coveredByGLView)
1640 [self setColorSpace:nil];
1642 [self setColorSpace:[NSColorSpace genericRGBColorSpace]];
1647 * ---------- NSResponder method overrides ----------
1649 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1651 - (void) flagsChanged:(NSEvent *)theEvent
1653 static const struct {
1657 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1658 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1659 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1660 { NX_DEVICELCTLKEYMASK, kVK_Control },
1661 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1662 { NX_DEVICELALTKEYMASK, kVK_Option },
1663 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1664 { NX_DEVICELCMDKEYMASK, kVK_Command },
1665 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1668 NSUInteger modifierFlags = adjusted_modifiers_for_option_behavior([theEvent modifierFlags]);
1670 int i, last_changed;
1672 fix_device_modifiers_by_generic(&modifierFlags);
1673 changed = modifierFlags ^ lastModifierFlags;
1676 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1677 if (changed & modifiers[i].mask)
1680 for (i = 0; i <= last_changed; i++)
1682 if (changed & modifiers[i].mask)
1684 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1686 if (i == last_changed)
1687 lastModifierFlags = modifierFlags;
1690 lastModifierFlags ^= modifiers[i].mask;
1691 fix_generic_modifiers_by_device(&lastModifierFlags);
1694 // Caps lock generates one event for each press-release action.
1695 // We need to simulate a pair of events for each actual event.
1696 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1698 [self postKey:modifiers[i].keycode
1700 modifiers:lastModifierFlags
1701 event:(NSEvent*)theEvent];
1705 [self postKey:modifiers[i].keycode
1707 modifiers:lastModifierFlags
1708 event:(NSEvent*)theEvent];
1713 - (void) applicationWillHide
1715 savedVisibleState = [self isVisible];
1718 - (void) applicationDidUnhide
1720 if ([self isVisible])
1721 [self becameEligibleParentOrChild];
1726 * ---------- NSWindowDelegate methods ----------
1728 - (NSSize) window:(NSWindow*)window willUseFullScreenContentSize:(NSSize)proposedSize
1730 macdrv_query* query;
1733 query = macdrv_create_query();
1734 query->type = QUERY_MIN_MAX_INFO;
1735 query->window = (macdrv_window)[self retain];
1736 [self.queue query:query timeout:0.5];
1737 macdrv_release_query(query);
1739 size = [self contentMaxSize];
1740 if (proposedSize.width < size.width)
1741 size.width = proposedSize.width;
1742 if (proposedSize.height < size.height)
1743 size.height = proposedSize.height;
1747 - (void)windowDidBecomeKey:(NSNotification *)notification
1749 WineApplicationController* controller = [WineApplicationController sharedController];
1750 NSEvent* event = [controller lastFlagsChanged];
1752 [self flagsChanged:event];
1754 if (causing_becomeKeyWindow == self) return;
1756 [controller windowGotFocus:self];
1759 - (void)windowDidDeminiaturize:(NSNotification *)notification
1761 WineApplicationController* controller = [WineApplicationController sharedController];
1763 if (!ignore_windowDeminiaturize)
1764 [self postDidUnminimizeEvent];
1765 ignore_windowDeminiaturize = FALSE;
1767 [self becameEligibleParentOrChild];
1769 if (fullscreen && [self isOnActiveSpace])
1770 [controller updateFullscreenWindows];
1771 [controller adjustWindowLevels];
1773 if (![self parentWindow])
1774 [self postBroughtForwardEvent];
1776 if (!self.disabled && !self.noActivate)
1778 causing_becomeKeyWindow = self;
1779 [self makeKeyWindow];
1780 causing_becomeKeyWindow = nil;
1781 [controller windowGotFocus:self];
1784 [self windowDidResize:notification];
1787 - (void) windowDidEndLiveResize:(NSNotification *)notification
1789 macdrv_event* event = macdrv_create_event(WINDOW_RESIZE_ENDED, self);
1790 [queue postEvent:event];
1791 macdrv_release_event(event);
1793 self.liveResizeDisplayTimer = nil;
1796 - (void) windowDidEnterFullScreen:(NSNotification*)notification
1798 enteringFullScreen = FALSE;
1799 enteredFullScreenTime = [[NSProcessInfo processInfo] systemUptime];
1802 - (void) windowDidExitFullScreen:(NSNotification*)notification
1804 exitingFullScreen = FALSE;
1805 [self setFrame:nonFullscreenFrame display:YES animate:NO];
1806 [self windowDidResize:nil];
1809 - (void) windowDidFailToEnterFullScreen:(NSWindow*)window
1811 enteringFullScreen = FALSE;
1812 enteredFullScreenTime = 0;
1815 - (void) windowDidFailToExitFullScreen:(NSWindow*)window
1817 exitingFullScreen = FALSE;
1818 [self windowDidResize:nil];
1821 - (void)windowDidMiniaturize:(NSNotification *)notification
1823 if (fullscreen && [self isOnActiveSpace])
1824 [[WineApplicationController sharedController] updateFullscreenWindows];
1827 - (void)windowDidMove:(NSNotification *)notification
1829 [self windowDidResize:notification];
1832 - (void)windowDidResignKey:(NSNotification *)notification
1834 macdrv_event* event;
1836 if (causing_becomeKeyWindow) return;
1838 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1839 [queue postEvent:event];
1840 macdrv_release_event(event);
1843 - (void)windowDidResize:(NSNotification *)notification
1845 macdrv_event* event;
1846 NSRect frame = [self frame];
1848 if ([self inLiveResize])
1850 if (NSMinX(frame) != NSMinX(frameAtResizeStart))
1851 resizingFromLeft = TRUE;
1852 if (NSMaxY(frame) != NSMaxY(frameAtResizeStart))
1853 resizingFromTop = TRUE;
1856 frame = [self contentRectForFrameRect:frame];
1858 if (ignore_windowResize || exitingFullScreen) return;
1860 if ([self preventResizing])
1862 [self setContentMinSize:frame.size];
1863 [self setContentMaxSize:frame.size];
1866 [[WineApplicationController sharedController] flipRect:&frame];
1868 /* Coalesce events by discarding any previous ones still in the queue. */
1869 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1872 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1873 event->window_frame_changed.frame = NSRectToCGRect(frame);
1874 event->window_frame_changed.fullscreen = ([self styleMask] & NSFullScreenWindowMask) != 0;
1875 event->window_frame_changed.in_resize = [self inLiveResize];
1876 [queue postEvent:event];
1877 macdrv_release_event(event);
1879 [[[self contentView] inputContext] invalidateCharacterCoordinates];
1880 [self updateFullscreen];
1883 - (BOOL)windowShouldClose:(id)sender
1885 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1886 [queue postEvent:event];
1887 macdrv_release_event(event);
1891 - (BOOL) windowShouldZoom:(NSWindow*)window toFrame:(NSRect)newFrame
1895 macdrv_event* event = macdrv_create_event(WINDOW_RESTORE_REQUESTED, self);
1896 [queue postEvent:event];
1897 macdrv_release_event(event);
1900 else if (!resizable)
1902 macdrv_event* event = macdrv_create_event(WINDOW_MAXIMIZE_REQUESTED, self);
1903 [queue postEvent:event];
1904 macdrv_release_event(event);
1911 - (void) windowWillClose:(NSNotification*)notification
1915 if (fakingClose) return;
1916 if (latentParentWindow)
1918 [latentParentWindow->latentChildWindows removeObjectIdenticalTo:self];
1919 self.latentParentWindow = nil;
1922 for (child in latentChildWindows)
1924 if (child.latentParentWindow == self)
1925 child.latentParentWindow = nil;
1927 [latentChildWindows removeAllObjects];
1930 - (void) windowWillEnterFullScreen:(NSNotification*)notification
1932 enteringFullScreen = TRUE;
1933 nonFullscreenFrame = [self frame];
1936 - (void) windowWillExitFullScreen:(NSNotification*)notification
1938 exitingFullScreen = TRUE;
1941 - (void)windowWillMiniaturize:(NSNotification *)notification
1943 [self becameIneligibleParentOrChild];
1946 - (NSSize) windowWillResize:(NSWindow*)sender toSize:(NSSize)frameSize
1948 if ([self inLiveResize])
1951 macdrv_query* query;
1953 rect = [self frame];
1954 if (resizingFromLeft)
1955 rect.origin.x = NSMaxX(rect) - frameSize.width;
1956 if (!resizingFromTop)
1957 rect.origin.y = NSMaxY(rect) - frameSize.height;
1958 rect.size = frameSize;
1959 rect = [self contentRectForFrameRect:rect];
1960 [[WineApplicationController sharedController] flipRect:&rect];
1962 query = macdrv_create_query();
1963 query->type = QUERY_RESIZE_SIZE;
1964 query->window = (macdrv_window)[self retain];
1965 query->resize_size.rect = NSRectToCGRect(rect);
1966 query->resize_size.from_left = resizingFromLeft;
1967 query->resize_size.from_top = resizingFromTop;
1969 if ([self.queue query:query timeout:0.1])
1971 rect = NSRectFromCGRect(query->resize_size.rect);
1972 rect = [self frameRectForContentRect:rect];
1973 frameSize = rect.size;
1976 macdrv_release_query(query);
1982 - (void) windowWillStartLiveResize:(NSNotification *)notification
1984 macdrv_query* query = macdrv_create_query();
1985 query->type = QUERY_RESIZE_START;
1986 query->window = (macdrv_window)[self retain];
1988 [self.queue query:query timeout:0.3];
1989 macdrv_release_query(query);
1991 frameAtResizeStart = [self frame];
1992 resizingFromLeft = resizingFromTop = FALSE;
1994 // There's a strange restriction in window redrawing during Cocoa-
1995 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
1996 // that happen synchronously when Cocoa tells us that our window size
1997 // has changed or asynchronously in a short interval thereafter provoke
1998 // the window to redraw. Calls to those methods that happen asynchronously
1999 // a half second or more after the last change of the window size aren't
2000 // heeded until the next resize-related user event (e.g. mouse movement).
2002 // Wine often has a significant delay between when it's been told that
2003 // the window has changed size and when it can flush completed drawing.
2004 // So, our windows would get stuck with incomplete drawing for as long
2005 // as the user holds the mouse button down and doesn't move it.
2007 // We address this by "manually" asking our windows to check if they need
2008 // redrawing every so often (during live resize only).
2009 self.liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
2011 selector:@selector(displayIfNeeded)
2014 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
2015 forMode:NSRunLoopCommonModes];
2018 - (NSRect) windowWillUseStandardFrame:(NSWindow*)window defaultFrame:(NSRect)proposedFrame
2020 macdrv_query* query;
2021 NSRect currentContentRect, proposedContentRect, newContentRect, screenRect;
2024 query = macdrv_create_query();
2025 query->type = QUERY_MIN_MAX_INFO;
2026 query->window = (macdrv_window)[self retain];
2027 [self.queue query:query timeout:0.5];
2028 macdrv_release_query(query);
2030 currentContentRect = [self contentRectForFrameRect:[self frame]];
2031 proposedContentRect = [self contentRectForFrameRect:proposedFrame];
2033 maxSize = [self contentMaxSize];
2034 newContentRect.size.width = MIN(NSWidth(proposedContentRect), maxSize.width);
2035 newContentRect.size.height = MIN(NSHeight(proposedContentRect), maxSize.height);
2037 // Try to keep the top-left corner where it is.
2038 newContentRect.origin.x = NSMinX(currentContentRect);
2039 newContentRect.origin.y = NSMaxY(currentContentRect) - NSHeight(newContentRect);
2041 // If that pushes the bottom or right off the screen, pull it up and to the left.
2042 screenRect = [self contentRectForFrameRect:[[self screen] visibleFrame]];
2043 if (NSMaxX(newContentRect) > NSMaxX(screenRect))
2044 newContentRect.origin.x = NSMaxX(screenRect) - NSWidth(newContentRect);
2045 if (NSMinY(newContentRect) < NSMinY(screenRect))
2046 newContentRect.origin.y = NSMinY(screenRect);
2048 // If that pushes the top or left off the screen, push it down and the right
2049 // again. Do this last because the top-left corner is more important than the
2051 if (NSMinX(newContentRect) < NSMinX(screenRect))
2052 newContentRect.origin.x = NSMinX(screenRect);
2053 if (NSMaxY(newContentRect) > NSMaxY(screenRect))
2054 newContentRect.origin.y = NSMaxY(screenRect) - NSHeight(newContentRect);
2056 return [self frameRectForContentRect:newContentRect];
2061 * ---------- NSPasteboardOwner methods ----------
2063 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
2065 macdrv_query* query = macdrv_create_query();
2066 query->type = QUERY_PASTEBOARD_DATA;
2067 query->window = (macdrv_window)[self retain];
2068 query->pasteboard_data.type = (CFStringRef)[type copy];
2070 [self.queue query:query timeout:3];
2071 macdrv_release_query(query);
2076 * ---------- NSDraggingDestination methods ----------
2078 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
2080 return [self draggingUpdated:sender];
2083 - (void) draggingExited:(id <NSDraggingInfo>)sender
2085 // This isn't really a query. We don't need any response. However, it
2086 // has to be processed in a similar manner as the other drag-and-drop
2087 // queries in order to maintain the proper order of operations.
2088 macdrv_query* query = macdrv_create_query();
2089 query->type = QUERY_DRAG_EXITED;
2090 query->window = (macdrv_window)[self retain];
2092 [self.queue query:query timeout:0.1];
2093 macdrv_release_query(query);
2096 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
2098 NSDragOperation ret;
2099 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2100 NSPasteboard* pb = [sender draggingPasteboard];
2102 macdrv_query* query = macdrv_create_query();
2103 query->type = QUERY_DRAG_OPERATION;
2104 query->window = (macdrv_window)[self retain];
2105 query->drag_operation.x = pt.x;
2106 query->drag_operation.y = pt.y;
2107 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
2108 query->drag_operation.accepted_op = NSDragOperationNone;
2109 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
2111 [self.queue query:query timeout:3];
2112 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
2113 macdrv_release_query(query);
2118 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
2121 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
2122 NSPasteboard* pb = [sender draggingPasteboard];
2124 macdrv_query* query = macdrv_create_query();
2125 query->type = QUERY_DRAG_DROP;
2126 query->window = (macdrv_window)[self retain];
2127 query->drag_drop.x = pt.x;
2128 query->drag_drop.y = pt.y;
2129 query->drag_drop.op = [sender draggingSourceOperationMask];
2130 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
2132 [self.queue query:query timeout:3 * 60 processEvents:YES];
2133 ret = query->status;
2134 macdrv_release_query(query);
2139 - (BOOL) wantsPeriodicDraggingUpdates
2147 /***********************************************************************
2148 * macdrv_create_cocoa_window
2150 * Create a Cocoa window with the given content frame and features (e.g.
2151 * title bar, close box, etc.).
2153 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
2154 CGRect frame, void* hwnd, macdrv_event_queue queue)
2156 __block WineWindow* window;
2159 window = [[WineWindow createWindowWithFeatures:wf
2160 windowFrame:NSRectFromCGRect(frame)
2162 queue:(WineEventQueue*)queue] retain];
2165 return (macdrv_window)window;
2168 /***********************************************************************
2169 * macdrv_destroy_cocoa_window
2171 * Destroy a Cocoa window.
2173 void macdrv_destroy_cocoa_window(macdrv_window w)
2175 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2176 WineWindow* window = (WineWindow*)w;
2179 [window doOrderOut];
2182 [window.queue discardEventsMatchingMask:-1 forWindow:window];
2188 /***********************************************************************
2189 * macdrv_get_window_hwnd
2191 * Get the hwnd that was set for the window at creation.
2193 void* macdrv_get_window_hwnd(macdrv_window w)
2195 WineWindow* window = (WineWindow*)w;
2199 /***********************************************************************
2200 * macdrv_set_cocoa_window_features
2202 * Update a Cocoa window's features.
2204 void macdrv_set_cocoa_window_features(macdrv_window w,
2205 const struct macdrv_window_features* wf)
2207 WineWindow* window = (WineWindow*)w;
2210 [window setWindowFeatures:wf];
2214 /***********************************************************************
2215 * macdrv_set_cocoa_window_state
2217 * Update a Cocoa window's state.
2219 void macdrv_set_cocoa_window_state(macdrv_window w,
2220 const struct macdrv_window_state* state)
2222 WineWindow* window = (WineWindow*)w;
2225 [window setMacDrvState:state];
2229 /***********************************************************************
2230 * macdrv_set_cocoa_window_title
2232 * Set a Cocoa window's title.
2234 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
2237 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2238 WineWindow* window = (WineWindow*)w;
2239 NSString* titleString;
2242 titleString = [NSString stringWithCharacters:title length:length];
2245 OnMainThreadAsync(^{
2246 [window setTitle:titleString];
2247 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
2248 [NSApp changeWindowsItem:window title:titleString filename:NO];
2254 /***********************************************************************
2255 * macdrv_order_cocoa_window
2257 * Reorder a Cocoa window relative to other windows. If prev is
2258 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
2259 * it is ordered above that window. Otherwise, it is ordered to the
2262 void macdrv_order_cocoa_window(macdrv_window w, macdrv_window p,
2263 macdrv_window n, int activate)
2265 WineWindow* window = (WineWindow*)w;
2266 WineWindow* prev = (WineWindow*)p;
2267 WineWindow* next = (WineWindow*)n;
2269 OnMainThreadAsync(^{
2270 [window orderBelow:prev
2274 [window.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2276 [next.queue discardEventsMatchingMask:event_mask_for_type(WINDOW_BROUGHT_FORWARD)
2280 /***********************************************************************
2281 * macdrv_hide_cocoa_window
2283 * Hides a Cocoa window.
2285 void macdrv_hide_cocoa_window(macdrv_window w)
2287 WineWindow* window = (WineWindow*)w;
2290 [window doOrderOut];
2294 /***********************************************************************
2295 * macdrv_set_cocoa_window_frame
2297 * Move a Cocoa window.
2299 void macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
2301 WineWindow* window = (WineWindow*)w;
2304 [window setFrameFromWine:NSRectFromCGRect(*new_frame)];
2308 /***********************************************************************
2309 * macdrv_get_cocoa_window_frame
2311 * Gets the frame of a Cocoa window.
2313 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
2315 WineWindow* window = (WineWindow*)w;
2320 frame = [window contentRectForFrameRect:[window frame]];
2321 [[WineApplicationController sharedController] flipRect:&frame];
2322 *out_frame = NSRectToCGRect(frame);
2326 /***********************************************************************
2327 * macdrv_set_cocoa_parent_window
2329 * Sets the parent window for a Cocoa window. If parent is NULL, clears
2330 * the parent window.
2332 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
2334 WineWindow* window = (WineWindow*)w;
2337 [window setMacDrvParentWindow:(WineWindow*)parent];
2341 /***********************************************************************
2342 * macdrv_set_window_surface
2344 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
2346 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2347 WineWindow* window = (WineWindow*)w;
2350 window.surface = surface;
2351 window.surface_mutex = mutex;
2357 /***********************************************************************
2358 * macdrv_window_needs_display
2360 * Mark a window as needing display in a specified rect (in non-client
2361 * area coordinates).
2363 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
2365 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2366 WineWindow* window = (WineWindow*)w;
2368 OnMainThreadAsync(^{
2369 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
2375 /***********************************************************************
2376 * macdrv_set_window_shape
2378 * Sets the shape of a Cocoa window from an array of rectangles. If
2379 * rects is NULL, resets the window's shape to its frame.
2381 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
2383 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2384 WineWindow* window = (WineWindow*)w;
2387 if (!rects || !count)
2390 window.shapeData = nil;
2394 size_t length = sizeof(*rects) * count;
2395 if (window.shapeData.length != length || memcmp(window.shapeData.bytes, rects, length))
2400 path = [NSBezierPath bezierPath];
2401 for (i = 0; i < count; i++)
2402 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
2403 window.shape = path;
2404 window.shapeData = [NSData dataWithBytes:rects length:length];
2412 /***********************************************************************
2413 * macdrv_set_window_alpha
2415 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
2417 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2418 WineWindow* window = (WineWindow*)w;
2420 [window setAlphaValue:alpha];
2425 /***********************************************************************
2426 * macdrv_set_window_color_key
2428 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
2431 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2432 WineWindow* window = (WineWindow*)w;
2435 window.colorKeyed = TRUE;
2436 window.colorKeyRed = keyRed;
2437 window.colorKeyGreen = keyGreen;
2438 window.colorKeyBlue = keyBlue;
2439 [window checkTransparency];
2445 /***********************************************************************
2446 * macdrv_clear_window_color_key
2448 void macdrv_clear_window_color_key(macdrv_window w)
2450 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2451 WineWindow* window = (WineWindow*)w;
2454 window.colorKeyed = FALSE;
2455 [window checkTransparency];
2461 /***********************************************************************
2462 * macdrv_window_use_per_pixel_alpha
2464 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
2466 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2467 WineWindow* window = (WineWindow*)w;
2470 window.usePerPixelAlpha = use_per_pixel_alpha;
2471 [window checkTransparency];
2477 /***********************************************************************
2478 * macdrv_give_cocoa_window_focus
2480 * Makes the Cocoa window "key" (gives it keyboard focus). This also
2481 * orders it front and, if its frame was not within the desktop bounds,
2482 * Cocoa will typically move it on-screen.
2484 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
2486 WineWindow* window = (WineWindow*)w;
2489 [window makeFocused:activate];
2493 /***********************************************************************
2494 * macdrv_set_window_min_max_sizes
2496 * Sets the window's minimum and maximum content sizes.
2498 void macdrv_set_window_min_max_sizes(macdrv_window w, CGSize min_size, CGSize max_size)
2500 WineWindow* window = (WineWindow*)w;
2503 [window setWineMinSize:NSSizeFromCGSize(min_size) maxSize:NSSizeFromCGSize(max_size)];
2507 /***********************************************************************
2508 * macdrv_create_view
2510 * Creates and returns a view in the specified rect of the window. The
2511 * caller is responsible for calling macdrv_dispose_view() on the view
2512 * when it is done with it.
2514 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
2516 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2517 WineWindow* window = (WineWindow*)w;
2518 __block WineContentView* view;
2520 if (CGRectIsNull(rect)) rect = CGRectZero;
2523 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2525 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
2526 [view setAutoresizesSubviews:NO];
2527 [nc addObserver:view
2528 selector:@selector(updateGLContexts)
2529 name:NSViewGlobalFrameDidChangeNotification
2531 [nc addObserver:view
2532 selector:@selector(updateGLContexts)
2533 name:NSApplicationDidChangeScreenParametersNotification
2535 [[window contentView] addSubview:view];
2536 [window updateColorSpace];
2540 return (macdrv_view)view;
2543 /***********************************************************************
2544 * macdrv_dispose_view
2546 * Destroys a view previously returned by macdrv_create_view.
2548 void macdrv_dispose_view(macdrv_view v)
2550 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2551 WineContentView* view = (WineContentView*)v;
2554 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
2555 WineWindow* window = (WineWindow*)[view window];
2557 [nc removeObserver:view
2558 name:NSViewGlobalFrameDidChangeNotification
2560 [nc removeObserver:view
2561 name:NSApplicationDidChangeScreenParametersNotification
2563 [view removeFromSuperview];
2565 [window updateColorSpace];
2571 /***********************************************************************
2572 * macdrv_set_view_window_and_frame
2574 * Move a view to a new window and/or position within its window. If w
2575 * is NULL, leave the view in its current window and just change its
2578 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
2580 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2581 WineContentView* view = (WineContentView*)v;
2582 WineWindow* window = (WineWindow*)w;
2584 if (CGRectIsNull(rect)) rect = CGRectZero;
2587 BOOL changedWindow = (window && window != [view window]);
2588 NSRect newFrame = NSRectFromCGRect(rect);
2589 NSRect oldFrame = [view frame];
2590 BOOL needUpdateWindowColorSpace = FALSE;
2594 WineWindow* oldWindow = (WineWindow*)[view window];
2595 [view removeFromSuperview];
2596 [oldWindow updateColorSpace];
2597 [[window contentView] addSubview:view];
2598 needUpdateWindowColorSpace = TRUE;
2601 if (!NSEqualRects(oldFrame, newFrame))
2604 [[view superview] setNeedsDisplayInRect:oldFrame];
2605 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
2606 [view setFrameSize:newFrame.size];
2607 else if (NSEqualSizes(oldFrame.size, newFrame.size))
2608 [view setFrameOrigin:newFrame.origin];
2610 [view setFrame:newFrame];
2611 [view setNeedsDisplay:YES];
2612 needUpdateWindowColorSpace = TRUE;
2615 if (needUpdateWindowColorSpace)
2616 [(WineWindow*)[view window] updateColorSpace];
2622 /***********************************************************************
2623 * macdrv_add_view_opengl_context
2625 * Add an OpenGL context to the list being tracked for each view.
2627 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2629 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2630 WineContentView* view = (WineContentView*)v;
2631 WineOpenGLContext *context = (WineOpenGLContext*)c;
2634 [view addGLContext:context];
2640 /***********************************************************************
2641 * macdrv_remove_view_opengl_context
2643 * Add an OpenGL context to the list being tracked for each view.
2645 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
2647 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
2648 WineContentView* view = (WineContentView*)v;
2649 WineOpenGLContext *context = (WineOpenGLContext*)c;
2651 OnMainThreadAsync(^{
2652 [view removeGLContext:context];
2658 /***********************************************************************
2659 * macdrv_window_background_color
2661 * Returns the standard Mac window background color as a 32-bit value of
2662 * the form 0x00rrggbb.
2664 uint32_t macdrv_window_background_color(void)
2666 static uint32_t result;
2667 static dispatch_once_t once;
2669 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
2670 // color spaces (RGB or grayscale). So, the only way to get RGB values out
2671 // of it is to draw with it.
2672 dispatch_once(&once, ^{
2674 unsigned char rgbx[4];
2675 unsigned char *planes = rgbx;
2676 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
2683 colorSpaceName:NSCalibratedRGBColorSpace
2687 [NSGraphicsContext saveGraphicsState];
2688 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
2689 [[NSColor windowBackgroundColor] set];
2690 NSRectFill(NSMakeRect(0, 0, 1, 1));
2691 [NSGraphicsContext restoreGraphicsState];
2693 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
2700 /***********************************************************************
2701 * macdrv_send_text_input_event
2703 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2708 WineWindow* window = (WineWindow*)[NSApp keyWindow];
2709 if (![window isKindOfClass:[WineWindow class]])
2711 window = (WineWindow*)[NSApp mainWindow];
2712 if (![window isKindOfClass:[WineWindow class]])
2713 window = [[WineApplicationController sharedController] frontWineWindow];
2718 NSUInteger localFlags = flags;
2722 window.imeData = data;
2723 fix_device_modifiers_by_generic(&localFlags);
2725 // An NSEvent created with +keyEventWithType:... is internally marked
2726 // as synthetic and doesn't get sent through input methods. But one
2727 // created from a CGEvent doesn't have that problem.
2728 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2729 CGEventSetFlags(c, localFlags);
2730 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2731 event = [NSEvent eventWithCGEvent:c];
2734 window.commandDone = FALSE;
2735 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;