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, 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];
521 [liveResizeDisplayTimer invalidate];
522 [liveResizeDisplayTimer release];
524 [latentParentWindow release];
529 - (void) adjustFeaturesForState
531 NSUInteger style = [self styleMask];
533 if (style & NSClosableWindowMask)
534 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
535 if (style & NSMiniaturizableWindowMask)
536 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
537 if (style & NSResizableWindowMask)
538 [[self standardWindowButton:NSWindowZoomButton] setEnabled:!self.disabled];
541 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
543 NSUInteger currentStyle = [self styleMask];
544 NSUInteger newStyle = style_mask_for_features(wf);
546 if (newStyle != currentStyle)
548 BOOL showingButtons = (currentStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
549 BOOL shouldShowButtons = (newStyle & (NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask)) != 0;
550 if (shouldShowButtons != showingButtons && !((newStyle ^ currentStyle) & NSClosableWindowMask))
552 // -setStyleMask: is buggy on 10.7+ with respect to NSResizableWindowMask.
553 // If transitioning from NSTitledWindowMask | NSResizableWindowMask to
554 // just NSTitledWindowMask, the window buttons should disappear rather
555 // than just being disabled. But they don't. Similarly in reverse.
556 // The workaround is to also toggle NSClosableWindowMask at the same time.
557 [self setStyleMask:newStyle ^ NSClosableWindowMask];
559 [self setStyleMask:newStyle];
562 [self adjustFeaturesForState];
563 [self setHasShadow:wf->shadow];
568 return [self isVisible] || [self isMiniaturized];
571 - (NSInteger) minimumLevelForActive:(BOOL)active
577 screen = screen_covered_by_rect([self frame], [NSScreen screens]);
578 fullscreen = (screen != nil);
580 if (self.floating && (active || topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_ALL ||
581 (topmost_float_inactive == TOPMOST_FLOAT_INACTIVE_NONFULLSCREEN && !fullscreen)))
582 level = NSFloatingWindowLevel;
584 level = NSNormalWindowLevel;
590 captured = (screen || [self screen]) && [[WineApplicationController sharedController] areDisplaysCaptured];
592 if (captured || fullscreen)
595 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
597 level = NSMainMenuWindowLevel + 1;
607 - (void) setMacDrvState:(const struct macdrv_window_state*)state
609 NSWindowCollectionBehavior behavior;
611 self.disabled = state->disabled;
612 self.noActivate = state->no_activate;
614 if (self.floating != state->floating)
616 self.floating = state->floating;
617 [[WineApplicationController sharedController] adjustWindowLevels];
620 behavior = NSWindowCollectionBehaviorDefault;
621 if (state->excluded_by_expose)
622 behavior |= NSWindowCollectionBehaviorTransient;
624 behavior |= NSWindowCollectionBehaviorManaged;
625 if (state->excluded_by_cycle)
627 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
628 if ([self isOrderedIn])
629 [NSApp removeWindowsItem:self];
633 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
634 if ([self isOrderedIn])
635 [NSApp addWindowsItem:self title:[self title] filename:NO];
637 [self setCollectionBehavior:behavior];
639 pendingMinimize = FALSE;
640 if (state->minimized && ![self isMiniaturized])
642 if ([self isVisible])
644 ignore_windowMiniaturize = TRUE;
645 [self miniaturize:nil];
648 pendingMinimize = TRUE;
650 else if (!state->minimized && [self isMiniaturized])
652 ignore_windowDeminiaturize = TRUE;
653 [self deminiaturize:nil];
656 /* Whatever events regarding minimization might have been in the queue are now stale. */
657 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
658 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
662 // Determine if, among Wine windows, this window is directly above or below
663 // a given other Wine window with no other Wine window intervening.
664 // Intervening non-Wine windows are ignored.
665 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
667 NSNumber* windowNumber;
668 NSNumber* otherWindowNumber;
669 NSArray* windowNumbers;
670 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
672 if (![self isVisible] || ![otherWindow isVisible])
675 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
676 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
677 windowNumbers = [[self class] windowNumbersWithOptions:0];
678 windowIndex = [windowNumbers indexOfObject:windowNumber];
679 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
681 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
684 if (orderingMode == NSWindowAbove)
686 lowIndex = windowIndex;
687 highIndex = otherWindowIndex;
689 else if (orderingMode == NSWindowBelow)
691 lowIndex = otherWindowIndex;
692 highIndex = windowIndex;
697 if (highIndex <= lowIndex)
700 for (i = lowIndex + 1; i < highIndex; i++)
702 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
703 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
704 if ([interveningWindow isKindOfClass:[WineWindow class]])
711 /* Returns whether or not the window was ordered in, which depends on if
712 its frame intersects any screen. */
713 - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
715 WineApplicationController* controller = [WineApplicationController sharedController];
716 BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
717 if (on_screen && ![self isMiniaturized])
719 BOOL needAdjustWindowLevels = FALSE;
721 [controller transformProcessToForeground];
723 NSDisableScreenUpdates();
725 if (latentParentWindow)
727 if ([latentParentWindow level] > [self level])
728 [self setLevel:[latentParentWindow level]];
729 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
730 self.latentParentWindow = nil;
731 needAdjustWindowLevels = TRUE;
735 WineWindow* other = [prev isVisible] ? prev : next;
736 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
738 if (![self isOrdered:orderingMode relativeTo:other])
740 // This window level may not be right for this window based
741 // on floating-ness, fullscreen-ness, etc. But we set it
742 // temporarily to allow us to order the windows properly.
743 // Then the levels get fixed by -adjustWindowLevels.
744 if ([self level] != [other level])
745 [self setLevel:[other level]];
746 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
747 needAdjustWindowLevels = TRUE;
752 // Again, temporarily set level to make sure we can order to
754 next = [controller frontWineWindow];
755 if (next && [self level] < [next level])
756 [self setLevel:[next level]];
757 [self orderFront:nil];
758 needAdjustWindowLevels = TRUE;
760 if (needAdjustWindowLevels)
761 [controller adjustWindowLevels];
765 ignore_windowMiniaturize = TRUE;
766 [self miniaturize:nil];
767 pendingMinimize = FALSE;
770 NSEnableScreenUpdates();
772 /* Cocoa may adjust the frame when the window is ordered onto the screen.
773 Generate a frame-changed event just in case. The back end will ignore
774 it if nothing actually changed. */
775 [self windowDidResize:nil];
777 if (![self isExcludedFromWindowsMenu])
778 [NSApp addWindowsItem:self title:[self title] filename:NO];
786 if ([self isMiniaturized])
787 pendingMinimize = TRUE;
788 self.latentParentWindow = [self parentWindow];
789 [latentParentWindow removeChildWindow:self];
791 [[WineApplicationController sharedController] adjustWindowLevels];
792 [NSApp removeWindowsItem:self];
795 - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
797 NSArray* screens = [NSScreen screens];
798 BOOL on_screen = [self isOrderedIn];
800 if (![screens count]) return on_screen;
802 /* Origin is (left, top) in a top-down space. Need to convert it to
803 (left, bottom) in a bottom-up space. */
804 [[WineApplicationController sharedController] flipRect:&contentRect];
808 on_screen = frame_intersects_screens(contentRect, screens);
813 /* The back end is establishing a new window size and position. It's
814 not interested in any stale events regarding those that may be sitting
816 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
819 if (!NSIsEmptyRect(contentRect))
821 NSRect frame, oldFrame;
823 oldFrame = [self frame];
824 frame = [self frameRectForContentRect:contentRect];
825 if (!NSEqualRects(frame, oldFrame))
827 if (NSEqualSizes(frame.size, oldFrame.size))
828 [self setFrameOrigin:frame.origin];
830 [self setFrame:frame display:YES];
834 BOOL fullscreen = (screen_covered_by_rect(frame, screens) != nil);
835 BOOL oldFullscreen = (screen_covered_by_rect(oldFrame, screens) != nil);
837 if (fullscreen != oldFullscreen)
838 [[WineApplicationController sharedController] adjustWindowLevels];
840 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
841 event. The back end will ignore it if nothing actually changed. */
842 [self windowDidResize:nil];
850 - (void) setMacDrvParentWindow:(WineWindow*)parent
852 if ([self parentWindow] != parent)
854 [[self parentWindow] removeChildWindow:self];
855 self.latentParentWindow = nil;
856 if ([self isVisible] && parent)
858 if ([parent level] > [self level])
859 [self setLevel:[parent level]];
860 [parent addChildWindow:self ordered:NSWindowAbove];
861 [[WineApplicationController sharedController] adjustWindowLevels];
864 self.latentParentWindow = parent;
868 - (void) setDisabled:(BOOL)newValue
870 if (disabled != newValue)
873 [self adjustFeaturesForState];
877 NSSize size = [self frame].size;
878 [self setMinSize:size];
879 [self setMaxSize:size];
883 [self setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
884 [self setMinSize:NSZeroSize];
889 - (BOOL) needsTransparency
891 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
894 - (void) checkTransparency
896 if (![self isOpaque] && !self.needsTransparency)
898 [self setBackgroundColor:[NSColor windowBackgroundColor]];
899 [self setOpaque:YES];
901 else if ([self isOpaque] && self.needsTransparency)
903 [self setBackgroundColor:[NSColor clearColor]];
908 - (void) setShape:(NSBezierPath*)newShape
910 if (shape == newShape) return;
911 if (shape && newShape && [shape isEqual:newShape]) return;
915 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
919 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
921 shape = [newShape copy];
922 self.shapeChangedSinceLastDraw = TRUE;
924 [self checkTransparency];
927 - (void) makeFocused:(BOOL)activate
929 WineApplicationController* controller = [WineApplicationController sharedController];
933 [controller transformProcessToForeground];
935 /* If a borderless window is offscreen, orderFront: won't move
936 it onscreen like it would for a titled window. Do that ourselves. */
937 screens = [NSScreen screens];
938 if (!([self styleMask] & NSTitledWindowMask) && ![self isOrderedIn] &&
939 !frame_intersects_screens([self frame], screens))
941 NSScreen* primaryScreen = [screens objectAtIndex:0];
942 NSRect frame = [primaryScreen frame];
943 [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
944 frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
945 [self setFrame:frame display:YES];
949 [NSApp activateIgnoringOtherApps:YES];
951 NSDisableScreenUpdates();
953 if (latentParentWindow)
955 if ([latentParentWindow level] > [self level])
956 [self setLevel:[latentParentWindow level]];
957 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
958 self.latentParentWindow = nil;
960 front = [controller frontWineWindow];
961 if (front && [self level] < [front level])
962 [self setLevel:[front level]];
963 [self orderFront:nil];
964 [controller adjustWindowLevels];
968 ignore_windowMiniaturize = TRUE;
969 [self miniaturize:nil];
970 pendingMinimize = FALSE;
973 NSEnableScreenUpdates();
975 causing_becomeKeyWindow = TRUE;
976 [self makeKeyWindow];
977 causing_becomeKeyWindow = FALSE;
979 if (![self isExcludedFromWindowsMenu])
980 [NSApp addWindowsItem:self title:[self title] filename:NO];
982 /* Cocoa may adjust the frame when the window is ordered onto the screen.
983 Generate a frame-changed event just in case. The back end will ignore
984 it if nothing actually changed. */
985 [self windowDidResize:nil];
988 - (void) postKey:(uint16_t)keyCode
989 pressed:(BOOL)pressed
990 modifiers:(NSUInteger)modifiers
991 event:(NSEvent*)theEvent
995 WineApplicationController* controller = [WineApplicationController sharedController];
997 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
998 event->key.keycode = keyCode;
999 event->key.modifiers = modifiers;
1000 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
1002 if ((cgevent = [theEvent CGEvent]))
1004 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
1005 kCGKeyboardEventKeyboardType);
1006 if (keyboardType != controller.keyboardType)
1008 controller.keyboardType = keyboardType;
1009 [controller keyboardSelectionDidChange];
1013 [queue postEvent:event];
1015 macdrv_release_event(event);
1018 - (void) postKeyEvent:(NSEvent *)theEvent
1020 [self flagsChanged:theEvent];
1021 [self postKey:[theEvent keyCode]
1022 pressed:[theEvent type] == NSKeyDown
1023 modifiers:[theEvent modifierFlags]
1029 * ---------- NSWindow method overrides ----------
1031 - (BOOL) canBecomeKeyWindow
1033 if (causing_becomeKeyWindow) return YES;
1034 if (self.disabled || self.noActivate) return NO;
1035 return [self isKeyWindow];
1038 - (BOOL) canBecomeMainWindow
1040 return [self canBecomeKeyWindow];
1043 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1045 // If a window is sized to completely cover a screen, then it's in
1046 // full-screen mode. In that case, we don't allow NSWindow to constrain
1048 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1049 if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
1050 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1054 - (BOOL) isExcludedFromWindowsMenu
1056 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1059 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1061 BOOL ret = [super validateMenuItem:menuItem];
1063 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1064 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1069 /* We don't call this. It's the action method of the items in the Window menu. */
1070 - (void) makeKeyAndOrderFront:(id)sender
1072 WineApplicationController* controller = [WineApplicationController sharedController];
1073 WineWindow* front = [controller frontWineWindow];
1075 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1076 [controller windowGotFocus:self];
1078 if (front && [self level] < [front level])
1079 [self setLevel:[front level]];
1080 [self orderFront:nil];
1081 [controller adjustWindowLevels];
1083 if (pendingMinimize)
1085 ignore_windowMiniaturize = TRUE;
1086 [self miniaturize:nil];
1087 pendingMinimize = FALSE;
1091 - (void) sendEvent:(NSEvent*)event
1093 WineApplicationController* controller = [WineApplicationController sharedController];
1095 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1096 interface control. For example, Control-Tab switches focus among
1097 views. We want to bypass that feature, so directly route key-down
1098 events to -keyDown:. */
1099 if ([event type] == NSKeyDown)
1100 [[self firstResponder] keyDown:event];
1103 if ([event type] == NSLeftMouseDown)
1105 /* Since our windows generally claim they can't be made key, clicks
1106 in their title bars are swallowed by the theme frame stuff. So,
1107 we hook directly into the event stream and assume that any click
1108 in the window will activate it, if Wine and the Win32 program
1110 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1111 [controller windowGotFocus:self];
1114 [super sendEvent:event];
1120 * ---------- NSResponder method overrides ----------
1122 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1123 - (void) keyUp:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1125 - (void) flagsChanged:(NSEvent *)theEvent
1127 static const struct {
1131 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1132 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1133 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1134 { NX_DEVICELCTLKEYMASK, kVK_Control },
1135 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1136 { NX_DEVICELALTKEYMASK, kVK_Option },
1137 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1138 { NX_DEVICELCMDKEYMASK, kVK_Command },
1139 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1142 NSUInteger modifierFlags = [theEvent modifierFlags];
1144 int i, last_changed;
1146 fix_device_modifiers_by_generic(&modifierFlags);
1147 changed = modifierFlags ^ lastModifierFlags;
1150 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1151 if (changed & modifiers[i].mask)
1154 for (i = 0; i <= last_changed; i++)
1156 if (changed & modifiers[i].mask)
1158 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1160 if (i == last_changed)
1161 lastModifierFlags = modifierFlags;
1164 lastModifierFlags ^= modifiers[i].mask;
1165 fix_generic_modifiers_by_device(&lastModifierFlags);
1168 // Caps lock generates one event for each press-release action.
1169 // We need to simulate a pair of events for each actual event.
1170 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1172 [self postKey:modifiers[i].keycode
1174 modifiers:lastModifierFlags
1175 event:(NSEvent*)theEvent];
1179 [self postKey:modifiers[i].keycode
1181 modifiers:lastModifierFlags
1182 event:(NSEvent*)theEvent];
1189 * ---------- NSWindowDelegate methods ----------
1191 - (void)windowDidBecomeKey:(NSNotification *)notification
1193 WineApplicationController* controller = [WineApplicationController sharedController];
1194 NSEvent* event = [controller lastFlagsChanged];
1196 [self flagsChanged:event];
1198 if (causing_becomeKeyWindow) return;
1200 [controller windowGotFocus:self];
1203 - (void)windowDidDeminiaturize:(NSNotification *)notification
1205 WineApplicationController* controller = [WineApplicationController sharedController];
1207 if (!ignore_windowDeminiaturize)
1209 macdrv_event* event;
1211 /* Coalesce events by discarding any previous ones still in the queue. */
1212 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1213 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1216 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1217 [queue postEvent:event];
1218 macdrv_release_event(event);
1221 ignore_windowDeminiaturize = FALSE;
1223 [controller adjustWindowLevels];
1225 if (!self.disabled && !self.noActivate)
1227 causing_becomeKeyWindow = TRUE;
1228 [self makeKeyWindow];
1229 causing_becomeKeyWindow = FALSE;
1230 [controller windowGotFocus:self];
1233 [self windowDidResize:notification];
1236 - (void) windowDidEndLiveResize:(NSNotification *)notification
1238 [liveResizeDisplayTimer invalidate];
1239 [liveResizeDisplayTimer release];
1240 liveResizeDisplayTimer = nil;
1243 - (void)windowDidMove:(NSNotification *)notification
1245 [self windowDidResize:notification];
1248 - (void)windowDidResignKey:(NSNotification *)notification
1250 macdrv_event* event;
1252 if (causing_becomeKeyWindow) return;
1254 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1255 [queue postEvent:event];
1256 macdrv_release_event(event);
1259 - (void)windowDidResize:(NSNotification *)notification
1261 macdrv_event* event;
1262 NSRect frame = [self frame];
1266 NSSize size = frame.size;
1267 [self setMinSize:size];
1268 [self setMaxSize:size];
1271 frame = [self contentRectForFrameRect:frame];
1272 [[WineApplicationController sharedController] flipRect:&frame];
1274 /* Coalesce events by discarding any previous ones still in the queue. */
1275 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1278 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1279 event->window_frame_changed.frame = NSRectToCGRect(frame);
1280 [queue postEvent:event];
1281 macdrv_release_event(event);
1283 [[[self contentView] inputContext] invalidateCharacterCoordinates];
1286 - (BOOL)windowShouldClose:(id)sender
1288 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1289 [queue postEvent:event];
1290 macdrv_release_event(event);
1294 - (void)windowWillMiniaturize:(NSNotification *)notification
1296 if (!ignore_windowMiniaturize)
1298 macdrv_event* event;
1300 /* Coalesce events by discarding any previous ones still in the queue. */
1301 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1302 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1305 event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
1306 [queue postEvent:event];
1307 macdrv_release_event(event);
1310 ignore_windowMiniaturize = FALSE;
1313 - (void) windowWillStartLiveResize:(NSNotification *)notification
1315 // There's a strange restriction in window redrawing during Cocoa-
1316 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
1317 // that happen synchronously when Cocoa tells us that our window size
1318 // has changed or asynchronously in a short interval thereafter provoke
1319 // the window to redraw. Calls to those methods that happen asynchronously
1320 // a half second or more after the last change of the window size aren't
1321 // heeded until the next resize-related user event (e.g. mouse movement).
1323 // Wine often has a significant delay between when it's been told that
1324 // the window has changed size and when it can flush completed drawing.
1325 // So, our windows would get stuck with incomplete drawing for as long
1326 // as the user holds the mouse button down and doesn't move it.
1328 // We address this by "manually" asking our windows to check if they need
1329 // redrawing every so often (during live resize only).
1330 [self windowDidEndLiveResize:nil];
1331 liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1333 selector:@selector(displayIfNeeded)
1336 [liveResizeDisplayTimer retain];
1337 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1338 forMode:NSRunLoopCommonModes];
1343 * ---------- NSPasteboardOwner methods ----------
1345 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1347 macdrv_query* query = macdrv_create_query();
1348 query->type = QUERY_PASTEBOARD_DATA;
1349 query->window = (macdrv_window)[self retain];
1350 query->pasteboard_data.type = (CFStringRef)[type copy];
1352 [self.queue query:query timeout:3];
1353 macdrv_release_query(query);
1358 * ---------- NSDraggingDestination methods ----------
1360 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1362 return [self draggingUpdated:sender];
1365 - (void) draggingExited:(id <NSDraggingInfo>)sender
1367 // This isn't really a query. We don't need any response. However, it
1368 // has to be processed in a similar manner as the other drag-and-drop
1369 // queries in order to maintain the proper order of operations.
1370 macdrv_query* query = macdrv_create_query();
1371 query->type = QUERY_DRAG_EXITED;
1372 query->window = (macdrv_window)[self retain];
1374 [self.queue query:query timeout:0.1];
1375 macdrv_release_query(query);
1378 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1380 NSDragOperation ret;
1381 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1382 NSPasteboard* pb = [sender draggingPasteboard];
1384 macdrv_query* query = macdrv_create_query();
1385 query->type = QUERY_DRAG_OPERATION;
1386 query->window = (macdrv_window)[self retain];
1387 query->drag_operation.x = pt.x;
1388 query->drag_operation.y = pt.y;
1389 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1390 query->drag_operation.accepted_op = NSDragOperationNone;
1391 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1393 [self.queue query:query timeout:3];
1394 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1395 macdrv_release_query(query);
1400 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1403 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1404 NSPasteboard* pb = [sender draggingPasteboard];
1406 macdrv_query* query = macdrv_create_query();
1407 query->type = QUERY_DRAG_DROP;
1408 query->window = (macdrv_window)[self retain];
1409 query->drag_drop.x = pt.x;
1410 query->drag_drop.y = pt.y;
1411 query->drag_drop.op = [sender draggingSourceOperationMask];
1412 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1414 [self.queue query:query timeout:3 * 60 processEvents:YES];
1415 ret = query->status;
1416 macdrv_release_query(query);
1421 - (BOOL) wantsPeriodicDraggingUpdates
1429 /***********************************************************************
1430 * macdrv_create_cocoa_window
1432 * Create a Cocoa window with the given content frame and features (e.g.
1433 * title bar, close box, etc.).
1435 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1436 CGRect frame, void* hwnd, macdrv_event_queue queue)
1438 __block WineWindow* window;
1441 window = [[WineWindow createWindowWithFeatures:wf
1442 windowFrame:NSRectFromCGRect(frame)
1444 queue:(WineEventQueue*)queue] retain];
1447 return (macdrv_window)window;
1450 /***********************************************************************
1451 * macdrv_destroy_cocoa_window
1453 * Destroy a Cocoa window.
1455 void macdrv_destroy_cocoa_window(macdrv_window w)
1457 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1458 WineWindow* window = (WineWindow*)w;
1460 [window.queue discardEventsMatchingMask:-1 forWindow:window];
1467 /***********************************************************************
1468 * macdrv_get_window_hwnd
1470 * Get the hwnd that was set for the window at creation.
1472 void* macdrv_get_window_hwnd(macdrv_window w)
1474 WineWindow* window = (WineWindow*)w;
1478 /***********************************************************************
1479 * macdrv_set_cocoa_window_features
1481 * Update a Cocoa window's features.
1483 void macdrv_set_cocoa_window_features(macdrv_window w,
1484 const struct macdrv_window_features* wf)
1486 WineWindow* window = (WineWindow*)w;
1489 [window setWindowFeatures:wf];
1493 /***********************************************************************
1494 * macdrv_set_cocoa_window_state
1496 * Update a Cocoa window's state.
1498 void macdrv_set_cocoa_window_state(macdrv_window w,
1499 const struct macdrv_window_state* state)
1501 WineWindow* window = (WineWindow*)w;
1504 [window setMacDrvState:state];
1508 /***********************************************************************
1509 * macdrv_set_cocoa_window_title
1511 * Set a Cocoa window's title.
1513 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1516 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1517 WineWindow* window = (WineWindow*)w;
1518 NSString* titleString;
1521 titleString = [NSString stringWithCharacters:title length:length];
1524 OnMainThreadAsync(^{
1525 [window setTitle:titleString];
1526 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
1527 [NSApp changeWindowsItem:window title:titleString filename:NO];
1533 /***********************************************************************
1534 * macdrv_order_cocoa_window
1536 * Reorder a Cocoa window relative to other windows. If prev is
1537 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
1538 * it is ordered above that window. Otherwise, it is ordered to the
1541 * Returns true if the window has actually been ordered onto the screen
1542 * (i.e. if its frame intersects with a screen). Otherwise, false.
1544 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1547 WineWindow* window = (WineWindow*)w;
1548 __block BOOL on_screen;
1551 on_screen = [window orderBelow:(WineWindow*)prev
1552 orAbove:(WineWindow*)next];
1558 /***********************************************************************
1559 * macdrv_hide_cocoa_window
1561 * Hides a Cocoa window.
1563 void macdrv_hide_cocoa_window(macdrv_window w)
1565 WineWindow* window = (WineWindow*)w;
1568 [window doOrderOut];
1572 /***********************************************************************
1573 * macdrv_set_cocoa_window_frame
1575 * Move a Cocoa window. If the window has been moved out of the bounds
1576 * of the desktop, it is ordered out. (This routine won't ever order a
1577 * window in, though.)
1579 * Returns true if the window is on screen; false otherwise.
1581 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1583 WineWindow* window = (WineWindow*)w;
1584 __block BOOL on_screen;
1587 on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1593 /***********************************************************************
1594 * macdrv_get_cocoa_window_frame
1596 * Gets the frame of a Cocoa window.
1598 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1600 WineWindow* window = (WineWindow*)w;
1605 frame = [window contentRectForFrameRect:[window frame]];
1606 [[WineApplicationController sharedController] flipRect:&frame];
1607 *out_frame = NSRectToCGRect(frame);
1611 /***********************************************************************
1612 * macdrv_set_cocoa_parent_window
1614 * Sets the parent window for a Cocoa window. If parent is NULL, clears
1615 * the parent window.
1617 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1619 WineWindow* window = (WineWindow*)w;
1622 [window setMacDrvParentWindow:(WineWindow*)parent];
1626 /***********************************************************************
1627 * macdrv_set_window_surface
1629 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1631 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1632 WineWindow* window = (WineWindow*)w;
1635 window.surface = surface;
1636 window.surface_mutex = mutex;
1642 /***********************************************************************
1643 * macdrv_window_needs_display
1645 * Mark a window as needing display in a specified rect (in non-client
1646 * area coordinates).
1648 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1650 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1651 WineWindow* window = (WineWindow*)w;
1653 OnMainThreadAsync(^{
1654 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1660 /***********************************************************************
1661 * macdrv_set_window_shape
1663 * Sets the shape of a Cocoa window from an array of rectangles. If
1664 * rects is NULL, resets the window's shape to its frame.
1666 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1668 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1669 WineWindow* window = (WineWindow*)w;
1672 if (!rects || !count)
1679 path = [NSBezierPath bezierPath];
1680 for (i = 0; i < count; i++)
1681 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1682 window.shape = path;
1689 /***********************************************************************
1690 * macdrv_set_window_alpha
1692 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1694 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1695 WineWindow* window = (WineWindow*)w;
1697 [window setAlphaValue:alpha];
1702 /***********************************************************************
1703 * macdrv_set_window_color_key
1705 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1708 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1709 WineWindow* window = (WineWindow*)w;
1712 window.colorKeyed = TRUE;
1713 window.colorKeyRed = keyRed;
1714 window.colorKeyGreen = keyGreen;
1715 window.colorKeyBlue = keyBlue;
1716 [window checkTransparency];
1722 /***********************************************************************
1723 * macdrv_clear_window_color_key
1725 void macdrv_clear_window_color_key(macdrv_window w)
1727 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1728 WineWindow* window = (WineWindow*)w;
1731 window.colorKeyed = FALSE;
1732 [window checkTransparency];
1738 /***********************************************************************
1739 * macdrv_window_use_per_pixel_alpha
1741 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1743 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1744 WineWindow* window = (WineWindow*)w;
1747 window.usePerPixelAlpha = use_per_pixel_alpha;
1748 [window checkTransparency];
1754 /***********************************************************************
1755 * macdrv_give_cocoa_window_focus
1757 * Makes the Cocoa window "key" (gives it keyboard focus). This also
1758 * orders it front and, if its frame was not within the desktop bounds,
1759 * Cocoa will typically move it on-screen.
1761 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
1763 WineWindow* window = (WineWindow*)w;
1766 [window makeFocused:activate];
1770 /***********************************************************************
1771 * macdrv_create_view
1773 * Creates and returns a view in the specified rect of the window. The
1774 * caller is responsible for calling macdrv_dispose_view() on the view
1775 * when it is done with it.
1777 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1779 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1780 WineWindow* window = (WineWindow*)w;
1781 __block WineContentView* view;
1783 if (CGRectIsNull(rect)) rect = CGRectZero;
1786 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1788 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1789 [view setAutoresizesSubviews:NO];
1790 [nc addObserver:view
1791 selector:@selector(updateGLContexts)
1792 name:NSViewGlobalFrameDidChangeNotification
1794 [nc addObserver:view
1795 selector:@selector(updateGLContexts)
1796 name:NSApplicationDidChangeScreenParametersNotification
1798 [[window contentView] addSubview:view];
1802 return (macdrv_view)view;
1805 /***********************************************************************
1806 * macdrv_dispose_view
1808 * Destroys a view previously returned by macdrv_create_view.
1810 void macdrv_dispose_view(macdrv_view v)
1812 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1813 WineContentView* view = (WineContentView*)v;
1816 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1818 [nc removeObserver:view
1819 name:NSViewGlobalFrameDidChangeNotification
1821 [nc removeObserver:view
1822 name:NSApplicationDidChangeScreenParametersNotification
1824 [view removeFromSuperview];
1831 /***********************************************************************
1832 * macdrv_set_view_window_and_frame
1834 * Move a view to a new window and/or position within its window. If w
1835 * is NULL, leave the view in its current window and just change its
1838 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1840 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1841 WineContentView* view = (WineContentView*)v;
1842 WineWindow* window = (WineWindow*)w;
1844 if (CGRectIsNull(rect)) rect = CGRectZero;
1847 BOOL changedWindow = (window && window != [view window]);
1848 NSRect newFrame = NSRectFromCGRect(rect);
1849 NSRect oldFrame = [view frame];
1853 [view removeFromSuperview];
1854 [[window contentView] addSubview:view];
1857 if (!NSEqualRects(oldFrame, newFrame))
1860 [[view superview] setNeedsDisplayInRect:oldFrame];
1861 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1862 [view setFrameSize:newFrame.size];
1863 else if (NSEqualSizes(oldFrame.size, newFrame.size))
1864 [view setFrameOrigin:newFrame.origin];
1866 [view setFrame:newFrame];
1867 [view setNeedsDisplay:YES];
1874 /***********************************************************************
1875 * macdrv_add_view_opengl_context
1877 * Add an OpenGL context to the list being tracked for each view.
1879 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1881 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1882 WineContentView* view = (WineContentView*)v;
1883 WineOpenGLContext *context = (WineOpenGLContext*)c;
1885 OnMainThreadAsync(^{
1886 [view addGLContext:context];
1892 /***********************************************************************
1893 * macdrv_remove_view_opengl_context
1895 * Add an OpenGL context to the list being tracked for each view.
1897 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1899 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1900 WineContentView* view = (WineContentView*)v;
1901 WineOpenGLContext *context = (WineOpenGLContext*)c;
1903 OnMainThreadAsync(^{
1904 [view removeGLContext:context];
1910 /***********************************************************************
1911 * macdrv_window_background_color
1913 * Returns the standard Mac window background color as a 32-bit value of
1914 * the form 0x00rrggbb.
1916 uint32_t macdrv_window_background_color(void)
1918 static uint32_t result;
1919 static dispatch_once_t once;
1921 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
1922 // color spaces (RGB or grayscale). So, the only way to get RGB values out
1923 // of it is to draw with it.
1924 dispatch_once(&once, ^{
1926 unsigned char rgbx[4];
1927 unsigned char *planes = rgbx;
1928 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
1935 colorSpaceName:NSCalibratedRGBColorSpace
1939 [NSGraphicsContext saveGraphicsState];
1940 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
1941 [[NSColor windowBackgroundColor] set];
1942 NSRectFill(NSMakeRect(0, 0, 1, 1));
1943 [NSGraphicsContext restoreGraphicsState];
1945 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
1952 /***********************************************************************
1953 * macdrv_send_text_input_event
1955 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
1960 WineWindow* window = (WineWindow*)[NSApp keyWindow];
1961 if (![window isKindOfClass:[WineWindow class]])
1963 window = (WineWindow*)[NSApp mainWindow];
1964 if (![window isKindOfClass:[WineWindow class]])
1965 window = [[WineApplicationController sharedController] frontWineWindow];
1970 NSUInteger localFlags = flags;
1974 window.imeData = data;
1975 fix_device_modifiers_by_generic(&localFlags);
1977 // An NSEvent created with +keyEventWithType:... is internally marked
1978 // as synthetic and doesn't get sent through input methods. But one
1979 // created from a CGEvent doesn't have that problem.
1980 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
1981 CGEventSetFlags(c, localFlags);
1982 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
1983 event = [NSEvent eventWithCGEvent:c];
1986 window.commandDone = FALSE;
1987 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;