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
123 NSMutableArray* glContexts;
124 NSMutableArray* pendingGlContexts;
127 - (void) addGLContext:(WineOpenGLContext*)context;
128 - (void) removeGLContext:(WineOpenGLContext*)context;
129 - (void) updateGLContexts;
134 @interface WineWindow ()
136 @property (nonatomic) BOOL disabled;
137 @property (nonatomic) BOOL noActivate;
138 @property (readwrite, nonatomic) BOOL floating;
139 @property (retain, nonatomic) NSWindow* latentParentWindow;
141 @property (nonatomic) void* hwnd;
142 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
144 @property (nonatomic) void* surface;
145 @property (nonatomic) pthread_mutex_t* surface_mutex;
147 @property (copy, nonatomic) NSBezierPath* shape;
148 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
149 @property (readonly, nonatomic) BOOL needsTransparency;
151 @property (nonatomic) BOOL colorKeyed;
152 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
153 @property (nonatomic) BOOL usePerPixelAlpha;
155 @property (readwrite, nonatomic) NSInteger levelWhenActive;
160 @implementation WineContentView
164 [glContexts release];
165 [pendingGlContexts release];
174 - (void) drawRect:(NSRect)rect
176 WineWindow* window = (WineWindow*)[self window];
178 for (WineOpenGLContext* context in pendingGlContexts)
179 context.needsUpdate = TRUE;
180 [glContexts addObjectsFromArray:pendingGlContexts];
181 [pendingGlContexts removeAllObjects];
183 if ([window contentView] != self)
186 if (window.surface && window.surface_mutex &&
187 !pthread_mutex_lock(window.surface_mutex))
192 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
194 CGContextRef context;
197 [window.shape addClip];
199 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
200 CGContextSetBlendMode(context, kCGBlendModeCopy);
202 for (i = 0; i < count; i++)
207 imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
208 image = create_surface_image(window.surface, &imageRect, FALSE);
212 if (window.colorKeyed)
214 CGImageRef maskedImage;
215 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
216 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
217 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
218 maskedImage = CGImageCreateWithMaskingColors(image, components);
221 CGImageRelease(image);
226 CGContextDrawImage(context, imageRect, image);
228 CGImageRelease(image);
233 pthread_mutex_unlock(window.surface_mutex);
236 // If the window may be transparent, then we have to invalidate the
237 // shadow every time we draw. Also, if this is the first time we've
238 // drawn since changing from transparent to opaque.
239 if (![window isOpaque] || window.shapeChangedSinceLastDraw)
241 window.shapeChangedSinceLastDraw = FALSE;
242 [window invalidateShadow];
246 /* By default, NSView will swallow right-clicks in an attempt to support contextual
247 menus. We need to bypass that and allow the event to make it to the window. */
248 - (void) rightMouseDown:(NSEvent*)theEvent
250 [[self window] rightMouseDown:theEvent];
253 - (void) addGLContext:(WineOpenGLContext*)context
256 glContexts = [[NSMutableArray alloc] init];
257 if (!pendingGlContexts)
258 pendingGlContexts = [[NSMutableArray alloc] init];
259 [pendingGlContexts addObject:context];
260 [self setNeedsDisplay:YES];
263 - (void) removeGLContext:(WineOpenGLContext*)context
265 [glContexts removeObjectIdenticalTo:context];
266 [pendingGlContexts removeObjectIdenticalTo:context];
269 - (void) updateGLContexts
271 for (WineOpenGLContext* context in glContexts)
272 context.needsUpdate = TRUE;
275 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
280 - (BOOL) preservesContentDuringLiveResize
282 // Returning YES from this tells Cocoa to keep our view's content during
283 // a Cocoa-driven resize. In theory, we're also supposed to override
284 // -setFrameSize: to mark exposed sections as needing redisplay, but
285 // user32 will take care of that in a roundabout way. This way, we don't
286 // redraw until the window surface is flushed.
288 // This doesn't do anything when we resize the window ourselves.
295 @implementation WineWindow
297 @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
298 @synthesize surface, surface_mutex;
299 @synthesize shape, shapeChangedSinceLastDraw;
300 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
301 @synthesize usePerPixelAlpha;
302 @synthesize levelWhenActive;
304 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
305 windowFrame:(NSRect)window_frame
307 queue:(WineEventQueue*)queue
310 WineContentView* contentView;
311 NSTrackingArea* trackingArea;
313 [NSApp flipRect:&window_frame];
315 window = [[[self alloc] initWithContentRect:window_frame
316 styleMask:style_mask_for_features(wf)
317 backing:NSBackingStoreBuffered
318 defer:YES] autorelease];
320 if (!window) return nil;
321 window->normalStyleMask = [window styleMask];
323 /* Standardize windows to eliminate differences between titled and
324 borderless windows and between NSWindow and NSPanel. */
325 [window setHidesOnDeactivate:NO];
326 [window setReleasedWhenClosed:NO];
328 [window disableCursorRects];
329 [window setShowsResizeIndicator:NO];
330 [window setHasShadow:wf->shadow];
331 [window setAcceptsMouseMovedEvents:YES];
332 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
333 [window setDelegate:window];
335 window.queue = queue;
337 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
338 (NSString*)kUTTypeContent,
341 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
344 [contentView setAutoresizesSubviews:NO];
346 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
347 because they give us mouse moves in the background. */
348 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
349 options:(NSTrackingMouseMoved |
350 NSTrackingActiveAlways |
351 NSTrackingInVisibleRect)
353 userInfo:nil] autorelease];
356 [contentView addTrackingArea:trackingArea];
358 [window setContentView:contentView];
365 [liveResizeDisplayTimer invalidate];
366 [liveResizeDisplayTimer release];
368 [latentParentWindow release];
373 - (void) adjustFeaturesForState
375 NSUInteger style = normalStyleMask;
378 style &= ~NSResizableWindowMask;
379 if (style != [self styleMask])
380 [self setStyleMask:style];
382 if (style & NSClosableWindowMask)
383 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
384 if (style & NSMiniaturizableWindowMask)
385 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
388 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
390 normalStyleMask = style_mask_for_features(wf);
391 [self adjustFeaturesForState];
392 [self setHasShadow:wf->shadow];
395 - (void) adjustWindowLevel
398 BOOL fullscreen, captured;
401 WineWindow* other = nil;
403 screen = screen_covered_by_rect([self frame], [NSScreen screens]);
404 fullscreen = (screen != nil);
405 captured = (screen || [self screen]) && [NSApp areDisplaysCaptured];
407 if (captured || fullscreen)
410 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
412 level = NSMainMenuWindowLevel + 1;
417 else if (self.floating)
418 level = NSFloatingWindowLevel;
420 level = NSNormalWindowLevel;
422 index = [[NSApp orderedWineWindows] indexOfObjectIdenticalTo:self];
423 if (index != NSNotFound && index + 1 < [[NSApp orderedWineWindows] count])
425 other = [[NSApp orderedWineWindows] objectAtIndex:index + 1];
426 if (level < [other level])
427 level = [other level];
430 if (level != [self level])
432 [self setLevelWhenActive:level];
434 /* Setting the window level above has moved this window to the front
435 of all other windows at the same level. We need to move it
436 back into its proper place among other windows of that level.
437 Also, any windows which are supposed to be in front of it had
438 better have the same or higher window level. If not, bump them
440 if (index != NSNotFound && [self isVisible])
442 for (; index > 0; index--)
444 other = [[NSApp orderedWineWindows] objectAtIndex:index - 1];
445 if ([other level] < level)
446 [other setLevelWhenActive:level];
449 [self orderWindow:NSWindowBelow relativeTo:[other windowNumber]];
457 - (void) setMacDrvState:(const struct macdrv_window_state*)state
459 NSWindowCollectionBehavior behavior;
461 self.disabled = state->disabled;
462 self.noActivate = state->no_activate;
464 self.floating = state->floating;
465 [self adjustWindowLevel];
467 behavior = NSWindowCollectionBehaviorDefault;
468 if (state->excluded_by_expose)
469 behavior |= NSWindowCollectionBehaviorTransient;
471 behavior |= NSWindowCollectionBehaviorManaged;
472 if (state->excluded_by_cycle)
474 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
475 if ([self isVisible])
476 [NSApp removeWindowsItem:self];
480 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
481 if ([self isVisible])
482 [NSApp addWindowsItem:self title:[self title] filename:NO];
484 [self setCollectionBehavior:behavior];
486 if (state->minimized && ![self isMiniaturized])
488 ignore_windowMiniaturize = TRUE;
489 [self miniaturize:nil];
491 else if (!state->minimized && [self isMiniaturized])
493 ignore_windowDeminiaturize = TRUE;
494 [self deminiaturize:nil];
497 /* Whatever events regarding minimization might have been in the queue are now stale. */
498 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
499 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
503 /* Returns whether or not the window was ordered in, which depends on if
504 its frame intersects any screen. */
505 - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
507 BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
510 [NSApp transformProcessToForeground];
514 /* Make sure that windows that should be above this one really are.
515 This is necessary since a full-screen window gets a boost to its
516 window level to be in front of the menu bar and Dock and that moves
517 it out of the z-order that Win32 would otherwise establish. */
518 if ([prev level] < [self level])
520 NSUInteger index = [[NSApp orderedWineWindows] indexOfObjectIdenticalTo:prev];
521 if (index != NSNotFound)
523 [prev setLevelWhenActive:[self level]];
524 for (; index > 0; index--)
526 WineWindow* other = [[NSApp orderedWineWindows] objectAtIndex:index - 1];
527 if ([other level] < [self level])
528 [other setLevelWhenActive:[self level]];
532 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
533 [NSApp wineWindow:self ordered:NSWindowBelow relativeTo:prev];
537 /* Similarly, make sure this window is really above what it should be. */
538 if (next && [next level] > [self level])
539 [self setLevelWhenActive:[next level]];
540 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
541 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:next];
543 if (latentParentWindow)
545 if ([latentParentWindow level] > [self level])
546 [self setLevelWhenActive:[latentParentWindow level]];
547 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
548 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
549 self.latentParentWindow = nil;
552 /* Cocoa may adjust the frame when the window is ordered onto the screen.
553 Generate a frame-changed event just in case. The back end will ignore
554 it if nothing actually changed. */
555 [self windowDidResize:nil];
557 if (![self isExcludedFromWindowsMenu])
558 [NSApp addWindowsItem:self title:[self title] filename:NO];
566 self.latentParentWindow = [self parentWindow];
567 [latentParentWindow removeChildWindow:self];
569 [NSApp wineWindow:self ordered:NSWindowOut relativeTo:nil];
570 [NSApp removeWindowsItem:self];
573 - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
575 NSArray* screens = [NSScreen screens];
576 BOOL on_screen = [self isVisible];
577 NSRect frame, oldFrame;
579 if (![screens count]) return on_screen;
581 /* Origin is (left, top) in a top-down space. Need to convert it to
582 (left, bottom) in a bottom-up space. */
583 [NSApp flipRect:&contentRect];
587 on_screen = frame_intersects_screens(contentRect, screens);
592 if (!NSIsEmptyRect(contentRect))
594 oldFrame = [self frame];
595 frame = [self frameRectForContentRect:contentRect];
596 if (!NSEqualRects(frame, oldFrame))
598 if (NSEqualSizes(frame.size, oldFrame.size))
599 [self setFrameOrigin:frame.origin];
601 [self setFrame:frame display:YES];
607 [self adjustWindowLevel];
609 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
610 event. The back end will ignore it if nothing actually changed. */
611 [self windowDidResize:nil];
615 /* The back end is establishing a new window size and position. It's
616 not interested in any stale events regarding those that may be sitting
618 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
625 - (void) setMacDrvParentWindow:(WineWindow*)parent
627 if ([self parentWindow] != parent)
629 [[self parentWindow] removeChildWindow:self];
630 self.latentParentWindow = nil;
631 if ([self isVisible] && parent)
633 if ([parent level] > [self level])
634 [self setLevelWhenActive:[parent level]];
635 [parent addChildWindow:self ordered:NSWindowAbove];
636 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:parent];
639 self.latentParentWindow = parent;
643 - (void) setDisabled:(BOOL)newValue
645 if (disabled != newValue)
648 [self adjustFeaturesForState];
652 - (BOOL) needsTransparency
654 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
657 - (void) checkTransparency
659 if (![self isOpaque] && !self.needsTransparency)
661 [self setBackgroundColor:[NSColor windowBackgroundColor]];
662 [self setOpaque:YES];
664 else if ([self isOpaque] && self.needsTransparency)
666 [self setBackgroundColor:[NSColor clearColor]];
671 - (void) setShape:(NSBezierPath*)newShape
673 if (shape == newShape) return;
674 if (shape && newShape && [shape isEqual:newShape]) return;
678 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
682 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
684 shape = [newShape copy];
685 self.shapeChangedSinceLastDraw = TRUE;
687 [self checkTransparency];
690 - (void) postMouseButtonEvent:(NSEvent *)theEvent pressed:(int)pressed
692 CGPoint pt = CGEventGetLocation([theEvent CGEvent]);
695 event = macdrv_create_event(MOUSE_BUTTON, self);
696 event->mouse_button.button = [theEvent buttonNumber];
697 event->mouse_button.pressed = pressed;
698 event->mouse_button.x = pt.x;
699 event->mouse_button.y = pt.y;
700 event->mouse_button.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
702 [queue postEvent:event];
704 macdrv_release_event(event);
711 [NSApp transformProcessToForeground];
713 /* If a borderless window is offscreen, orderFront: won't move
714 it onscreen like it would for a titled window. Do that ourselves. */
715 screens = [NSScreen screens];
716 if (!([self styleMask] & NSTitledWindowMask) && ![self isVisible] &&
717 !frame_intersects_screens([self frame], screens))
719 NSScreen* primaryScreen = [screens objectAtIndex:0];
720 NSRect frame = [primaryScreen frame];
721 [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
722 frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
723 [self setFrame:frame display:YES];
726 if ([[NSApp orderedWineWindows] count])
730 front = [[NSApp orderedWineWindows] objectAtIndex:0];
733 for (front in [NSApp orderedWineWindows])
734 if (!front.floating) break;
736 if (front && [front levelWhenActive] > [self levelWhenActive])
737 [self setLevelWhenActive:[front levelWhenActive]];
739 [self orderFront:nil];
740 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
741 causing_becomeKeyWindow = TRUE;
742 [self makeKeyWindow];
743 causing_becomeKeyWindow = FALSE;
744 if (latentParentWindow)
746 if ([latentParentWindow level] > [self level])
747 [self setLevelWhenActive:[latentParentWindow level]];
748 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
749 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
750 self.latentParentWindow = nil;
752 if (![self isExcludedFromWindowsMenu])
753 [NSApp addWindowsItem:self title:[self title] filename:NO];
755 /* Cocoa may adjust the frame when the window is ordered onto the screen.
756 Generate a frame-changed event just in case. The back end will ignore
757 it if nothing actually changed. */
758 [self windowDidResize:nil];
761 - (void) postKey:(uint16_t)keyCode
762 pressed:(BOOL)pressed
763 modifiers:(NSUInteger)modifiers
764 event:(NSEvent*)theEvent
768 WineApplication* app = (WineApplication*)NSApp;
770 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
771 event->key.keycode = keyCode;
772 event->key.modifiers = modifiers;
773 event->key.time_ms = [app ticksForEventTime:[theEvent timestamp]];
775 if ((cgevent = [theEvent CGEvent]))
777 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
778 kCGKeyboardEventKeyboardType);
779 if (keyboardType != app.keyboardType)
781 app.keyboardType = keyboardType;
782 [app keyboardSelectionDidChange];
786 [queue postEvent:event];
788 macdrv_release_event(event);
791 - (void) postKeyEvent:(NSEvent *)theEvent
793 [self flagsChanged:theEvent];
794 [self postKey:[theEvent keyCode]
795 pressed:[theEvent type] == NSKeyDown
796 modifiers:[theEvent modifierFlags]
800 - (void) postMouseMovedEvent:(NSEvent *)theEvent absolute:(BOOL)absolute
806 CGPoint point = CGEventGetLocation([theEvent CGEvent]);
808 event = macdrv_create_event(MOUSE_MOVED_ABSOLUTE, self);
809 event->mouse_moved.x = point.x;
810 event->mouse_moved.y = point.y;
817 /* Add event delta to accumulated delta error */
818 /* deltaY is already flipped */
819 mouseMoveDeltaX += [theEvent deltaX];
820 mouseMoveDeltaY += [theEvent deltaY];
822 event = macdrv_create_event(MOUSE_MOVED, self);
823 event->mouse_moved.x = mouseMoveDeltaX;
824 event->mouse_moved.y = mouseMoveDeltaY;
826 /* Keep the remainder after integer truncation. */
827 mouseMoveDeltaX -= event->mouse_moved.x;
828 mouseMoveDeltaY -= event->mouse_moved.y;
831 if (event->type == MOUSE_MOVED_ABSOLUTE || event->mouse_moved.x || event->mouse_moved.y)
833 event->mouse_moved.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
835 [queue postEvent:event];
838 macdrv_release_event(event);
841 - (void) setLevelWhenActive:(NSInteger)level
843 levelWhenActive = level;
844 if (([NSApp isActive] || level <= NSFloatingWindowLevel) &&
845 level != [self level])
846 [self setLevel:level];
851 * ---------- NSWindow method overrides ----------
853 - (BOOL) canBecomeKeyWindow
855 if (causing_becomeKeyWindow) return YES;
856 if (self.disabled || self.noActivate) return NO;
857 return [self isKeyWindow];
860 - (BOOL) canBecomeMainWindow
862 return [self canBecomeKeyWindow];
865 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
867 // If a window is sized to completely cover a screen, then it's in
868 // full-screen mode. In that case, we don't allow NSWindow to constrain
870 NSRect contentRect = [self contentRectForFrameRect:frameRect];
871 if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
872 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
876 - (BOOL) isExcludedFromWindowsMenu
878 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
881 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
883 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
884 return [self isKeyWindow] || (!self.disabled && !self.noActivate);
885 return [super validateMenuItem:menuItem];
888 /* We don't call this. It's the action method of the items in the Window menu. */
889 - (void) makeKeyAndOrderFront:(id)sender
891 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
892 [NSApp windowGotFocus:self];
895 - (void) sendEvent:(NSEvent*)event
897 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
898 interface control. For example, Control-Tab switches focus among
899 views. We want to bypass that feature, so directly route key-down
900 events to -keyDown:. */
901 if ([event type] == NSKeyDown)
902 [[self firstResponder] keyDown:event];
905 if ([event type] == NSLeftMouseDown)
907 NSWindowButton windowButton;
908 BOOL broughtWindowForward = TRUE;
910 /* Since our windows generally claim they can't be made key, clicks
911 in their title bars are swallowed by the theme frame stuff. So,
912 we hook directly into the event stream and assume that any click
913 in the window will activate it, if Wine and the Win32 program
915 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
916 [NSApp windowGotFocus:self];
918 /* Any left-click on our window anyplace other than the close or
919 minimize buttons will bring it forward. */
920 for (windowButton = NSWindowCloseButton;
921 windowButton <= NSWindowMiniaturizeButton;
924 NSButton* button = [[event window] standardWindowButton:windowButton];
927 NSPoint point = [button convertPoint:[event locationInWindow] fromView:nil];
928 if ([button mouse:point inRect:[button bounds]])
930 broughtWindowForward = FALSE;
936 if (broughtWindowForward)
937 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
940 [super sendEvent:event];
946 * ---------- NSResponder method overrides ----------
948 - (void) mouseDown:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:1]; }
949 - (void) rightMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
950 - (void) otherMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
952 - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:0]; }
953 - (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
954 - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
956 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
957 - (void) keyUp:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
959 - (void) flagsChanged:(NSEvent *)theEvent
961 static const struct {
965 { NX_ALPHASHIFTMASK, kVK_CapsLock },
966 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
967 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
968 { NX_DEVICELCTLKEYMASK, kVK_Control },
969 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
970 { NX_DEVICELALTKEYMASK, kVK_Option },
971 { NX_DEVICERALTKEYMASK, kVK_RightOption },
972 { NX_DEVICELCMDKEYMASK, kVK_Command },
973 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
976 NSUInteger modifierFlags = [theEvent modifierFlags];
980 fix_device_modifiers_by_generic(&modifierFlags);
981 changed = modifierFlags ^ lastModifierFlags;
984 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
985 if (changed & modifiers[i].mask)
988 for (i = 0; i <= last_changed; i++)
990 if (changed & modifiers[i].mask)
992 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
994 if (i == last_changed)
995 lastModifierFlags = modifierFlags;
998 lastModifierFlags ^= modifiers[i].mask;
999 fix_generic_modifiers_by_device(&lastModifierFlags);
1002 // Caps lock generates one event for each press-release action.
1003 // We need to simulate a pair of events for each actual event.
1004 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1006 [self postKey:modifiers[i].keycode
1008 modifiers:lastModifierFlags
1009 event:(NSEvent*)theEvent];
1013 [self postKey:modifiers[i].keycode
1015 modifiers:lastModifierFlags
1016 event:(NSEvent*)theEvent];
1021 - (void) scrollWheel:(NSEvent *)theEvent
1024 macdrv_event* event;
1027 BOOL continuous = FALSE;
1029 cgevent = [theEvent CGEvent];
1030 pt = CGEventGetLocation(cgevent);
1032 event = macdrv_create_event(MOUSE_SCROLL, self);
1033 event->mouse_scroll.x = pt.x;
1034 event->mouse_scroll.y = pt.y;
1035 event->mouse_scroll.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
1037 if (CGEventGetIntegerValueField(cgevent, kCGScrollWheelEventIsContinuous))
1041 /* Continuous scroll wheel events come from high-precision scrolling
1042 hardware like Apple's Magic Mouse, Mighty Mouse, and trackpads.
1043 For these, we can get more precise data from the CGEvent API. */
1044 /* Axis 1 is vertical, axis 2 is horizontal. */
1045 x = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis2);
1046 y = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis1);
1050 double pixelsPerLine = 10;
1051 CGEventSourceRef source;
1053 /* The non-continuous values are in units of "lines", not pixels. */
1054 if ((source = CGEventCreateSourceFromEvent(cgevent)))
1056 pixelsPerLine = CGEventSourceGetPixelsPerLine(source);
1060 x = pixelsPerLine * [theEvent deltaX];
1061 y = pixelsPerLine * [theEvent deltaY];
1064 /* Mac: negative is right or down, positive is left or up.
1065 Win32: negative is left or down, positive is right or up.
1066 So, negate the X scroll value to translate. */
1069 /* The x,y values so far are in pixels. Win32 expects to receive some
1070 fraction of WHEEL_DELTA == 120. By my estimation, that's roughly
1071 6 times the pixel value. */
1072 event->mouse_scroll.x_scroll = 6 * x;
1073 event->mouse_scroll.y_scroll = 6 * y;
1077 /* For non-continuous "clicky" wheels, if there was any motion, make
1078 sure there was at least WHEEL_DELTA motion. This is so, at slow
1079 speeds where the system's acceleration curve is actually reducing the
1080 scroll distance, the user is sure to get some action out of each click.
1081 For example, this is important for rotating though weapons in a
1082 first-person shooter. */
1083 if (0 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 120)
1084 event->mouse_scroll.x_scroll = 120;
1085 else if (-120 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 0)
1086 event->mouse_scroll.x_scroll = -120;
1088 if (0 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 120)
1089 event->mouse_scroll.y_scroll = 120;
1090 else if (-120 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 0)
1091 event->mouse_scroll.y_scroll = -120;
1094 if (event->mouse_scroll.x_scroll || event->mouse_scroll.y_scroll)
1095 [queue postEvent:event];
1097 macdrv_release_event(event);
1102 * ---------- NSWindowDelegate methods ----------
1104 - (void)windowDidBecomeKey:(NSNotification *)notification
1106 NSEvent* event = [NSApp lastFlagsChanged];
1108 [self flagsChanged:event];
1110 if (causing_becomeKeyWindow) return;
1112 [NSApp windowGotFocus:self];
1115 - (void)windowDidDeminiaturize:(NSNotification *)notification
1117 if (!ignore_windowDeminiaturize)
1119 macdrv_event* event;
1121 /* Coalesce events by discarding any previous ones still in the queue. */
1122 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1123 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1126 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1127 [queue postEvent:event];
1128 macdrv_release_event(event);
1131 ignore_windowDeminiaturize = FALSE;
1133 [NSApp wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1136 - (void) windowDidEndLiveResize:(NSNotification *)notification
1138 [liveResizeDisplayTimer invalidate];
1139 [liveResizeDisplayTimer release];
1140 liveResizeDisplayTimer = nil;
1143 - (void)windowDidMove:(NSNotification *)notification
1145 [self windowDidResize:notification];
1148 - (void)windowDidResignKey:(NSNotification *)notification
1150 macdrv_event* event;
1152 if (causing_becomeKeyWindow) return;
1154 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1155 [queue postEvent:event];
1156 macdrv_release_event(event);
1159 - (void)windowDidResize:(NSNotification *)notification
1161 macdrv_event* event;
1162 NSRect frame = [self contentRectForFrameRect:[self frame]];
1164 [NSApp flipRect:&frame];
1166 /* Coalesce events by discarding any previous ones still in the queue. */
1167 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1170 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1171 event->window_frame_changed.frame = NSRectToCGRect(frame);
1172 [queue postEvent:event];
1173 macdrv_release_event(event);
1176 - (BOOL)windowShouldClose:(id)sender
1178 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1179 [queue postEvent:event];
1180 macdrv_release_event(event);
1184 - (void)windowWillMiniaturize:(NSNotification *)notification
1186 if (!ignore_windowMiniaturize)
1188 macdrv_event* event;
1190 /* Coalesce events by discarding any previous ones still in the queue. */
1191 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1192 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1195 event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
1196 [queue postEvent:event];
1197 macdrv_release_event(event);
1200 ignore_windowMiniaturize = FALSE;
1203 - (void) windowWillStartLiveResize:(NSNotification *)notification
1205 // There's a strange restriction in window redrawing during Cocoa-
1206 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
1207 // that happen synchronously when Cocoa tells us that our window size
1208 // has changed or asynchronously in a short interval thereafter provoke
1209 // the window to redraw. Calls to those methods that happen asynchronously
1210 // a half second or more after the last change of the window size aren't
1211 // heeded until the next resize-related user event (e.g. mouse movement).
1213 // Wine often has a significant delay between when it's been told that
1214 // the window has changed size and when it can flush completed drawing.
1215 // So, our windows would get stuck with incomplete drawing for as long
1216 // as the user holds the mouse button down and doesn't move it.
1218 // We address this by "manually" asking our windows to check if they need
1219 // redrawing every so often (during live resize only).
1220 [self windowDidEndLiveResize:nil];
1221 liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1223 selector:@selector(displayIfNeeded)
1226 [liveResizeDisplayTimer retain];
1227 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1228 forMode:NSRunLoopCommonModes];
1233 * ---------- NSPasteboardOwner methods ----------
1235 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1237 macdrv_query* query = macdrv_create_query();
1238 query->type = QUERY_PASTEBOARD_DATA;
1239 query->window = (macdrv_window)[self retain];
1240 query->pasteboard_data.type = (CFStringRef)[type copy];
1242 [self.queue query:query timeout:3];
1243 macdrv_release_query(query);
1248 * ---------- NSDraggingDestination methods ----------
1250 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1252 return [self draggingUpdated:sender];
1255 - (void) draggingExited:(id <NSDraggingInfo>)sender
1257 // This isn't really a query. We don't need any response. However, it
1258 // has to be processed in a similar manner as the other drag-and-drop
1259 // queries in order to maintain the proper order of operations.
1260 macdrv_query* query = macdrv_create_query();
1261 query->type = QUERY_DRAG_EXITED;
1262 query->window = (macdrv_window)[self retain];
1264 [self.queue query:query timeout:0.1];
1265 macdrv_release_query(query);
1268 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1270 NSDragOperation ret;
1271 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1272 NSPasteboard* pb = [sender draggingPasteboard];
1274 macdrv_query* query = macdrv_create_query();
1275 query->type = QUERY_DRAG_OPERATION;
1276 query->window = (macdrv_window)[self retain];
1277 query->drag_operation.x = pt.x;
1278 query->drag_operation.y = pt.y;
1279 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1280 query->drag_operation.accepted_op = NSDragOperationNone;
1281 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1283 [self.queue query:query timeout:3];
1284 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1285 macdrv_release_query(query);
1290 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1293 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1294 NSPasteboard* pb = [sender draggingPasteboard];
1296 macdrv_query* query = macdrv_create_query();
1297 query->type = QUERY_DRAG_DROP;
1298 query->window = (macdrv_window)[self retain];
1299 query->drag_drop.x = pt.x;
1300 query->drag_drop.y = pt.y;
1301 query->drag_drop.op = [sender draggingSourceOperationMask];
1302 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1304 [self.queue query:query timeout:3 * 60 processEvents:YES];
1305 ret = query->status;
1306 macdrv_release_query(query);
1311 - (BOOL) wantsPeriodicDraggingUpdates
1319 /***********************************************************************
1320 * macdrv_create_cocoa_window
1322 * Create a Cocoa window with the given content frame and features (e.g.
1323 * title bar, close box, etc.).
1325 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1326 CGRect frame, void* hwnd, macdrv_event_queue queue)
1328 __block WineWindow* window;
1331 window = [[WineWindow createWindowWithFeatures:wf
1332 windowFrame:NSRectFromCGRect(frame)
1334 queue:(WineEventQueue*)queue] retain];
1337 return (macdrv_window)window;
1340 /***********************************************************************
1341 * macdrv_destroy_cocoa_window
1343 * Destroy a Cocoa window.
1345 void macdrv_destroy_cocoa_window(macdrv_window w)
1347 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1348 WineWindow* window = (WineWindow*)w;
1350 [window.queue discardEventsMatchingMask:-1 forWindow:window];
1357 /***********************************************************************
1358 * macdrv_get_window_hwnd
1360 * Get the hwnd that was set for the window at creation.
1362 void* macdrv_get_window_hwnd(macdrv_window w)
1364 WineWindow* window = (WineWindow*)w;
1368 /***********************************************************************
1369 * macdrv_set_cocoa_window_features
1371 * Update a Cocoa window's features.
1373 void macdrv_set_cocoa_window_features(macdrv_window w,
1374 const struct macdrv_window_features* wf)
1376 WineWindow* window = (WineWindow*)w;
1379 [window setWindowFeatures:wf];
1383 /***********************************************************************
1384 * macdrv_set_cocoa_window_state
1386 * Update a Cocoa window's state.
1388 void macdrv_set_cocoa_window_state(macdrv_window w,
1389 const struct macdrv_window_state* state)
1391 WineWindow* window = (WineWindow*)w;
1394 [window setMacDrvState:state];
1398 /***********************************************************************
1399 * macdrv_set_cocoa_window_title
1401 * Set a Cocoa window's title.
1403 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1406 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1407 WineWindow* window = (WineWindow*)w;
1408 NSString* titleString;
1411 titleString = [NSString stringWithCharacters:title length:length];
1414 OnMainThreadAsync(^{
1415 [window setTitle:titleString];
1416 if ([window isVisible] && ![window isExcludedFromWindowsMenu])
1417 [NSApp changeWindowsItem:window title:titleString filename:NO];
1423 /***********************************************************************
1424 * macdrv_order_cocoa_window
1426 * Reorder a Cocoa window relative to other windows. If prev is
1427 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
1428 * it is ordered above that window. Otherwise, it is ordered to the
1431 * Returns true if the window has actually been ordered onto the screen
1432 * (i.e. if its frame intersects with a screen). Otherwise, false.
1434 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1437 WineWindow* window = (WineWindow*)w;
1438 __block BOOL on_screen;
1441 on_screen = [window orderBelow:(WineWindow*)prev
1442 orAbove:(WineWindow*)next];
1448 /***********************************************************************
1449 * macdrv_hide_cocoa_window
1451 * Hides a Cocoa window.
1453 void macdrv_hide_cocoa_window(macdrv_window w)
1455 WineWindow* window = (WineWindow*)w;
1458 [window doOrderOut];
1462 /***********************************************************************
1463 * macdrv_set_cocoa_window_frame
1465 * Move a Cocoa window. If the window has been moved out of the bounds
1466 * of the desktop, it is ordered out. (This routine won't ever order a
1467 * window in, though.)
1469 * Returns true if the window is on screen; false otherwise.
1471 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1473 WineWindow* window = (WineWindow*)w;
1474 __block BOOL on_screen;
1477 on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1483 /***********************************************************************
1484 * macdrv_get_cocoa_window_frame
1486 * Gets the frame of a Cocoa window.
1488 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1490 WineWindow* window = (WineWindow*)w;
1495 frame = [window contentRectForFrameRect:[window frame]];
1496 [NSApp flipRect:&frame];
1497 *out_frame = NSRectToCGRect(frame);
1501 /***********************************************************************
1502 * macdrv_set_cocoa_parent_window
1504 * Sets the parent window for a Cocoa window. If parent is NULL, clears
1505 * the parent window.
1507 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1509 WineWindow* window = (WineWindow*)w;
1512 [window setMacDrvParentWindow:(WineWindow*)parent];
1516 /***********************************************************************
1517 * macdrv_set_window_surface
1519 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1521 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1522 WineWindow* window = (WineWindow*)w;
1525 window.surface = surface;
1526 window.surface_mutex = mutex;
1532 /***********************************************************************
1533 * macdrv_window_needs_display
1535 * Mark a window as needing display in a specified rect (in non-client
1536 * area coordinates).
1538 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1540 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1541 WineWindow* window = (WineWindow*)w;
1543 OnMainThreadAsync(^{
1544 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1550 /***********************************************************************
1551 * macdrv_set_window_shape
1553 * Sets the shape of a Cocoa window from an array of rectangles. If
1554 * rects is NULL, resets the window's shape to its frame.
1556 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1558 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1559 WineWindow* window = (WineWindow*)w;
1562 if (!rects || !count)
1569 path = [NSBezierPath bezierPath];
1570 for (i = 0; i < count; i++)
1571 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1572 window.shape = path;
1579 /***********************************************************************
1580 * macdrv_set_window_alpha
1582 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1584 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1585 WineWindow* window = (WineWindow*)w;
1587 [window setAlphaValue:alpha];
1592 /***********************************************************************
1593 * macdrv_set_window_color_key
1595 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1598 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1599 WineWindow* window = (WineWindow*)w;
1602 window.colorKeyed = TRUE;
1603 window.colorKeyRed = keyRed;
1604 window.colorKeyGreen = keyGreen;
1605 window.colorKeyBlue = keyBlue;
1606 [window checkTransparency];
1612 /***********************************************************************
1613 * macdrv_clear_window_color_key
1615 void macdrv_clear_window_color_key(macdrv_window w)
1617 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1618 WineWindow* window = (WineWindow*)w;
1621 window.colorKeyed = FALSE;
1622 [window checkTransparency];
1628 /***********************************************************************
1629 * macdrv_window_use_per_pixel_alpha
1631 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1633 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1634 WineWindow* window = (WineWindow*)w;
1637 window.usePerPixelAlpha = use_per_pixel_alpha;
1638 [window checkTransparency];
1644 /***********************************************************************
1645 * macdrv_give_cocoa_window_focus
1647 * Makes the Cocoa window "key" (gives it keyboard focus). This also
1648 * orders it front and, if its frame was not within the desktop bounds,
1649 * Cocoa will typically move it on-screen.
1651 void macdrv_give_cocoa_window_focus(macdrv_window w)
1653 WineWindow* window = (WineWindow*)w;
1656 [window makeFocused];
1660 /***********************************************************************
1661 * macdrv_create_view
1663 * Creates and returns a view in the specified rect of the window. The
1664 * caller is responsible for calling macdrv_dispose_view() on the view
1665 * when it is done with it.
1667 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1669 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1670 WineWindow* window = (WineWindow*)w;
1671 __block WineContentView* view;
1673 if (CGRectIsNull(rect)) rect = CGRectZero;
1676 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1678 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1679 [view setAutoresizesSubviews:NO];
1680 [nc addObserver:view
1681 selector:@selector(updateGLContexts)
1682 name:NSViewGlobalFrameDidChangeNotification
1684 [nc addObserver:view
1685 selector:@selector(updateGLContexts)
1686 name:NSApplicationDidChangeScreenParametersNotification
1688 [[window contentView] addSubview:view];
1692 return (macdrv_view)view;
1695 /***********************************************************************
1696 * macdrv_dispose_view
1698 * Destroys a view previously returned by macdrv_create_view.
1700 void macdrv_dispose_view(macdrv_view v)
1702 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1703 WineContentView* view = (WineContentView*)v;
1706 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1708 [nc removeObserver:view
1709 name:NSViewGlobalFrameDidChangeNotification
1711 [nc removeObserver:view
1712 name:NSApplicationDidChangeScreenParametersNotification
1714 [view removeFromSuperview];
1721 /***********************************************************************
1722 * macdrv_set_view_window_and_frame
1724 * Move a view to a new window and/or position within its window. If w
1725 * is NULL, leave the view in its current window and just change its
1728 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1730 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1731 WineContentView* view = (WineContentView*)v;
1732 WineWindow* window = (WineWindow*)w;
1734 if (CGRectIsNull(rect)) rect = CGRectZero;
1737 BOOL changedWindow = (window && window != [view window]);
1738 NSRect newFrame = NSRectFromCGRect(rect);
1739 NSRect oldFrame = [view frame];
1743 [view removeFromSuperview];
1744 [[window contentView] addSubview:view];
1747 if (!NSEqualRects(oldFrame, newFrame))
1750 [[view superview] setNeedsDisplayInRect:oldFrame];
1751 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1752 [view setFrameSize:newFrame.size];
1753 else if (NSEqualSizes(oldFrame.size, newFrame.size))
1754 [view setFrameOrigin:newFrame.origin];
1756 [view setFrame:newFrame];
1757 [view setNeedsDisplay:YES];
1764 /***********************************************************************
1765 * macdrv_add_view_opengl_context
1767 * Add an OpenGL context to the list being tracked for each view.
1769 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1771 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1772 WineContentView* view = (WineContentView*)v;
1773 WineOpenGLContext *context = (WineOpenGLContext*)c;
1775 OnMainThreadAsync(^{
1776 [view addGLContext:context];
1782 /***********************************************************************
1783 * macdrv_remove_view_opengl_context
1785 * Add an OpenGL context to the list being tracked for each view.
1787 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1789 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1790 WineContentView* view = (WineContentView*)v;
1791 WineOpenGLContext *context = (WineOpenGLContext*)c;
1793 OnMainThreadAsync(^{
1794 [view removeGLContext:context];
1800 /***********************************************************************
1801 * macdrv_window_background_color
1803 * Returns the standard Mac window background color as a 32-bit value of
1804 * the form 0x00rrggbb.
1806 uint32_t macdrv_window_background_color(void)
1808 static uint32_t result;
1809 static dispatch_once_t once;
1811 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
1812 // color spaces (RGB or grayscale). So, the only way to get RGB values out
1813 // of it is to draw with it.
1814 dispatch_once(&once, ^{
1816 unsigned char rgbx[4];
1817 unsigned char *planes = rgbx;
1818 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
1825 colorSpaceName:NSCalibratedRGBColorSpace
1829 [NSGraphicsContext saveGraphicsState];
1830 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
1831 [[NSColor windowBackgroundColor] set];
1832 NSRectFill(NSMakeRect(0, 0, 1, 1));
1833 [NSGraphicsContext restoreGraphicsState];
1835 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];