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 if (state->minimized && ![self isMiniaturized])
641 ignore_windowMiniaturize = TRUE;
642 [self miniaturize:nil];
644 else if (!state->minimized && [self isMiniaturized])
646 ignore_windowDeminiaturize = TRUE;
647 [self deminiaturize:nil];
650 /* Whatever events regarding minimization might have been in the queue are now stale. */
651 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
652 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
656 // Determine if, among Wine windows, this window is directly above or below
657 // a given other Wine window with no other Wine window intervening.
658 // Intervening non-Wine windows are ignored.
659 - (BOOL) isOrdered:(NSWindowOrderingMode)orderingMode relativeTo:(WineWindow*)otherWindow
661 NSNumber* windowNumber;
662 NSNumber* otherWindowNumber;
663 NSArray* windowNumbers;
664 NSUInteger windowIndex, otherWindowIndex, lowIndex, highIndex, i;
666 if (![self isVisible] || ![otherWindow isVisible])
669 windowNumber = [NSNumber numberWithInteger:[self windowNumber]];
670 otherWindowNumber = [NSNumber numberWithInteger:[otherWindow windowNumber]];
671 windowNumbers = [[self class] windowNumbersWithOptions:0];
672 windowIndex = [windowNumbers indexOfObject:windowNumber];
673 otherWindowIndex = [windowNumbers indexOfObject:otherWindowNumber];
675 if (windowIndex == NSNotFound || otherWindowIndex == NSNotFound)
678 if (orderingMode == NSWindowAbove)
680 lowIndex = windowIndex;
681 highIndex = otherWindowIndex;
683 else if (orderingMode == NSWindowBelow)
685 lowIndex = otherWindowIndex;
686 highIndex = windowIndex;
691 if (highIndex <= lowIndex)
694 for (i = lowIndex + 1; i < highIndex; i++)
696 NSInteger interveningWindowNumber = [[windowNumbers objectAtIndex:i] integerValue];
697 NSWindow* interveningWindow = [NSApp windowWithWindowNumber:interveningWindowNumber];
698 if ([interveningWindow isKindOfClass:[WineWindow class]])
705 /* Returns whether or not the window was ordered in, which depends on if
706 its frame intersects any screen. */
707 - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
709 WineApplicationController* controller = [WineApplicationController sharedController];
710 BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
711 if (on_screen && ![self isMiniaturized])
713 BOOL needAdjustWindowLevels = FALSE;
715 [controller transformProcessToForeground];
717 NSDisableScreenUpdates();
719 if (latentParentWindow)
721 if ([latentParentWindow level] > [self level])
722 [self setLevel:[latentParentWindow level]];
723 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
724 self.latentParentWindow = nil;
725 needAdjustWindowLevels = TRUE;
729 WineWindow* other = [prev isVisible] ? prev : next;
730 NSWindowOrderingMode orderingMode = [prev isVisible] ? NSWindowBelow : NSWindowAbove;
732 if (![self isOrdered:orderingMode relativeTo:other])
734 // This window level may not be right for this window based
735 // on floating-ness, fullscreen-ness, etc. But we set it
736 // temporarily to allow us to order the windows properly.
737 // Then the levels get fixed by -adjustWindowLevels.
738 if ([self level] != [other level])
739 [self setLevel:[other level]];
740 [self orderWindow:orderingMode relativeTo:[other windowNumber]];
741 needAdjustWindowLevels = TRUE;
746 // Again, temporarily set level to make sure we can order to
748 next = [controller frontWineWindow];
749 if (next && [self level] < [next level])
750 [self setLevel:[next level]];
751 [self orderFront:nil];
752 needAdjustWindowLevels = TRUE;
754 if (needAdjustWindowLevels)
755 [controller adjustWindowLevels];
757 NSEnableScreenUpdates();
759 /* Cocoa may adjust the frame when the window is ordered onto the screen.
760 Generate a frame-changed event just in case. The back end will ignore
761 it if nothing actually changed. */
762 [self windowDidResize:nil];
764 if (![self isExcludedFromWindowsMenu])
765 [NSApp addWindowsItem:self title:[self title] filename:NO];
773 self.latentParentWindow = [self parentWindow];
774 [latentParentWindow removeChildWindow:self];
776 [[WineApplicationController sharedController] adjustWindowLevels];
777 [NSApp removeWindowsItem:self];
780 - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
782 NSArray* screens = [NSScreen screens];
783 BOOL on_screen = [self isOrderedIn];
785 if (![screens count]) return on_screen;
787 /* Origin is (left, top) in a top-down space. Need to convert it to
788 (left, bottom) in a bottom-up space. */
789 [[WineApplicationController sharedController] flipRect:&contentRect];
793 on_screen = frame_intersects_screens(contentRect, screens);
798 /* The back end is establishing a new window size and position. It's
799 not interested in any stale events regarding those that may be sitting
801 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
804 if (!NSIsEmptyRect(contentRect))
806 NSRect frame, oldFrame;
808 oldFrame = [self frame];
809 frame = [self frameRectForContentRect:contentRect];
810 if (!NSEqualRects(frame, oldFrame))
812 if (NSEqualSizes(frame.size, oldFrame.size))
813 [self setFrameOrigin:frame.origin];
815 [self setFrame:frame display:YES];
819 BOOL fullscreen = (screen_covered_by_rect(frame, screens) != nil);
820 BOOL oldFullscreen = (screen_covered_by_rect(oldFrame, screens) != nil);
822 if (fullscreen != oldFullscreen)
823 [[WineApplicationController sharedController] adjustWindowLevels];
825 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
826 event. The back end will ignore it if nothing actually changed. */
827 [self windowDidResize:nil];
835 - (void) setMacDrvParentWindow:(WineWindow*)parent
837 if ([self parentWindow] != parent)
839 [[self parentWindow] removeChildWindow:self];
840 self.latentParentWindow = nil;
841 if ([self isVisible] && parent)
843 if ([parent level] > [self level])
844 [self setLevel:[parent level]];
845 [parent addChildWindow:self ordered:NSWindowAbove];
846 [[WineApplicationController sharedController] adjustWindowLevels];
849 self.latentParentWindow = parent;
853 - (void) setDisabled:(BOOL)newValue
855 if (disabled != newValue)
858 [self adjustFeaturesForState];
862 NSSize size = [self frame].size;
863 [self setMinSize:size];
864 [self setMaxSize:size];
868 [self setMaxSize:NSMakeSize(FLT_MAX, FLT_MAX)];
869 [self setMinSize:NSZeroSize];
874 - (BOOL) needsTransparency
876 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
879 - (void) checkTransparency
881 if (![self isOpaque] && !self.needsTransparency)
883 [self setBackgroundColor:[NSColor windowBackgroundColor]];
884 [self setOpaque:YES];
886 else if ([self isOpaque] && self.needsTransparency)
888 [self setBackgroundColor:[NSColor clearColor]];
893 - (void) setShape:(NSBezierPath*)newShape
895 if (shape == newShape) return;
896 if (shape && newShape && [shape isEqual:newShape]) return;
900 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
904 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
906 shape = [newShape copy];
907 self.shapeChangedSinceLastDraw = TRUE;
909 [self checkTransparency];
912 - (void) makeFocused:(BOOL)activate
914 WineApplicationController* controller = [WineApplicationController sharedController];
918 [controller transformProcessToForeground];
920 /* If a borderless window is offscreen, orderFront: won't move
921 it onscreen like it would for a titled window. Do that ourselves. */
922 screens = [NSScreen screens];
923 if (!([self styleMask] & NSTitledWindowMask) && ![self isOrderedIn] &&
924 !frame_intersects_screens([self frame], screens))
926 NSScreen* primaryScreen = [screens objectAtIndex:0];
927 NSRect frame = [primaryScreen frame];
928 [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
929 frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
930 [self setFrame:frame display:YES];
934 [NSApp activateIgnoringOtherApps:YES];
936 NSDisableScreenUpdates();
938 if (latentParentWindow)
940 if ([latentParentWindow level] > [self level])
941 [self setLevel:[latentParentWindow level]];
942 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
943 self.latentParentWindow = nil;
945 front = [controller frontWineWindow];
946 if (front && [self level] < [front level])
947 [self setLevel:[front level]];
948 [self orderFront:nil];
949 [controller adjustWindowLevels];
951 NSEnableScreenUpdates();
953 causing_becomeKeyWindow = TRUE;
954 [self makeKeyWindow];
955 causing_becomeKeyWindow = FALSE;
957 if (![self isExcludedFromWindowsMenu])
958 [NSApp addWindowsItem:self title:[self title] filename:NO];
960 /* Cocoa may adjust the frame when the window is ordered onto the screen.
961 Generate a frame-changed event just in case. The back end will ignore
962 it if nothing actually changed. */
963 [self windowDidResize:nil];
966 - (void) postKey:(uint16_t)keyCode
967 pressed:(BOOL)pressed
968 modifiers:(NSUInteger)modifiers
969 event:(NSEvent*)theEvent
973 WineApplicationController* controller = [WineApplicationController sharedController];
975 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
976 event->key.keycode = keyCode;
977 event->key.modifiers = modifiers;
978 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
980 if ((cgevent = [theEvent CGEvent]))
982 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
983 kCGKeyboardEventKeyboardType);
984 if (keyboardType != controller.keyboardType)
986 controller.keyboardType = keyboardType;
987 [controller keyboardSelectionDidChange];
991 [queue postEvent:event];
993 macdrv_release_event(event);
996 - (void) postKeyEvent:(NSEvent *)theEvent
998 [self flagsChanged:theEvent];
999 [self postKey:[theEvent keyCode]
1000 pressed:[theEvent type] == NSKeyDown
1001 modifiers:[theEvent modifierFlags]
1007 * ---------- NSWindow method overrides ----------
1009 - (BOOL) canBecomeKeyWindow
1011 if (causing_becomeKeyWindow) return YES;
1012 if (self.disabled || self.noActivate) return NO;
1013 return [self isKeyWindow];
1016 - (BOOL) canBecomeMainWindow
1018 return [self canBecomeKeyWindow];
1021 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1023 // If a window is sized to completely cover a screen, then it's in
1024 // full-screen mode. In that case, we don't allow NSWindow to constrain
1026 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1027 if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
1028 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1032 - (BOOL) isExcludedFromWindowsMenu
1034 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1037 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1039 BOOL ret = [super validateMenuItem:menuItem];
1041 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1042 ret = [self isKeyWindow] || (!self.disabled && !self.noActivate);
1047 /* We don't call this. It's the action method of the items in the Window menu. */
1048 - (void) makeKeyAndOrderFront:(id)sender
1050 WineApplicationController* controller = [WineApplicationController sharedController];
1051 WineWindow* front = [controller frontWineWindow];
1053 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1054 [controller windowGotFocus:self];
1056 if (front && [self level] < [front level])
1057 [self setLevel:[front level]];
1058 [self orderFront:nil];
1059 [controller adjustWindowLevels];
1062 - (void) sendEvent:(NSEvent*)event
1064 WineApplicationController* controller = [WineApplicationController sharedController];
1066 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1067 interface control. For example, Control-Tab switches focus among
1068 views. We want to bypass that feature, so directly route key-down
1069 events to -keyDown:. */
1070 if ([event type] == NSKeyDown)
1071 [[self firstResponder] keyDown:event];
1074 if ([event type] == NSLeftMouseDown)
1076 /* Since our windows generally claim they can't be made key, clicks
1077 in their title bars are swallowed by the theme frame stuff. So,
1078 we hook directly into the event stream and assume that any click
1079 in the window will activate it, if Wine and the Win32 program
1081 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1082 [controller windowGotFocus:self];
1085 [super sendEvent:event];
1091 * ---------- NSResponder method overrides ----------
1093 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1094 - (void) keyUp:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1096 - (void) flagsChanged:(NSEvent *)theEvent
1098 static const struct {
1102 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1103 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1104 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1105 { NX_DEVICELCTLKEYMASK, kVK_Control },
1106 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1107 { NX_DEVICELALTKEYMASK, kVK_Option },
1108 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1109 { NX_DEVICELCMDKEYMASK, kVK_Command },
1110 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1113 NSUInteger modifierFlags = [theEvent modifierFlags];
1115 int i, last_changed;
1117 fix_device_modifiers_by_generic(&modifierFlags);
1118 changed = modifierFlags ^ lastModifierFlags;
1121 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1122 if (changed & modifiers[i].mask)
1125 for (i = 0; i <= last_changed; i++)
1127 if (changed & modifiers[i].mask)
1129 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1131 if (i == last_changed)
1132 lastModifierFlags = modifierFlags;
1135 lastModifierFlags ^= modifiers[i].mask;
1136 fix_generic_modifiers_by_device(&lastModifierFlags);
1139 // Caps lock generates one event for each press-release action.
1140 // We need to simulate a pair of events for each actual event.
1141 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1143 [self postKey:modifiers[i].keycode
1145 modifiers:lastModifierFlags
1146 event:(NSEvent*)theEvent];
1150 [self postKey:modifiers[i].keycode
1152 modifiers:lastModifierFlags
1153 event:(NSEvent*)theEvent];
1160 * ---------- NSWindowDelegate methods ----------
1162 - (void)windowDidBecomeKey:(NSNotification *)notification
1164 WineApplicationController* controller = [WineApplicationController sharedController];
1165 NSEvent* event = [controller lastFlagsChanged];
1167 [self flagsChanged:event];
1169 if (causing_becomeKeyWindow) return;
1171 [controller windowGotFocus:self];
1174 - (void)windowDidDeminiaturize:(NSNotification *)notification
1176 if (!ignore_windowDeminiaturize)
1178 macdrv_event* event;
1180 /* Coalesce events by discarding any previous ones still in the queue. */
1181 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1182 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1185 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1186 [queue postEvent:event];
1187 macdrv_release_event(event);
1190 ignore_windowDeminiaturize = FALSE;
1192 [[WineApplicationController sharedController] adjustWindowLevels];
1195 - (void) windowDidEndLiveResize:(NSNotification *)notification
1197 [liveResizeDisplayTimer invalidate];
1198 [liveResizeDisplayTimer release];
1199 liveResizeDisplayTimer = nil;
1202 - (void)windowDidMove:(NSNotification *)notification
1204 [self windowDidResize:notification];
1207 - (void)windowDidResignKey:(NSNotification *)notification
1209 macdrv_event* event;
1211 if (causing_becomeKeyWindow) return;
1213 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1214 [queue postEvent:event];
1215 macdrv_release_event(event);
1218 - (void)windowDidResize:(NSNotification *)notification
1220 macdrv_event* event;
1221 NSRect frame = [self frame];
1225 NSSize size = frame.size;
1226 [self setMinSize:size];
1227 [self setMaxSize:size];
1230 frame = [self contentRectForFrameRect:frame];
1231 [[WineApplicationController sharedController] flipRect:&frame];
1233 /* Coalesce events by discarding any previous ones still in the queue. */
1234 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1237 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1238 event->window_frame_changed.frame = NSRectToCGRect(frame);
1239 [queue postEvent:event];
1240 macdrv_release_event(event);
1242 [[[self contentView] inputContext] invalidateCharacterCoordinates];
1245 - (BOOL)windowShouldClose:(id)sender
1247 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1248 [queue postEvent:event];
1249 macdrv_release_event(event);
1253 - (void)windowWillMiniaturize:(NSNotification *)notification
1255 if (!ignore_windowMiniaturize)
1257 macdrv_event* event;
1259 /* Coalesce events by discarding any previous ones still in the queue. */
1260 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1261 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1264 event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
1265 [queue postEvent:event];
1266 macdrv_release_event(event);
1269 ignore_windowMiniaturize = FALSE;
1272 - (void) windowWillStartLiveResize:(NSNotification *)notification
1274 // There's a strange restriction in window redrawing during Cocoa-
1275 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
1276 // that happen synchronously when Cocoa tells us that our window size
1277 // has changed or asynchronously in a short interval thereafter provoke
1278 // the window to redraw. Calls to those methods that happen asynchronously
1279 // a half second or more after the last change of the window size aren't
1280 // heeded until the next resize-related user event (e.g. mouse movement).
1282 // Wine often has a significant delay between when it's been told that
1283 // the window has changed size and when it can flush completed drawing.
1284 // So, our windows would get stuck with incomplete drawing for as long
1285 // as the user holds the mouse button down and doesn't move it.
1287 // We address this by "manually" asking our windows to check if they need
1288 // redrawing every so often (during live resize only).
1289 [self windowDidEndLiveResize:nil];
1290 liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1292 selector:@selector(displayIfNeeded)
1295 [liveResizeDisplayTimer retain];
1296 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1297 forMode:NSRunLoopCommonModes];
1302 * ---------- NSPasteboardOwner methods ----------
1304 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1306 macdrv_query* query = macdrv_create_query();
1307 query->type = QUERY_PASTEBOARD_DATA;
1308 query->window = (macdrv_window)[self retain];
1309 query->pasteboard_data.type = (CFStringRef)[type copy];
1311 [self.queue query:query timeout:3];
1312 macdrv_release_query(query);
1317 * ---------- NSDraggingDestination methods ----------
1319 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1321 return [self draggingUpdated:sender];
1324 - (void) draggingExited:(id <NSDraggingInfo>)sender
1326 // This isn't really a query. We don't need any response. However, it
1327 // has to be processed in a similar manner as the other drag-and-drop
1328 // queries in order to maintain the proper order of operations.
1329 macdrv_query* query = macdrv_create_query();
1330 query->type = QUERY_DRAG_EXITED;
1331 query->window = (macdrv_window)[self retain];
1333 [self.queue query:query timeout:0.1];
1334 macdrv_release_query(query);
1337 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1339 NSDragOperation ret;
1340 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1341 NSPasteboard* pb = [sender draggingPasteboard];
1343 macdrv_query* query = macdrv_create_query();
1344 query->type = QUERY_DRAG_OPERATION;
1345 query->window = (macdrv_window)[self retain];
1346 query->drag_operation.x = pt.x;
1347 query->drag_operation.y = pt.y;
1348 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1349 query->drag_operation.accepted_op = NSDragOperationNone;
1350 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1352 [self.queue query:query timeout:3];
1353 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1354 macdrv_release_query(query);
1359 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1362 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1363 NSPasteboard* pb = [sender draggingPasteboard];
1365 macdrv_query* query = macdrv_create_query();
1366 query->type = QUERY_DRAG_DROP;
1367 query->window = (macdrv_window)[self retain];
1368 query->drag_drop.x = pt.x;
1369 query->drag_drop.y = pt.y;
1370 query->drag_drop.op = [sender draggingSourceOperationMask];
1371 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1373 [self.queue query:query timeout:3 * 60 processEvents:YES];
1374 ret = query->status;
1375 macdrv_release_query(query);
1380 - (BOOL) wantsPeriodicDraggingUpdates
1388 /***********************************************************************
1389 * macdrv_create_cocoa_window
1391 * Create a Cocoa window with the given content frame and features (e.g.
1392 * title bar, close box, etc.).
1394 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1395 CGRect frame, void* hwnd, macdrv_event_queue queue)
1397 __block WineWindow* window;
1400 window = [[WineWindow createWindowWithFeatures:wf
1401 windowFrame:NSRectFromCGRect(frame)
1403 queue:(WineEventQueue*)queue] retain];
1406 return (macdrv_window)window;
1409 /***********************************************************************
1410 * macdrv_destroy_cocoa_window
1412 * Destroy a Cocoa window.
1414 void macdrv_destroy_cocoa_window(macdrv_window w)
1416 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1417 WineWindow* window = (WineWindow*)w;
1419 [window.queue discardEventsMatchingMask:-1 forWindow:window];
1426 /***********************************************************************
1427 * macdrv_get_window_hwnd
1429 * Get the hwnd that was set for the window at creation.
1431 void* macdrv_get_window_hwnd(macdrv_window w)
1433 WineWindow* window = (WineWindow*)w;
1437 /***********************************************************************
1438 * macdrv_set_cocoa_window_features
1440 * Update a Cocoa window's features.
1442 void macdrv_set_cocoa_window_features(macdrv_window w,
1443 const struct macdrv_window_features* wf)
1445 WineWindow* window = (WineWindow*)w;
1448 [window setWindowFeatures:wf];
1452 /***********************************************************************
1453 * macdrv_set_cocoa_window_state
1455 * Update a Cocoa window's state.
1457 void macdrv_set_cocoa_window_state(macdrv_window w,
1458 const struct macdrv_window_state* state)
1460 WineWindow* window = (WineWindow*)w;
1463 [window setMacDrvState:state];
1467 /***********************************************************************
1468 * macdrv_set_cocoa_window_title
1470 * Set a Cocoa window's title.
1472 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1475 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1476 WineWindow* window = (WineWindow*)w;
1477 NSString* titleString;
1480 titleString = [NSString stringWithCharacters:title length:length];
1483 OnMainThreadAsync(^{
1484 [window setTitle:titleString];
1485 if ([window isOrderedIn] && ![window isExcludedFromWindowsMenu])
1486 [NSApp changeWindowsItem:window title:titleString filename:NO];
1492 /***********************************************************************
1493 * macdrv_order_cocoa_window
1495 * Reorder a Cocoa window relative to other windows. If prev is
1496 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
1497 * it is ordered above that window. Otherwise, it is ordered to the
1500 * Returns true if the window has actually been ordered onto the screen
1501 * (i.e. if its frame intersects with a screen). Otherwise, false.
1503 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1506 WineWindow* window = (WineWindow*)w;
1507 __block BOOL on_screen;
1510 on_screen = [window orderBelow:(WineWindow*)prev
1511 orAbove:(WineWindow*)next];
1517 /***********************************************************************
1518 * macdrv_hide_cocoa_window
1520 * Hides a Cocoa window.
1522 void macdrv_hide_cocoa_window(macdrv_window w)
1524 WineWindow* window = (WineWindow*)w;
1527 [window doOrderOut];
1531 /***********************************************************************
1532 * macdrv_set_cocoa_window_frame
1534 * Move a Cocoa window. If the window has been moved out of the bounds
1535 * of the desktop, it is ordered out. (This routine won't ever order a
1536 * window in, though.)
1538 * Returns true if the window is on screen; false otherwise.
1540 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1542 WineWindow* window = (WineWindow*)w;
1543 __block BOOL on_screen;
1546 on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1552 /***********************************************************************
1553 * macdrv_get_cocoa_window_frame
1555 * Gets the frame of a Cocoa window.
1557 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1559 WineWindow* window = (WineWindow*)w;
1564 frame = [window contentRectForFrameRect:[window frame]];
1565 [[WineApplicationController sharedController] flipRect:&frame];
1566 *out_frame = NSRectToCGRect(frame);
1570 /***********************************************************************
1571 * macdrv_set_cocoa_parent_window
1573 * Sets the parent window for a Cocoa window. If parent is NULL, clears
1574 * the parent window.
1576 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1578 WineWindow* window = (WineWindow*)w;
1581 [window setMacDrvParentWindow:(WineWindow*)parent];
1585 /***********************************************************************
1586 * macdrv_set_window_surface
1588 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1590 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1591 WineWindow* window = (WineWindow*)w;
1594 window.surface = surface;
1595 window.surface_mutex = mutex;
1601 /***********************************************************************
1602 * macdrv_window_needs_display
1604 * Mark a window as needing display in a specified rect (in non-client
1605 * area coordinates).
1607 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1609 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1610 WineWindow* window = (WineWindow*)w;
1612 OnMainThreadAsync(^{
1613 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1619 /***********************************************************************
1620 * macdrv_set_window_shape
1622 * Sets the shape of a Cocoa window from an array of rectangles. If
1623 * rects is NULL, resets the window's shape to its frame.
1625 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1627 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1628 WineWindow* window = (WineWindow*)w;
1631 if (!rects || !count)
1638 path = [NSBezierPath bezierPath];
1639 for (i = 0; i < count; i++)
1640 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1641 window.shape = path;
1648 /***********************************************************************
1649 * macdrv_set_window_alpha
1651 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1653 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1654 WineWindow* window = (WineWindow*)w;
1656 [window setAlphaValue:alpha];
1661 /***********************************************************************
1662 * macdrv_set_window_color_key
1664 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1667 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1668 WineWindow* window = (WineWindow*)w;
1671 window.colorKeyed = TRUE;
1672 window.colorKeyRed = keyRed;
1673 window.colorKeyGreen = keyGreen;
1674 window.colorKeyBlue = keyBlue;
1675 [window checkTransparency];
1681 /***********************************************************************
1682 * macdrv_clear_window_color_key
1684 void macdrv_clear_window_color_key(macdrv_window w)
1686 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1687 WineWindow* window = (WineWindow*)w;
1690 window.colorKeyed = FALSE;
1691 [window checkTransparency];
1697 /***********************************************************************
1698 * macdrv_window_use_per_pixel_alpha
1700 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1702 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1703 WineWindow* window = (WineWindow*)w;
1706 window.usePerPixelAlpha = use_per_pixel_alpha;
1707 [window checkTransparency];
1713 /***********************************************************************
1714 * macdrv_give_cocoa_window_focus
1716 * Makes the Cocoa window "key" (gives it keyboard focus). This also
1717 * orders it front and, if its frame was not within the desktop bounds,
1718 * Cocoa will typically move it on-screen.
1720 void macdrv_give_cocoa_window_focus(macdrv_window w, int activate)
1722 WineWindow* window = (WineWindow*)w;
1725 [window makeFocused:activate];
1729 /***********************************************************************
1730 * macdrv_create_view
1732 * Creates and returns a view in the specified rect of the window. The
1733 * caller is responsible for calling macdrv_dispose_view() on the view
1734 * when it is done with it.
1736 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1738 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1739 WineWindow* window = (WineWindow*)w;
1740 __block WineContentView* view;
1742 if (CGRectIsNull(rect)) rect = CGRectZero;
1745 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1747 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1748 [view setAutoresizesSubviews:NO];
1749 [nc addObserver:view
1750 selector:@selector(updateGLContexts)
1751 name:NSViewGlobalFrameDidChangeNotification
1753 [nc addObserver:view
1754 selector:@selector(updateGLContexts)
1755 name:NSApplicationDidChangeScreenParametersNotification
1757 [[window contentView] addSubview:view];
1761 return (macdrv_view)view;
1764 /***********************************************************************
1765 * macdrv_dispose_view
1767 * Destroys a view previously returned by macdrv_create_view.
1769 void macdrv_dispose_view(macdrv_view v)
1771 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1772 WineContentView* view = (WineContentView*)v;
1775 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1777 [nc removeObserver:view
1778 name:NSViewGlobalFrameDidChangeNotification
1780 [nc removeObserver:view
1781 name:NSApplicationDidChangeScreenParametersNotification
1783 [view removeFromSuperview];
1790 /***********************************************************************
1791 * macdrv_set_view_window_and_frame
1793 * Move a view to a new window and/or position within its window. If w
1794 * is NULL, leave the view in its current window and just change its
1797 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1799 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1800 WineContentView* view = (WineContentView*)v;
1801 WineWindow* window = (WineWindow*)w;
1803 if (CGRectIsNull(rect)) rect = CGRectZero;
1806 BOOL changedWindow = (window && window != [view window]);
1807 NSRect newFrame = NSRectFromCGRect(rect);
1808 NSRect oldFrame = [view frame];
1812 [view removeFromSuperview];
1813 [[window contentView] addSubview:view];
1816 if (!NSEqualRects(oldFrame, newFrame))
1819 [[view superview] setNeedsDisplayInRect:oldFrame];
1820 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1821 [view setFrameSize:newFrame.size];
1822 else if (NSEqualSizes(oldFrame.size, newFrame.size))
1823 [view setFrameOrigin:newFrame.origin];
1825 [view setFrame:newFrame];
1826 [view setNeedsDisplay:YES];
1833 /***********************************************************************
1834 * macdrv_add_view_opengl_context
1836 * Add an OpenGL context to the list being tracked for each view.
1838 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1840 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1841 WineContentView* view = (WineContentView*)v;
1842 WineOpenGLContext *context = (WineOpenGLContext*)c;
1844 OnMainThreadAsync(^{
1845 [view addGLContext:context];
1851 /***********************************************************************
1852 * macdrv_remove_view_opengl_context
1854 * Add an OpenGL context to the list being tracked for each view.
1856 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1858 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1859 WineContentView* view = (WineContentView*)v;
1860 WineOpenGLContext *context = (WineOpenGLContext*)c;
1862 OnMainThreadAsync(^{
1863 [view removeGLContext:context];
1869 /***********************************************************************
1870 * macdrv_window_background_color
1872 * Returns the standard Mac window background color as a 32-bit value of
1873 * the form 0x00rrggbb.
1875 uint32_t macdrv_window_background_color(void)
1877 static uint32_t result;
1878 static dispatch_once_t once;
1880 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
1881 // color spaces (RGB or grayscale). So, the only way to get RGB values out
1882 // of it is to draw with it.
1883 dispatch_once(&once, ^{
1885 unsigned char rgbx[4];
1886 unsigned char *planes = rgbx;
1887 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
1894 colorSpaceName:NSCalibratedRGBColorSpace
1898 [NSGraphicsContext saveGraphicsState];
1899 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
1900 [[NSColor windowBackgroundColor] set];
1901 NSRectFill(NSMakeRect(0, 0, 1, 1));
1902 [NSGraphicsContext restoreGraphicsState];
1904 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
1911 /***********************************************************************
1912 * macdrv_send_text_input_event
1914 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
1919 WineWindow* window = (WineWindow*)[NSApp keyWindow];
1920 if (![window isKindOfClass:[WineWindow class]])
1922 window = (WineWindow*)[NSApp mainWindow];
1923 if (![window isKindOfClass:[WineWindow class]])
1924 window = [[WineApplicationController sharedController] frontWineWindow];
1929 NSUInteger localFlags = flags;
1933 window.imeData = data;
1934 fix_device_modifiers_by_generic(&localFlags);
1936 // An NSEvent created with +keyEventWithType:... is internally marked
1937 // as synthetic and doesn't get sent through input methods. But one
1938 // created from a CGEvent doesn't have that problem.
1939 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
1940 CGEventSetFlags(c, localFlags);
1941 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
1942 event = [NSEvent eventWithCGEvent:c];
1945 window.commandDone = FALSE;
1946 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;