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 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
33 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
37 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
39 NSUInteger style_mask;
43 style_mask = NSTitledWindowMask;
44 if (wf->close_button) style_mask |= NSClosableWindowMask;
45 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
46 if (wf->resizable) style_mask |= NSResizableWindowMask;
47 if (wf->utility) style_mask |= NSUtilityWindowMask;
49 else style_mask = NSBorderlessWindowMask;
55 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
58 for (screen in screens)
60 if (NSIntersectsRect(frame, [screen frame]))
67 static NSScreen* screen_covered_by_rect(NSRect rect, NSArray* screens)
69 for (NSScreen* screen in screens)
71 if (NSContainsRect(rect, [screen frame]))
78 /* We rely on the supposedly device-dependent modifier flags to distinguish the
79 keys on the left side of the keyboard from those on the right. Some event
80 sources don't set those device-depdendent flags. If we see a device-independent
81 flag for a modifier without either corresponding device-dependent flag, assume
83 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
85 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
86 *modifiers |= NX_DEVICELCMDKEYMASK;
87 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
88 *modifiers |= NX_DEVICELSHIFTKEYMASK;
89 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
90 *modifiers |= NX_DEVICELCTLKEYMASK;
91 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
92 *modifiers |= NX_DEVICELALTKEYMASK;
95 /* As we manipulate individual bits of a modifier mask, we can end up with
96 inconsistent sets of flags. In particular, we might set or clear one of the
97 left/right-specific bits, but not the corresponding non-side-specific bit.
98 Fix that. If either side-specific bit is set, set the non-side-specific bit,
99 otherwise clear it. */
100 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
102 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
103 *modifiers |= NX_COMMANDMASK;
105 *modifiers &= ~NX_COMMANDMASK;
106 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
107 *modifiers |= NX_SHIFTMASK;
109 *modifiers &= ~NX_SHIFTMASK;
110 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
111 *modifiers |= NX_CONTROLMASK;
113 *modifiers &= ~NX_CONTROLMASK;
114 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
115 *modifiers |= NX_ALTERNATEMASK;
117 *modifiers &= ~NX_ALTERNATEMASK;
121 @interface WineContentView : NSView <NSTextInputClient>
123 NSMutableArray* glContexts;
124 NSMutableArray* pendingGlContexts;
126 NSMutableAttributedString* markedText;
127 NSRange markedTextSelection;
130 - (void) addGLContext:(WineOpenGLContext*)context;
131 - (void) removeGLContext:(WineOpenGLContext*)context;
132 - (void) updateGLContexts;
137 @interface WineWindow ()
139 @property (nonatomic) BOOL disabled;
140 @property (nonatomic) BOOL noActivate;
141 @property (readwrite, nonatomic) BOOL floating;
142 @property (retain, nonatomic) NSWindow* latentParentWindow;
144 @property (nonatomic) void* hwnd;
145 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
147 @property (nonatomic) void* surface;
148 @property (nonatomic) pthread_mutex_t* surface_mutex;
150 @property (copy, nonatomic) NSBezierPath* shape;
151 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
152 @property (readonly, nonatomic) BOOL needsTransparency;
154 @property (nonatomic) BOOL colorKeyed;
155 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
156 @property (nonatomic) BOOL usePerPixelAlpha;
158 @property (assign, nonatomic) void* imeData;
159 @property (nonatomic) BOOL commandDone;
164 @implementation WineContentView
168 [markedText release];
169 [glContexts release];
170 [pendingGlContexts release];
179 - (void) drawRect:(NSRect)rect
181 WineWindow* window = (WineWindow*)[self window];
183 for (WineOpenGLContext* context in pendingGlContexts)
184 context.needsUpdate = TRUE;
185 [glContexts addObjectsFromArray:pendingGlContexts];
186 [pendingGlContexts removeAllObjects];
188 if ([window contentView] != self)
191 if (window.surface && window.surface_mutex &&
192 !pthread_mutex_lock(window.surface_mutex))
197 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
199 CGContextRef context;
202 [window.shape addClip];
204 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
205 CGContextSetBlendMode(context, kCGBlendModeCopy);
207 for (i = 0; i < count; i++)
212 imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
213 image = create_surface_image(window.surface, &imageRect, FALSE);
217 if (window.colorKeyed)
219 CGImageRef maskedImage;
220 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
221 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
222 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
223 maskedImage = CGImageCreateWithMaskingColors(image, components);
226 CGImageRelease(image);
231 CGContextDrawImage(context, imageRect, image);
233 CGImageRelease(image);
238 pthread_mutex_unlock(window.surface_mutex);
241 // If the window may be transparent, then we have to invalidate the
242 // shadow every time we draw. Also, if this is the first time we've
243 // drawn since changing from transparent to opaque.
244 if (![window isOpaque] || window.shapeChangedSinceLastDraw)
246 window.shapeChangedSinceLastDraw = FALSE;
247 [window invalidateShadow];
251 - (void) addGLContext:(WineOpenGLContext*)context
254 glContexts = [[NSMutableArray alloc] init];
255 if (!pendingGlContexts)
256 pendingGlContexts = [[NSMutableArray alloc] init];
257 [pendingGlContexts addObject:context];
258 [self setNeedsDisplay:YES];
261 - (void) removeGLContext:(WineOpenGLContext*)context
263 [glContexts removeObjectIdenticalTo:context];
264 [pendingGlContexts removeObjectIdenticalTo:context];
267 - (void) updateGLContexts
269 for (WineOpenGLContext* context in glContexts)
270 context.needsUpdate = TRUE;
273 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
278 - (BOOL) preservesContentDuringLiveResize
280 // Returning YES from this tells Cocoa to keep our view's content during
281 // a Cocoa-driven resize. In theory, we're also supposed to override
282 // -setFrameSize: to mark exposed sections as needing redisplay, but
283 // user32 will take care of that in a roundabout way. This way, we don't
284 // redraw until the window surface is flushed.
286 // This doesn't do anything when we resize the window ourselves.
290 - (BOOL)acceptsFirstResponder
292 return [[self window] contentView] == self;
295 - (void) completeText:(NSString*)text
298 WineWindow* window = (WineWindow*)[self window];
300 event = macdrv_create_event(IM_SET_TEXT, window);
301 event->im_set_text.data = [window imeData];
302 event->im_set_text.text = (CFStringRef)[text copy];
303 event->im_set_text.complete = TRUE;
305 [[window queue] postEvent:event];
307 macdrv_release_event(event);
309 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
310 markedTextSelection = NSMakeRange(0, 0);
311 [[self inputContext] discardMarkedText];
315 * ---------- NSTextInputClient methods ----------
317 - (NSTextInputContext*) inputContext
320 markedText = [[NSMutableAttributedString alloc] init];
321 return [super inputContext];
324 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
326 if ([string isKindOfClass:[NSAttributedString class]])
327 string = [string string];
329 if ([string isKindOfClass:[NSString class]])
330 [self completeText:string];
333 - (void) doCommandBySelector:(SEL)aSelector
335 [(WineWindow*)[self window] setCommandDone:TRUE];
338 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
340 if ([string isKindOfClass:[NSAttributedString class]])
341 string = [string string];
343 if ([string isKindOfClass:[NSString class]])
346 WineWindow* window = (WineWindow*)[self window];
348 if (replacementRange.location == NSNotFound)
349 replacementRange = NSMakeRange(0, [markedText length]);
351 [markedText replaceCharactersInRange:replacementRange withString:string];
352 markedTextSelection = selectedRange;
353 markedTextSelection.location += replacementRange.location;
355 event = macdrv_create_event(IM_SET_TEXT, window);
356 event->im_set_text.data = [window imeData];
357 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
358 event->im_set_text.complete = FALSE;
359 event->im_set_text.cursor_pos = markedTextSelection.location;
361 [[window queue] postEvent:event];
363 macdrv_release_event(event);
365 [[self inputContext] invalidateCharacterCoordinates];
371 [self completeText:nil];
374 - (NSRange) selectedRange
376 return markedTextSelection;
379 - (NSRange) markedRange
381 NSRange range = NSMakeRange(0, [markedText length]);
383 range.location = NSNotFound;
387 - (BOOL) hasMarkedText
389 return [markedText length] > 0;
392 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
394 if (aRange.location >= [markedText length])
397 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
399 *actualRange = aRange;
400 return [markedText attributedSubstringFromRange:aRange];
403 - (NSArray*) validAttributesForMarkedText
405 return [NSArray array];
408 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
411 WineWindow* window = (WineWindow*)[self window];
414 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
416 query = macdrv_create_query();
417 query->type = QUERY_IME_CHAR_RECT;
418 query->window = (macdrv_window)[window retain];
419 query->ime_char_rect.data = [window imeData];
420 query->ime_char_rect.range = CFRangeMake(aRange.location, aRange.length);
422 if ([window.queue query:query timeout:1])
424 aRange = NSMakeRange(query->ime_char_rect.range.location, query->ime_char_rect.range.length);
425 ret = NSRectFromCGRect(query->ime_char_rect.rect);
426 [[WineApplicationController sharedController] flipRect:&ret];
429 ret = NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
431 macdrv_release_query(query);
434 *actualRange = aRange;
438 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
443 - (NSInteger) windowLevel
445 return [[self window] level];
451 @implementation WineWindow
453 @synthesize disabled, noActivate, floating, fullscreen, latentParentWindow, hwnd, queue;
454 @synthesize surface, surface_mutex;
455 @synthesize shape, shapeChangedSinceLastDraw;
456 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
457 @synthesize usePerPixelAlpha;
458 @synthesize imeData, commandDone;
460 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
461 windowFrame:(NSRect)window_frame
463 queue:(WineEventQueue*)queue
466 WineContentView* contentView;
467 NSTrackingArea* trackingArea;
469 [[WineApplicationController sharedController] flipRect:&window_frame];
471 window = [[[self alloc] initWithContentRect:window_frame
472 styleMask:style_mask_for_features(wf)
473 backing:NSBackingStoreBuffered
474 defer:YES] autorelease];
476 if (!window) return nil;
478 /* Standardize windows to eliminate differences between titled and
479 borderless windows and between NSWindow and NSPanel. */
480 [window setHidesOnDeactivate:NO];
481 [window setReleasedWhenClosed:NO];
483 [window disableCursorRects];
484 [window setShowsResizeIndicator:NO];
485 [window setHasShadow:wf->shadow];
486 [window setAcceptsMouseMovedEvents:YES];
487 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
488 [window setDelegate:window];
490 window.queue = queue;
492 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
493 (NSString*)kUTTypeContent,
496 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
499 [contentView setAutoresizesSubviews:NO];
501 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
502 because they give us mouse moves in the background. */
503 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
504 options:(NSTrackingMouseMoved |
505 NSTrackingActiveAlways |
506 NSTrackingInVisibleRect)
508 userInfo:nil] autorelease];
511 [contentView addTrackingArea:trackingArea];
513 [window setContentView:contentView];
514 [window setInitialFirstResponder:contentView];
516 [[NSNotificationCenter defaultCenter] addObserver:window
517 selector:@selector(updateFullscreen)
518 name:NSApplicationDidChangeScreenParametersNotification
520 [window updateFullscreen];
527 [[NSNotificationCenter defaultCenter] removeObserver:self];
528 [liveResizeDisplayTimer invalidate];
529 [liveResizeDisplayTimer release];
531 [latentParentWindow release];
536 - (void) adjustFeaturesForState
538 NSUInteger style = [self styleMask];
540 if (style & NSClosableWindowMask)
541 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
542 if (style & NSMiniaturizableWindowMask)
543 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
544 if (style & NSResizableWindowMask)
545 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
548 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
550 NSUInteger currentStyle = [self styleMask];
551 NSUInteger newStyle = style_mask_for_features(wf);
553 if (newStyle != currentStyle)
555 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
556 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
557 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
559 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
560 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
561 // just NSTitledWindowMask, the window buttons should disappear rather
562 // than just being disabled. But they don't. Similarly in reverse.
563 // The workaround is to also toggle NSClosableWindowMask at the same time.
564 [self setStyleMask:newStyle ^ NSClosableWindowMask];
566 [self setStyleMask:newStyle];
569 [self adjustFeaturesForState];
570 [self setHasShadow:wf->shadow];
575 return [self isVisible] || [self isMiniaturized];
578 - (NSInteger) minimumLevelForActive:(BOOL)active
582 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
583 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
584 level = NSFloatingWindowLevel;
586 level = NSNormalWindowLevel;
592 captured = (fullscreen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
594 if (captured || fullscreen)
597 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
599 level = NSMainMenuWindowLevel + 1;
609 - (void) setMacDrvState:(const struct macdrv_window_state*)state
611 NSWindowCollectionBehavior behavior;
613 self.disabled = state->disabled;
614 self.noActivate = state->no_activate;
616 if (self.floating != state->floating)
618 self.floating = state->floating;
619 [[WineApplicationController sharedController] adjustWindowLevels];
622 behavior = NSWindowCollectionBehaviorDefault;
623 if (state->excluded_by_expose)
624 behavior |= NSWindowCollectionBehaviorTransient;
626 behavior |= NSWindowCollectionBehaviorManaged;
627 if (state->excluded_by_cycle)
629 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
630 if ([self isOrderedIn])
631 [NSApp removeWindowsItem:self];
635 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
636 if ([self isOrderedIn])
637 [NSApp addWindowsItem:self title:[self title] filename:NO];
639 [self setCollectionBehavior:behavior];
641 pendingMinimize = FALSE;
642 if (state->minimized && ![self isMiniaturized])
644 if ([self isVisible])
646 ignore_windowMiniaturize = TRUE;
647 [self miniaturize:nil];
650 pendingMinimize = TRUE;
652 else if (!state->minimized && [self isMiniaturized])
654 ignore_windowDeminiaturize = TRUE;
655 [self deminiaturize:nil];
658 /* Whatever events regarding minimization might have been in the queue are now stale. */
659 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
660 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
664 // Determine if, among Wine windows, this window is directly above or below
665 // a given other Wine window with no other Wine window intervening.
666 // Intervening non-Wine windows are ignored.
667 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
669 NSNumber* windowNumber;
670 NSNumber* otherWindowNumber;
671 NSArray* windowNumbers;
672 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
674 if (![self isVisible] || ![otherWindow isVisible])
677 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
678 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
679 windowNumbers = [[self class] windowNumbersWithOptions:0];
680 windowIndex = [windowNumbers indexOfObject:windowNumber];
681 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
683 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
686 if (orderingMode == NSWindowAbove)
688 lowIndex = windowIndex;
689 highIndex = otherWindowIndex;
691 else if (orderingMode == NSWindowBelow)
693 lowIndex = otherWindowIndex;
694 highIndex = windowIndex;
699 if (highIndex <= lowIndex)
702 for (i = lowIndex + 1; i < highIndex; i++)
704 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
705 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
706 if ([interveningWindow isKindOfClass:[WineWindow class]])
713 /* Returns whether or not the window was ordered in, which depends on if
714 its frame intersects any screen. */
715 - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
717 WineApplicationController* controller = [WineApplicationController sharedController];
718 BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
719 if (on_screen && ![self isMiniaturized])
721 BOOL needAdjustWindowLevels = FALSE;
722 BOOL wasVisible = [self isVisible];
724 [controller transformProcessToForeground];
726 NSDisableScreenUpdates();
728 if (latentParentWindow)
730 if ([latentParentWindow level] > [self level])
731 [self setLevel:[latentParentWindow level]];
732 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
733 self.latentParentWindow = nil;
734 needAdjustWindowLevels = TRUE;
738 WineWindow* other = [prev isVisible] ? prev : next;
739 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
741 if (![self isOrdered:orderingMode relativeTo:other])
743 // This window level may not be right for this window based
744 // on floating-ness, fullscreen-ness, etc. But we set it
745 // temporarily to allow us to order the windows properly.
746 // Then the levels get fixed by -adjustWindowLevels.
747 if ([self level] != [other level])
748 [self setLevel:[other level]];
749 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
750 needAdjustWindowLevels = TRUE;
755 // Again, temporarily set level to make sure we can order to
757 next = [controller frontWineWindow];
758 if (next && [self level] < [next level])
759 [self setLevel:[next level]];
760 [self orderFront:nil];
761 needAdjustWindowLevels = TRUE;
763 if (needAdjustWindowLevels)
765 if (!wasVisible && fullscreen && [self isOnActiveSpace])
766 [controller updateFullscreenWindows];
767 [controller adjustWindowLevels];
772 ignore_windowMiniaturize = TRUE;
773 [self miniaturize:nil];
774 pendingMinimize = FALSE;
777 NSEnableScreenUpdates();
779 /* Cocoa may adjust the frame when the window is ordered onto the screen.
780 Generate a frame-changed event just in case. The back end will ignore
781 it if nothing actually changed. */
782 [self windowDidResize:nil];
784 if (![self isExcludedFromWindowsMenu])
785 [NSApp addWindowsItem:self title:[self title] filename:NO];
793 WineApplicationController* controller = [WineApplicationController sharedController];
794 BOOL wasVisible = [self isVisible];
795 BOOL wasOnActiveSpace = [self isOnActiveSpace];
797 if ([self isMiniaturized])
798 pendingMinimize = TRUE;
799 self.latentParentWindow = [self parentWindow];
800 [latentParentWindow removeChildWindow:self];
802 if (wasVisible && wasOnActiveSpace && fullscreen)
803 [controller updateFullscreenWindows];
804 [controller adjustWindowLevels];
805 [NSApp removeWindowsItem:self];
808 - (void) updateFullscreen
810 NSRect contentRect = [self contentRectForFrameRect:[self frame]];
811 BOOL nowFullscreen = (screen_covered_by_rect(contentRect, [NSScreen screens]) != nil);
813 if (nowFullscreen != fullscreen)
815 WineApplicationController* controller = [WineApplicationController sharedController];
817 fullscreen = nowFullscreen;
818 if ([self isVisible] && [self isOnActiveSpace])
819 [controller updateFullscreenWindows];
821 [controller adjustWindowLevels];
825 - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
827 NSArray* screens = [NSScreen screens];
828 BOOL on_screen = [self isOrderedIn];
830 if (![screens count]) return on_screen;
832 /* Origin is (left, top) in a top-down space. Need to convert it to
833 (left, bottom) in a bottom-up space. */
834 [[WineApplicationController sharedController] flipRect:&contentRect];
838 on_screen = frame_intersects_screens(contentRect, screens);
843 /* The back end is establishing a new window size and position. It's
844 not interested in any stale events regarding those that may be sitting
846 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
849 if (!NSIsEmptyRect(contentRect))
851 NSRect frame, oldFrame;
853 oldFrame = [self frame];
854 frame = [self frameRectForContentRect:contentRect];
855 if (!NSEqualRects(frame, oldFrame))
857 if (NSEqualSizes(frame.size, oldFrame.size))
858 [self setFrameOrigin:frame.origin];
860 [self setFrame:frame display:YES];
862 [self updateFullscreen];
866 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
867 event. The back end will ignore it if nothing actually changed. */
868 [self windowDidResize:nil];
876 - (void) setMacDrvParentWindow:(WineWindow*)parent
878 if ([self parentWindow] != parent)
880 [[self parentWindow] removeChildWindow:self];
881 self.latentParentWindow = nil;
882 if ([self isVisible] && parent)
884 if ([parent level] > [self level])
885 [self setLevel:[parent level]];
886 [parent addChildWindow:self ordered:NSWindowAbove];
887 [[WineApplicationController sharedController] adjustWindowLevels];
890 self.latentParentWindow = parent;
894 - (void) setDisabled:(BOOL)newValue
896 if (disabled != newValue)
899 [self adjustFeaturesForState];
903 NSSize size = [self frame].size;
904 [self setMinSize:size];
905 [self setMaxSize:size];
909 [self setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
910 [self setMinSize:NSZeroSize];
915 - (BOOL) needsTransparency
917 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
920 - (void) checkTransparency
922 if (![self isOpaque] && !self.needsTransparency)
924 [self setBackgroundColor:[NSColor windowBackgroundColor]];
925 [self setOpaque:YES];
927 else if ([self isOpaque] && self.needsTransparency)
929 [self setBackgroundColor:[NSColor clearColor]];
934 - (void) setShape:(NSBezierPath*)newShape
936 if (shape == newShape) return;
937 if (shape && newShape && [shape isEqual:newShape]) return;
941 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
945 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
947 shape = [newShape copy];
948 self.shapeChangedSinceLastDraw = TRUE;
950 [self checkTransparency];
953 - (void) makeFocused:(BOOL)activate
955 WineApplicationController* controller = [WineApplicationController sharedController];
958 BOOL wasVisible = [self isVisible];
960 [controller transformProcessToForeground];
962 /* If a borderless window is offscreen, orderFront: won't move
963 it onscreen like it would for a titled window. Do that ourselves. */
964 screens = [NSScreen screens];
965 if (!([self styleMask] & NSTitledWindowMask) && ![self isOrderedIn] &&
966 !frame_intersects_screens([self frame], screens))
968 NSScreen* primaryScreen = [screens objectAtIndex:0];
969 NSRect frame = [primaryScreen frame];
970 [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
971 frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
972 [self setFrame:frame display:YES];
976 [NSApp activateIgnoringOtherApps:YES];
978 NSDisableScreenUpdates();
980 if (latentParentWindow)
982 if ([latentParentWindow level] > [self level])
983 [self setLevel:[latentParentWindow level]];
984 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
985 self.latentParentWindow = nil;
987 front = [controller frontWineWindow];
988 if (front && [self level] < [front level])
989 [self setLevel:[front level]];
990 [self orderFront:nil];
991 if (!wasVisible && fullscreen && [self isOnActiveSpace])
992 [controller updateFullscreenWindows];
993 [controller adjustWindowLevels];
997 ignore_windowMiniaturize = TRUE;
998 [self miniaturize:nil];
999 pendingMinimize = FALSE;
1002 NSEnableScreenUpdates();
1004 causing_becomeKeyWindow = TRUE;
1005 [self makeKeyWindow];
1006 causing_becomeKeyWindow = FALSE;
1008 if (![self isExcludedFromWindowsMenu])
1009 [NSApp addWindowsItem:self title:[self title] filename:NO];
1011 /* Cocoa may adjust the frame when the window is ordered onto the screen.
1012 Generate a frame-changed event just in case. The back end will ignore
1013 it if nothing actually changed. */
1014 [self windowDidResize:nil];
1017 - (void) postKey:(uint16_t)keyCode
1018 pressed:(BOOL)pressed
1019 modifiers:(NSUInteger)modifiers
1020 event:(NSEvent*)theEvent
1022 macdrv_event* event;
1024 WineApplicationController* controller = [WineApplicationController sharedController];
1026 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
1027 event->key.keycode = keyCode;
1028 event->key.modifiers = modifiers;
1029 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1031 if ((cgevent = [theEvent CGEvent]))
1033 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1034 kCGKeyboardEventKeyboardType);
1035 if (keyboardType != controller.keyboardType)
1037 controller.keyboardType = keyboardType;
1038 [controller keyboardSelectionDidChange];
1042 [queue postEvent:event];
1044 macdrv_release_event(event);
1047 - (void) postKeyEvent:(NSEvent *)theEvent
1049 [self flagsChanged:theEvent];
1050 [self postKey:[theEvent keyCode]
1051 pressed:[theEvent type] == NSKeyDown
1052 modifiers:[theEvent modifierFlags]
1058 * ---------- NSWindow method overrides ----------
1060 - (BOOL) canBecomeKeyWindow
1062 if (causing_becomeKeyWindow) return YES;
1063 if (self.disabled || self.noActivate) return NO;
1064 return [self isKeyWindow];
1067 - (BOOL) canBecomeMainWindow
1069 return [self canBecomeKeyWindow];
1072 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1074 // If a window is sized to completely cover a screen, then it's in
1075 // full-screen mode. In that case, we don't allow NSWindow to constrain
1077 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1078 if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
1079 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1083 - (BOOL) isExcludedFromWindowsMenu
1085 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1088 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1090 BOOL ret = [super validateMenuItem:menuItem];
1092 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1093 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1098 /* We don't call this. It's the action method of the items in the Window menu. */
1099 - (void) makeKeyAndOrderFront:(id)sender
1101 WineApplicationController* controller = [WineApplicationController sharedController];
1102 WineWindow* front = [controller frontWineWindow];
1103 BOOL wasVisible = [self isVisible];
1105 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1106 [controller windowGotFocus:self];
1108 if (front && [self level] < [front level])
1109 [self setLevel:[front level]];
1110 [self orderFront:nil];
1111 if (!wasVisible && fullscreen && [self isOnActiveSpace])
1112 [controller updateFullscreenWindows];
1113 [controller adjustWindowLevels];
1115 if (pendingMinimize)
1117 ignore_windowMiniaturize = TRUE;
1118 [self miniaturize:nil];
1119 pendingMinimize = FALSE;
1123 - (void) sendEvent:(NSEvent*)event
1125 WineApplicationController* controller = [WineApplicationController sharedController];
1127 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1128 interface control. For example, Control-Tab switches focus among
1129 views. We want to bypass that feature, so directly route key-down
1130 events to -keyDown:. */
1131 if ([event type] == NSKeyDown)
1132 [[self firstResponder] keyDown:event];
1135 if ([event type] == NSLeftMouseDown)
1137 /* Since our windows generally claim they can't be made key, clicks
1138 in their title bars are swallowed by the theme frame stuff. So,
1139 we hook directly into the event stream and assume that any click
1140 in the window will activate it, if Wine and the Win32 program
1142 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1143 [controller windowGotFocus:self];
1146 [super sendEvent:event];
1152 * ---------- NSResponder method overrides ----------
1154 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1155 - (void) keyUp:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1157 - (void) flagsChanged:(NSEvent *)theEvent
1159 static const struct {
1163 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1164 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1165 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1166 { NX_DEVICELCTLKEYMASK, kVK_Control },
1167 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1168 { NX_DEVICELALTKEYMASK, kVK_Option },
1169 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1170 { NX_DEVICELCMDKEYMASK, kVK_Command },
1171 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1174 NSUInteger modifierFlags = [theEvent modifierFlags];
1176 int i, last_changed;
1178 fix_device_modifiers_by_generic(&modifierFlags);
1179 changed = modifierFlags ^ lastModifierFlags;
1182 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1183 if (changed & modifiers[i].mask)
1186 for (i = 0; i <= last_changed; i++)
1188 if (changed & modifiers[i].mask)
1190 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1192 if (i == last_changed)
1193 lastModifierFlags = modifierFlags;
1196 lastModifierFlags ^= modifiers[i].mask;
1197 fix_generic_modifiers_by_device(&lastModifierFlags);
1200 // Caps lock generates one event for each press-release action.
1201 // We need to simulate a pair of events for each actual event.
1202 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1204 [self postKey:modifiers[i].keycode
1206 modifiers:lastModifierFlags
1207 event:(NSEvent*)theEvent];
1211 [self postKey:modifiers[i].keycode
1213 modifiers:lastModifierFlags
1214 event:(NSEvent*)theEvent];
1221 * ---------- NSWindowDelegate methods ----------
1223 - (void)windowDidBecomeKey:(NSNotification *)notification
1225 WineApplicationController* controller = [WineApplicationController sharedController];
1226 NSEvent* event = [controller lastFlagsChanged];
1228 [self flagsChanged:event];
1230 if (causing_becomeKeyWindow) return;
1232 [controller windowGotFocus:self];
1235 - (void)windowDidDeminiaturize:(NSNotification *)notification
1237 WineApplicationController* controller = [WineApplicationController sharedController];
1239 if (!ignore_windowDeminiaturize)
1241 macdrv_event* event;
1243 /* Coalesce events by discarding any previous ones still in the queue. */
1244 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1245 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1248 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1249 [queue postEvent:event];
1250 macdrv_release_event(event);
1253 ignore_windowDeminiaturize = FALSE;
1255 if (fullscreen && [self isOnActiveSpace])
1256 [controller updateFullscreenWindows];
1257 [controller adjustWindowLevels];
1259 if (!self.disabled && !self.noActivate)
1261 causing_becomeKeyWindow = TRUE;
1262 [self makeKeyWindow];
1263 causing_becomeKeyWindow = FALSE;
1264 [controller windowGotFocus:self];
1267 [self windowDidResize:notification];
1270 - (void) windowDidEndLiveResize:(NSNotification *)notification
1272 [liveResizeDisplayTimer invalidate];
1273 [liveResizeDisplayTimer release];
1274 liveResizeDisplayTimer = nil;
1277 - (void)windowDidMiniaturize:(NSNotification *)notification
1279 if (fullscreen && [self isOnActiveSpace])
1280 [[WineApplicationController sharedController] updateFullscreenWindows];
1283 - (void)windowDidMove:(NSNotification *)notification
1285 [self windowDidResize:notification];
1288 - (void)windowDidResignKey:(NSNotification *)notification
1290 macdrv_event* event;
1292 if (causing_becomeKeyWindow) return;
1294 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1295 [queue postEvent:event];
1296 macdrv_release_event(event);
1299 - (void)windowDidResize:(NSNotification *)notification
1301 macdrv_event* event;
1302 NSRect frame = [self frame];
1306 NSSize size = frame.size;
1307 [self setMinSize:size];
1308 [self setMaxSize:size];
1311 frame = [self contentRectForFrameRect:frame];
1312 [[WineApplicationController sharedController] flipRect:&frame];
1314 /* Coalesce events by discarding any previous ones still in the queue. */
1315 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1318 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1319 event->window_frame_changed.frame = NSRectToCGRect(frame);
1320 [queue postEvent:event];
1321 macdrv_release_event(event);
1323 [[[self contentView] inputContext] invalidateCharacterCoordinates];
1324 [self updateFullscreen];
1327 - (BOOL)windowShouldClose:(id)sender
1329 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1330 [queue postEvent:event];
1331 macdrv_release_event(event);
1335 - (void)windowWillMiniaturize:(NSNotification *)notification
1337 if (!ignore_windowMiniaturize)
1339 macdrv_event* event;
1341 /* Coalesce events by discarding any previous ones still in the queue. */
1342 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1343 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1346 event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
1347 [queue postEvent:event];
1348 macdrv_release_event(event);
1351 ignore_windowMiniaturize = FALSE;
1354 - (void) windowWillStartLiveResize:(NSNotification *)notification
1356 // There's a strange restriction in window redrawing during Cocoa-
1357 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
1358 // that happen synchronously when Cocoa tells us that our window size
1359 // has changed or asynchronously in a short interval thereafter provoke
1360 // the window to redraw. Calls to those methods that happen asynchronously
1361 // a half second or more after the last change of the window size aren't
1362 // heeded until the next resize-related user event (e.g. mouse movement).
1364 // Wine often has a significant delay between when it's been told that
1365 // the window has changed size and when it can flush completed drawing.
1366 // So, our windows would get stuck with incomplete drawing for as long
1367 // as the user holds the mouse button down and doesn't move it.
1369 // We address this by "manually" asking our windows to check if they need
1370 // redrawing every so often (during live resize only).
1371 [self windowDidEndLiveResize:nil];
1372 liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1374 selector:@selector(displayIfNeeded)
1377 [liveResizeDisplayTimer retain];
1378 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1379 forMode:NSRunLoopCommonModes];
1384 * ---------- NSPasteboardOwner methods ----------
1386 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1388 macdrv_query* query = macdrv_create_query();
1389 query->type = QUERY_PASTEBOARD_DATA;
1390 query->window = (macdrv_window)[self retain];
1391 query->pasteboard_data.type = (CFStringRef)[type copy];
1393 [self.queue query:query timeout:3];
1394 macdrv_release_query(query);
1399 * ---------- NSDraggingDestination methods ----------
1401 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1403 return [self draggingUpdated:sender];
1406 - (void) draggingExited:(id <NSDraggingInfo>)sender
1408 // This isn't really a query. We don't need any response. However, it
1409 // has to be processed in a similar manner as the other drag-and-drop
1410 // queries in order to maintain the proper order of operations.
1411 macdrv_query* query = macdrv_create_query();
1412 query->type = QUERY_DRAG_EXITED;
1413 query->window = (macdrv_window)[self retain];
1415 [self.queue query:query timeout:0.1];
1416 macdrv_release_query(query);
1419 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1421 NSDragOperation ret;
1422 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1423 NSPasteboard* pb = [sender draggingPasteboard];
1425 macdrv_query* query = macdrv_create_query();
1426 query->type = QUERY_DRAG_OPERATION;
1427 query->window = (macdrv_window)[self retain];
1428 query->drag_operation.x = pt.x;
1429 query->drag_operation.y = pt.y;
1430 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1431 query->drag_operation.accepted_op = NSDragOperationNone;
1432 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1434 [self.queue query:query timeout:3];
1435 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1436 macdrv_release_query(query);
1441 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1444 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1445 NSPasteboard* pb = [sender draggingPasteboard];
1447 macdrv_query* query = macdrv_create_query();
1448 query->type = QUERY_DRAG_DROP;
1449 query->window = (macdrv_window)[self retain];
1450 query->drag_drop.x = pt.x;
1451 query->drag_drop.y = pt.y;
1452 query->drag_drop.op = [sender draggingSourceOperationMask];
1453 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1455 [self.queue query:query timeout:3 * 60 processEvents:YES];
1456 ret = query->status;
1457 macdrv_release_query(query);
1462 - (BOOL) wantsPeriodicDraggingUpdates
1470 /***********************************************************************
1471 * macdrv_create_cocoa_window
1473 * Create a Cocoa window with the given content frame and features (e.g.
1474 * title bar, close box, etc.).
1476 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1477 CGRect frame, void* hwnd, macdrv_event_queue queue)
1479 __block WineWindow* window;
1482 window = [[WineWindow createWindowWithFeatures:wf
1483 windowFrame:NSRectFromCGRect(frame)
1485 queue:(WineEventQueue*)queue] retain];
1488 return (macdrv_window)window;
1491 /***********************************************************************
1492 * macdrv_destroy_cocoa_window
1494 * Destroy a Cocoa window.
1496 void macdrv_destroy_cocoa_window(macdrv_window w)
1498 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1499 WineWindow* window = (WineWindow*)w;
1501 [window.queue discardEventsMatchingMask:-1 forWindow:window];
1508 /***********************************************************************
1509 * macdrv_get_window_hwnd
1511 * Get the hwnd that was set for the window at creation.
1513 void* macdrv_get_window_hwnd(macdrv_window w)
1515 WineWindow* window = (WineWindow*)w;
1519 /***********************************************************************
1520 * macdrv_set_cocoa_window_features
1522 * Update a Cocoa window's features.
1524 void macdrv_set_cocoa_window_features(macdrv_window w,
1525 const struct macdrv_window_features* wf)
1527 WineWindow* window = (WineWindow*)w;
1530 [window setWindowFeatures:wf];
1534 /***********************************************************************
1535 * macdrv_set_cocoa_window_state
1537 * Update a Cocoa window's state.
1539 void macdrv_set_cocoa_window_state(macdrv_window w,
1540 const struct macdrv_window_state* state)
1542 WineWindow* window = (WineWindow*)w;
1545 [window setMacDrvState:state];
1549 /***********************************************************************
1550 * macdrv_set_cocoa_window_title
1552 * Set a Cocoa window's title.
1554 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1557 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1558 WineWindow* window = (WineWindow*)w;
1559 NSString* titleString;
1562 titleString = [NSString stringWithCharacters:title length:length];
1565 OnMainThreadAsync(^{
1566 [window setTitle:titleString];
1567 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
1568 [NSApp changeWindowsItem:window title:titleString filename:NO];
1574 /***********************************************************************
1575 * macdrv_order_cocoa_window
1577 * Reorder a Cocoa window relative to other windows. If prev is
1578 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
1579 * it is ordered above that window. Otherwise, it is ordered to the
1582 * Returns true if the window has actually been ordered onto the screen
1583 * (i.e. if its frame intersects with a screen). Otherwise, false.
1585 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1588 WineWindow* window = (WineWindow*)w;
1589 __block BOOL on_screen;
1592 on_screen = [window orderBelow:(WineWindow*)prev
1593 orAbove:(WineWindow*)next];
1599 /***********************************************************************
1600 * macdrv_hide_cocoa_window
1602 * Hides a Cocoa window.
1604 void macdrv_hide_cocoa_window(macdrv_window w)
1606 WineWindow* window = (WineWindow*)w;
1609 [window doOrderOut];
1613 /***********************************************************************
1614 * macdrv_set_cocoa_window_frame
1616 * Move a Cocoa window. If the window has been moved out of the bounds
1617 * of the desktop, it is ordered out. (This routine won't ever order a
1618 * window in, though.)
1620 * Returns true if the window is on screen; false otherwise.
1622 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1624 WineWindow* window = (WineWindow*)w;
1625 __block BOOL on_screen;
1628 on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1634 /***********************************************************************
1635 * macdrv_get_cocoa_window_frame
1637 * Gets the frame of a Cocoa window.
1639 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1641 WineWindow* window = (WineWindow*)w;
1646 frame = [window contentRectForFrameRect:[window frame]];
1647 [[WineApplicationController sharedController] flipRect:&frame];
1648 *out_frame = NSRectToCGRect(frame);
1652 /***********************************************************************
1653 * macdrv_set_cocoa_parent_window
1655 * Sets the parent window for a Cocoa window. If parent is NULL, clears
1656 * the parent window.
1658 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1660 WineWindow* window = (WineWindow*)w;
1663 [window setMacDrvParentWindow:(WineWindow*)parent];
1667 /***********************************************************************
1668 * macdrv_set_window_surface
1670 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1672 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1673 WineWindow* window = (WineWindow*)w;
1676 window.surface = surface;
1677 window.surface_mutex = mutex;
1683 /***********************************************************************
1684 * macdrv_window_needs_display
1686 * Mark a window as needing display in a specified rect (in non-client
1687 * area coordinates).
1689 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1691 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1692 WineWindow* window = (WineWindow*)w;
1694 OnMainThreadAsync(^{
1695 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1701 /***********************************************************************
1702 * macdrv_set_window_shape
1704 * Sets the shape of a Cocoa window from an array of rectangles. If
1705 * rects is NULL, resets the window's shape to its frame.
1707 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1709 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1710 WineWindow* window = (WineWindow*)w;
1713 if (!rects || !count)
1720 path = [NSBezierPath bezierPath];
1721 for (i = 0; i < count; i++)
1722 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1723 window.shape = path;
1730 /***********************************************************************
1731 * macdrv_set_window_alpha
1733 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1735 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1736 WineWindow* window = (WineWindow*)w;
1738 [window setAlphaValue:alpha];
1743 /***********************************************************************
1744 * macdrv_set_window_color_key
1746 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1749 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1750 WineWindow* window = (WineWindow*)w;
1753 window.colorKeyed = TRUE;
1754 window.colorKeyRed = keyRed;
1755 window.colorKeyGreen = keyGreen;
1756 window.colorKeyBlue = keyBlue;
1757 [window checkTransparency];
1763 /***********************************************************************
1764 * macdrv_clear_window_color_key
1766 void macdrv_clear_window_color_key(macdrv_window w)
1768 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1769 WineWindow* window = (WineWindow*)w;
1772 window.colorKeyed = FALSE;
1773 [window checkTransparency];
1779 /***********************************************************************
1780 * macdrv_window_use_per_pixel_alpha
1782 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1784 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1785 WineWindow* window = (WineWindow*)w;
1788 window.usePerPixelAlpha = use_per_pixel_alpha;
1789 [window checkTransparency];
1795 /***********************************************************************
1796 * macdrv_give_cocoa_window_focus
1798 * Makes the Cocoa window "key" (gives it keyboard focus). This also
1799 * orders it front and, if its frame was not within the desktop bounds,
1800 * Cocoa will typically move it on-screen.
1802 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
1804 WineWindow* window = (WineWindow*)w;
1807 [window makeFocused:activate];
1811 /***********************************************************************
1812 * macdrv_create_view
1814 * Creates and returns a view in the specified rect of the window. The
1815 * caller is responsible for calling macdrv_dispose_view() on the view
1816 * when it is done with it.
1818 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1820 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1821 WineWindow* window = (WineWindow*)w;
1822 __block WineContentView* view;
1824 if (CGRectIsNull(rect)) rect = CGRectZero;
1827 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1829 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1830 [view setAutoresizesSubviews:NO];
1831 [nc addObserver:view
1832 selector:@selector(updateGLContexts)
1833 name:NSViewGlobalFrameDidChangeNotification
1835 [nc addObserver:view
1836 selector:@selector(updateGLContexts)
1837 name:NSApplicationDidChangeScreenParametersNotification
1839 [[window contentView] addSubview:view];
1843 return (macdrv_view)view;
1846 /***********************************************************************
1847 * macdrv_dispose_view
1849 * Destroys a view previously returned by macdrv_create_view.
1851 void macdrv_dispose_view(macdrv_view v)
1853 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1854 WineContentView* view = (WineContentView*)v;
1857 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1859 [nc removeObserver:view
1860 name:NSViewGlobalFrameDidChangeNotification
1862 [nc removeObserver:view
1863 name:NSApplicationDidChangeScreenParametersNotification
1865 [view removeFromSuperview];
1872 /***********************************************************************
1873 * macdrv_set_view_window_and_frame
1875 * Move a view to a new window and/or position within its window. If w
1876 * is NULL, leave the view in its current window and just change its
1879 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1881 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1882 WineContentView* view = (WineContentView*)v;
1883 WineWindow* window = (WineWindow*)w;
1885 if (CGRectIsNull(rect)) rect = CGRectZero;
1888 BOOL changedWindow = (window && window != [view window]);
1889 NSRect newFrame = NSRectFromCGRect(rect);
1890 NSRect oldFrame = [view frame];
1894 [view removeFromSuperview];
1895 [[window contentView] addSubview:view];
1898 if (!NSEqualRects(oldFrame, newFrame))
1901 [[view superview] setNeedsDisplayInRect:oldFrame];
1902 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1903 [view setFrameSize:newFrame.size];
1904 else if (NSEqualSizes(oldFrame.size, newFrame.size))
1905 [view setFrameOrigin:newFrame.origin];
1907 [view setFrame:newFrame];
1908 [view setNeedsDisplay:YES];
1915 /***********************************************************************
1916 * macdrv_add_view_opengl_context
1918 * Add an OpenGL context to the list being tracked for each view.
1920 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1922 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1923 WineContentView* view = (WineContentView*)v;
1924 WineOpenGLContext *context = (WineOpenGLContext*)c;
1926 OnMainThreadAsync(^{
1927 [view addGLContext:context];
1933 /***********************************************************************
1934 * macdrv_remove_view_opengl_context
1936 * Add an OpenGL context to the list being tracked for each view.
1938 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1940 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1941 WineContentView* view = (WineContentView*)v;
1942 WineOpenGLContext *context = (WineOpenGLContext*)c;
1944 OnMainThreadAsync(^{
1945 [view removeGLContext:context];
1951 /***********************************************************************
1952 * macdrv_window_background_color
1954 * Returns the standard Mac window background color as a 32-bit value of
1955 * the form 0x00rrggbb.
1957 uint32_t macdrv_window_background_color(void)
1959 static uint32_t result;
1960 static dispatch_once_t once;
1962 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
1963 // color spaces (RGB or grayscale). So, the only way to get RGB values out
1964 // of it is to draw with it.
1965 dispatch_once(&once, ^{
1967 unsigned char rgbx[4];
1968 unsigned char *planes = rgbx;
1969 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
1976 colorSpaceName:NSCalibratedRGBColorSpace
1980 [NSGraphicsContext saveGraphicsState];
1981 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
1982 [[NSColor windowBackgroundColor] set];
1983 NSRectFill(NSMakeRect(0, 0, 1, 1));
1984 [NSGraphicsContext restoreGraphicsState];
1986 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
1993 /***********************************************************************
1994 * macdrv_send_text_input_event
1996 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2001 WineWindow* window = (WineWindow*)[NSApp keyWindow];
2002 if (![window isKindOfClass:[WineWindow class]])
2004 window = (WineWindow*)[NSApp mainWindow];
2005 if (![window isKindOfClass:[WineWindow class]])
2006 window = [[WineApplicationController sharedController] frontWineWindow];
2011 NSUInteger localFlags = flags;
2015 window.imeData = data;
2016 fix_device_modifiers_by_generic(&localFlags);
2018 // An NSEvent created with +keyEventWithType:... is internally marked
2019 // as synthetic and doesn't get sent through input methods. But one
2020 // created from a CGEvent doesn't have that problem.
2021 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2022 CGEventSetFlags(c, localFlags);
2023 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2024 event = [NSEvent eventWithCGEvent:c];
2027 window.commandDone = FALSE;
2028 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;