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 (readwrite, nonatomic) NSInteger levelWhenActive;
160 @property (assign, nonatomic) void* imeData;
161 @property (nonatomic) BOOL commandDone;
166 @implementation WineContentView
170 [markedText release];
171 [glContexts release];
172 [pendingGlContexts release];
181 - (void) drawRect:(NSRect)rect
183 WineWindow* window = (WineWindow*)[self window];
185 for (WineOpenGLContext* context in pendingGlContexts)
186 context.needsUpdate = TRUE;
187 [glContexts addObjectsFromArray:pendingGlContexts];
188 [pendingGlContexts removeAllObjects];
190 if ([window contentView] != self)
193 if (window.surface && window.surface_mutex &&
194 !pthread_mutex_lock(window.surface_mutex))
199 if (get_surface_blit_rects(window.surface, &rects, &count) && count)
201 CGContextRef context;
204 [window.shape addClip];
206 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
207 CGContextSetBlendMode(context, kCGBlendModeCopy);
209 for (i = 0; i < count; i++)
214 imageRect = CGRectIntersection(rects[i], NSRectToCGRect(rect));
215 image = create_surface_image(window.surface, &imageRect, FALSE);
219 if (window.colorKeyed)
221 CGImageRef maskedImage;
222 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
223 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
224 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
225 maskedImage = CGImageCreateWithMaskingColors(image, components);
228 CGImageRelease(image);
233 CGContextDrawImage(context, imageRect, image);
235 CGImageRelease(image);
240 pthread_mutex_unlock(window.surface_mutex);
243 // If the window may be transparent, then we have to invalidate the
244 // shadow every time we draw. Also, if this is the first time we've
245 // drawn since changing from transparent to opaque.
246 if (![window isOpaque] || window.shapeChangedSinceLastDraw)
248 window.shapeChangedSinceLastDraw = FALSE;
249 [window invalidateShadow];
253 /* By default, NSView will swallow right-clicks in an attempt to support contextual
254 menus. We need to bypass that and allow the event to make it to the window. */
255 - (void) rightMouseDown:(NSEvent*)theEvent
257 [[self window] rightMouseDown:theEvent];
260 - (void) addGLContext:(WineOpenGLContext*)context
263 glContexts = [[NSMutableArray alloc] init];
264 if (!pendingGlContexts)
265 pendingGlContexts = [[NSMutableArray alloc] init];
266 [pendingGlContexts addObject:context];
267 [self setNeedsDisplay:YES];
270 - (void) removeGLContext:(WineOpenGLContext*)context
272 [glContexts removeObjectIdenticalTo:context];
273 [pendingGlContexts removeObjectIdenticalTo:context];
276 - (void) updateGLContexts
278 for (WineOpenGLContext* context in glContexts)
279 context.needsUpdate = TRUE;
282 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
287 - (BOOL) preservesContentDuringLiveResize
289 // Returning YES from this tells Cocoa to keep our view's content during
290 // a Cocoa-driven resize. In theory, we're also supposed to override
291 // -setFrameSize: to mark exposed sections as needing redisplay, but
292 // user32 will take care of that in a roundabout way. This way, we don't
293 // redraw until the window surface is flushed.
295 // This doesn't do anything when we resize the window ourselves.
299 - (BOOL)acceptsFirstResponder
301 return [[self window] contentView] == self;
304 - (void) completeText:(NSString*)text
307 WineWindow* window = (WineWindow*)[self window];
309 event = macdrv_create_event(IM_SET_TEXT, window);
310 event->im_set_text.data = [window imeData];
311 event->im_set_text.text = (CFStringRef)[text copy];
312 event->im_set_text.complete = TRUE;
314 [[window queue] postEvent:event];
316 macdrv_release_event(event);
318 [markedText deleteCharactersInRange:NSMakeRange(0, [markedText length])];
319 markedTextSelection = NSMakeRange(0, 0);
320 [[self inputContext] discardMarkedText];
324 * ---------- NSTextInputClient methods ----------
326 - (NSTextInputContext*) inputContext
329 markedText = [[NSMutableAttributedString alloc] init];
330 return [super inputContext];
333 - (void) insertText:(id)string replacementRange:(NSRange)replacementRange
335 if ([string isKindOfClass:[NSAttributedString class]])
336 string = [string string];
338 if ([string isKindOfClass:[NSString class]])
339 [self completeText:string];
342 - (void) doCommandBySelector:(SEL)aSelector
344 [(WineWindow*)[self window] setCommandDone:TRUE];
347 - (void) setMarkedText:(id)string selectedRange:(NSRange)selectedRange replacementRange:(NSRange)replacementRange
349 if ([string isKindOfClass:[NSAttributedString class]])
350 string = [string string];
352 if ([string isKindOfClass:[NSString class]])
355 WineWindow* window = (WineWindow*)[self window];
357 if (replacementRange.location == NSNotFound)
358 replacementRange = NSMakeRange(0, [markedText length]);
360 [markedText replaceCharactersInRange:replacementRange withString:string];
361 markedTextSelection = selectedRange;
362 markedTextSelection.location += replacementRange.location;
364 event = macdrv_create_event(IM_SET_TEXT, window);
365 event->im_set_text.data = [window imeData];
366 event->im_set_text.text = (CFStringRef)[[markedText string] copy];
367 event->im_set_text.complete = FALSE;
369 [[window queue] postEvent:event];
371 macdrv_release_event(event);
373 event = macdrv_create_event(IM_SET_CURSOR_POS, window);
374 event->im_set_cursor_pos.data = [window imeData];
375 event->im_set_cursor_pos.pos = markedTextSelection.location;
377 [[window queue] postEvent:event];
379 macdrv_release_event(event);
385 [self completeText:nil];
388 - (NSRange) selectedRange
390 return markedTextSelection;
393 - (NSRange) markedRange
395 NSRange range = NSMakeRange(0, [markedText length]);
397 range.location = NSNotFound;
401 - (BOOL) hasMarkedText
403 return [markedText length] > 0;
406 - (NSAttributedString*) attributedSubstringForProposedRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
408 if (aRange.location >= [markedText length])
411 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
413 *actualRange = aRange;
414 return [markedText attributedSubstringFromRange:aRange];
417 - (NSArray*) validAttributesForMarkedText
419 return [NSArray array];
422 - (NSRect) firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
424 aRange = NSIntersectionRange(aRange, NSMakeRange(0, [markedText length]));
426 *actualRange = aRange;
427 return NSMakeRect(100, 100, aRange.length ? 1 : 0, 12);
430 - (NSUInteger) characterIndexForPoint:(NSPoint)aPoint
435 - (NSInteger) windowLevel
437 return [[self window] level];
443 @implementation WineWindow
445 @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
446 @synthesize surface, surface_mutex;
447 @synthesize shape, shapeChangedSinceLastDraw;
448 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
449 @synthesize usePerPixelAlpha;
450 @synthesize levelWhenActive;
451 @synthesize imeData, commandDone;
453 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
454 windowFrame:(NSRect)window_frame
456 queue:(WineEventQueue*)queue
459 WineContentView* contentView;
460 NSTrackingArea* trackingArea;
462 [[WineApplicationController sharedController] flipRect:&window_frame];
464 window = [[[self alloc] initWithContentRect:window_frame
465 styleMask:style_mask_for_features(wf)
466 backing:NSBackingStoreBuffered
467 defer:YES] autorelease];
469 if (!window) return nil;
470 window->normalStyleMask = [window styleMask];
472 /* Standardize windows to eliminate differences between titled and
473 borderless windows and between NSWindow and NSPanel. */
474 [window setHidesOnDeactivate:NO];
475 [window setReleasedWhenClosed:NO];
477 [window disableCursorRects];
478 [window setShowsResizeIndicator:NO];
479 [window setHasShadow:wf->shadow];
480 [window setAcceptsMouseMovedEvents:YES];
481 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
482 [window setDelegate:window];
484 window.queue = queue;
486 [window registerForDraggedTypes:[NSArray arrayWithObjects:(NSString*)kUTTypeData,
487 (NSString*)kUTTypeContent,
490 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
493 [contentView setAutoresizesSubviews:NO];
495 /* We use tracking areas in addition to setAcceptsMouseMovedEvents:YES
496 because they give us mouse moves in the background. */
497 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
498 options:(NSTrackingMouseMoved |
499 NSTrackingActiveAlways |
500 NSTrackingInVisibleRect)
502 userInfo:nil] autorelease];
505 [contentView addTrackingArea:trackingArea];
507 [window setContentView:contentView];
508 [window setInitialFirstResponder:contentView];
515 [liveResizeDisplayTimer invalidate];
516 [liveResizeDisplayTimer release];
518 [latentParentWindow release];
523 - (void) adjustFeaturesForState
525 NSUInteger style = normalStyleMask;
528 style &= ~NSResizableWindowMask;
529 if (style != [self styleMask])
530 [self setStyleMask:style];
532 if (style & NSClosableWindowMask)
533 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
534 if (style & NSMiniaturizableWindowMask)
535 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
538 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
540 normalStyleMask = style_mask_for_features(wf);
541 [self adjustFeaturesForState];
542 [self setHasShadow:wf->shadow];
545 - (void) adjustWindowLevel
547 WineApplicationController* controller = [WineApplicationController sharedController];
549 BOOL fullscreen, captured;
552 WineWindow* other = nil;
554 screen = screen_covered_by_rect([self frame], [NSScreen screens]);
555 fullscreen = (screen != nil);
556 captured = (screen || [self screen]) && [controller areDisplaysCaptured];
558 if (captured || fullscreen)
561 level = CGShieldingWindowLevel() + 1; /* Need +1 or we don't get mouse moves */
563 level = NSMainMenuWindowLevel + 1;
568 else if (self.floating)
569 level = NSFloatingWindowLevel;
571 level = NSNormalWindowLevel;
573 index = [[controller orderedWineWindows] indexOfObjectIdenticalTo:self];
574 if (index != NSNotFound && index + 1 < [[controller orderedWineWindows] count])
576 other = [[controller orderedWineWindows] objectAtIndex:index + 1];
577 if (level < [other level])
578 level = [other level];
581 if (level != [self level])
583 [self setLevelWhenActive:level];
585 /* Setting the window level above has moved this window to the front
586 of all other windows at the same level. We need to move it
587 back into its proper place among other windows of that level.
588 Also, any windows which are supposed to be in front of it had
589 better have the same or higher window level. If not, bump them
591 if (index != NSNotFound && [self isVisible])
593 for (; index > 0; index--)
595 other = [[controller orderedWineWindows] objectAtIndex:index - 1];
596 if ([other level] < level)
597 [other setLevelWhenActive:level];
600 [self orderWindow:NSWindowBelow relativeTo:[other windowNumber]];
608 - (void) setMacDrvState:(const struct macdrv_window_state*)state
610 NSWindowCollectionBehavior behavior;
612 self.disabled = state->disabled;
613 self.noActivate = state->no_activate;
615 self.floating = state->floating;
616 [self adjustWindowLevel];
618 behavior = NSWindowCollectionBehaviorDefault;
619 if (state->excluded_by_expose)
620 behavior |= NSWindowCollectionBehaviorTransient;
622 behavior |= NSWindowCollectionBehaviorManaged;
623 if (state->excluded_by_cycle)
625 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
626 if ([self isVisible])
627 [NSApp removeWindowsItem:self];
631 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
632 if ([self isVisible])
633 [NSApp addWindowsItem:self title:[self title] filename:NO];
635 [self setCollectionBehavior:behavior];
637 if (state->minimized && ![self isMiniaturized])
639 ignore_windowMiniaturize = TRUE;
640 [self miniaturize:nil];
642 else if (!state->minimized && [self isMiniaturized])
644 ignore_windowDeminiaturize = TRUE;
645 [self deminiaturize:nil];
648 /* Whatever events regarding minimization might have been in the queue are now stale. */
649 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
650 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
654 /* Returns whether or not the window was ordered in, which depends on if
655 its frame intersects any screen. */
656 - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
658 WineApplicationController* controller = [WineApplicationController sharedController];
659 BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
662 [controller transformProcessToForeground];
666 /* Make sure that windows that should be above this one really are.
667 This is necessary since a full-screen window gets a boost to its
668 window level to be in front of the menu bar and Dock and that moves
669 it out of the z-order that Win32 would otherwise establish. */
670 if ([prev level] < [self level])
672 NSUInteger index = [[controller orderedWineWindows] indexOfObjectIdenticalTo:prev];
673 if (index != NSNotFound)
675 [prev setLevelWhenActive:[self level]];
676 for (; index > 0; index--)
678 WineWindow* other = [[controller orderedWineWindows] objectAtIndex:index - 1];
679 if ([other level] < [self level])
680 [other setLevelWhenActive:[self level]];
684 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
685 [controller wineWindow:self ordered:NSWindowBelow relativeTo:prev];
689 /* Similarly, make sure this window is really above what it should be. */
690 if (next && [next level] > [self level])
691 [self setLevelWhenActive:[next level]];
692 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
693 [controller wineWindow:self ordered:NSWindowAbove relativeTo:next];
695 if (latentParentWindow)
697 if ([latentParentWindow level] > [self level])
698 [self setLevelWhenActive:[latentParentWindow level]];
699 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
700 [controller wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
701 self.latentParentWindow = nil;
704 /* Cocoa may adjust the frame when the window is ordered onto the screen.
705 Generate a frame-changed event just in case. The back end will ignore
706 it if nothing actually changed. */
707 [self windowDidResize:nil];
709 if (![self isExcludedFromWindowsMenu])
710 [NSApp addWindowsItem:self title:[self title] filename:NO];
718 self.latentParentWindow = [self parentWindow];
719 [latentParentWindow removeChildWindow:self];
721 [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowOut relativeTo:nil];
722 [NSApp removeWindowsItem:self];
725 - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
727 NSArray* screens = [NSScreen screens];
728 BOOL on_screen = [self isVisible];
729 NSRect frame, oldFrame;
731 if (![screens count]) return on_screen;
733 /* Origin is (left, top) in a top-down space. Need to convert it to
734 (left, bottom) in a bottom-up space. */
735 [[WineApplicationController sharedController] flipRect:&contentRect];
739 on_screen = frame_intersects_screens(contentRect, screens);
744 if (!NSIsEmptyRect(contentRect))
746 oldFrame = [self frame];
747 frame = [self frameRectForContentRect:contentRect];
748 if (!NSEqualRects(frame, oldFrame))
750 if (NSEqualSizes(frame.size, oldFrame.size))
751 [self setFrameOrigin:frame.origin];
753 [self setFrame:frame display:YES];
759 [self adjustWindowLevel];
761 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
762 event. The back end will ignore it if nothing actually changed. */
763 [self windowDidResize:nil];
767 /* The back end is establishing a new window size and position. It's
768 not interested in any stale events regarding those that may be sitting
770 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
777 - (void) setMacDrvParentWindow:(WineWindow*)parent
779 if ([self parentWindow] != parent)
781 [[self parentWindow] removeChildWindow:self];
782 self.latentParentWindow = nil;
783 if ([self isVisible] && parent)
785 if ([parent level] > [self level])
786 [self setLevelWhenActive:[parent level]];
787 [parent addChildWindow:self ordered:NSWindowAbove];
788 [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowAbove relativeTo:parent];
791 self.latentParentWindow = parent;
795 - (void) setDisabled:(BOOL)newValue
797 if (disabled != newValue)
800 [self adjustFeaturesForState];
804 - (BOOL) needsTransparency
806 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
809 - (void) checkTransparency
811 if (![self isOpaque] && !self.needsTransparency)
813 [self setBackgroundColor:[NSColor windowBackgroundColor]];
814 [self setOpaque:YES];
816 else if ([self isOpaque] && self.needsTransparency)
818 [self setBackgroundColor:[NSColor clearColor]];
823 - (void) setShape:(NSBezierPath*)newShape
825 if (shape == newShape) return;
826 if (shape && newShape && [shape isEqual:newShape]) return;
830 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
834 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
836 shape = [newShape copy];
837 self.shapeChangedSinceLastDraw = TRUE;
839 [self checkTransparency];
842 - (void) postMouseButtonEvent:(NSEvent *)theEvent pressed:(int)pressed
844 CGPoint pt = CGEventGetLocation([theEvent CGEvent]);
847 event = macdrv_create_event(MOUSE_BUTTON, self);
848 event->mouse_button.button = [theEvent buttonNumber];
849 event->mouse_button.pressed = pressed;
850 event->mouse_button.x = pt.x;
851 event->mouse_button.y = pt.y;
852 event->mouse_button.time_ms = [[WineApplicationController sharedController] ticksForEventTime:[theEvent timestamp]];
854 [queue postEvent:event];
856 macdrv_release_event(event);
861 WineApplicationController* controller = [WineApplicationController sharedController];
864 [controller transformProcessToForeground];
866 /* If a borderless window is offscreen, orderFront: won't move
867 it onscreen like it would for a titled window. Do that ourselves. */
868 screens = [NSScreen screens];
869 if (!([self styleMask] & NSTitledWindowMask) && ![self isVisible] &&
870 !frame_intersects_screens([self frame], screens))
872 NSScreen* primaryScreen = [screens objectAtIndex:0];
873 NSRect frame = [primaryScreen frame];
874 [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
875 frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
876 [self setFrame:frame display:YES];
879 if ([[controller orderedWineWindows] count])
883 front = [[controller orderedWineWindows] objectAtIndex:0];
886 for (front in [controller orderedWineWindows])
887 if (!front.floating) break;
889 if (front && [front levelWhenActive] > [self levelWhenActive])
890 [self setLevelWhenActive:[front levelWhenActive]];
892 [self orderFront:nil];
893 [controller wineWindow:self ordered:NSWindowAbove relativeTo:nil];
894 causing_becomeKeyWindow = TRUE;
895 [self makeKeyWindow];
896 causing_becomeKeyWindow = FALSE;
897 if (latentParentWindow)
899 if ([latentParentWindow level] > [self level])
900 [self setLevelWhenActive:[latentParentWindow level]];
901 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
902 [controller wineWindow:self ordered:NSWindowAbove relativeTo:latentParentWindow];
903 self.latentParentWindow = nil;
905 if (![self isExcludedFromWindowsMenu])
906 [NSApp addWindowsItem:self title:[self title] filename:NO];
908 /* Cocoa may adjust the frame when the window is ordered onto the screen.
909 Generate a frame-changed event just in case. The back end will ignore
910 it if nothing actually changed. */
911 [self windowDidResize:nil];
914 - (void) postKey:(uint16_t)keyCode
915 pressed:(BOOL)pressed
916 modifiers:(NSUInteger)modifiers
917 event:(NSEvent*)theEvent
921 WineApplicationController* controller = [WineApplicationController sharedController];
923 event = macdrv_create_event(pressed ? KEY_PRESS : KEY_RELEASE, self);
924 event->key.keycode = keyCode;
925 event->key.modifiers = modifiers;
926 event->key.time_ms = [controller ticksForEventTime:[theEvent timestamp]];
928 if ((cgevent = [theEvent CGEvent]))
930 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
931 kCGKeyboardEventKeyboardType);
932 if (keyboardType != controller.keyboardType)
934 controller.keyboardType = keyboardType;
935 [controller keyboardSelectionDidChange];
939 [queue postEvent:event];
941 macdrv_release_event(event);
944 - (void) postKeyEvent:(NSEvent *)theEvent
946 [self flagsChanged:theEvent];
947 [self postKey:[theEvent keyCode]
948 pressed:[theEvent type] == NSKeyDown
949 modifiers:[theEvent modifierFlags]
953 - (void) postMouseMovedEvent:(NSEvent *)theEvent absolute:(BOOL)absolute
959 CGPoint point = CGEventGetLocation([theEvent CGEvent]);
961 event = macdrv_create_event(MOUSE_MOVED_ABSOLUTE, self);
962 event->mouse_moved.x = point.x;
963 event->mouse_moved.y = point.y;
970 /* Add event delta to accumulated delta error */
971 /* deltaY is already flipped */
972 mouseMoveDeltaX += [theEvent deltaX];
973 mouseMoveDeltaY += [theEvent deltaY];
975 event = macdrv_create_event(MOUSE_MOVED, self);
976 event->mouse_moved.x = mouseMoveDeltaX;
977 event->mouse_moved.y = mouseMoveDeltaY;
979 /* Keep the remainder after integer truncation. */
980 mouseMoveDeltaX -= event->mouse_moved.x;
981 mouseMoveDeltaY -= event->mouse_moved.y;
984 if (event->type == MOUSE_MOVED_ABSOLUTE || event->mouse_moved.x || event->mouse_moved.y)
986 event->mouse_moved.time_ms = [[WineApplicationController sharedController] ticksForEventTime:[theEvent timestamp]];
988 [queue postEvent:event];
991 macdrv_release_event(event);
994 - (void) setLevelWhenActive:(NSInteger)level
996 levelWhenActive = level;
997 if (([NSApp isActive] || level <= NSFloatingWindowLevel) &&
998 level != [self level])
999 [self setLevel:level];
1004 * ---------- NSWindow method overrides ----------
1006 - (BOOL) canBecomeKeyWindow
1008 if (causing_becomeKeyWindow) return YES;
1009 if (self.disabled || self.noActivate) return NO;
1010 return [self isKeyWindow];
1013 - (BOOL) canBecomeMainWindow
1015 return [self canBecomeKeyWindow];
1018 - (NSRect) constrainFrameRect:(NSRect)frameRect toScreen:(NSScreen *)screen
1020 // If a window is sized to completely cover a screen, then it's in
1021 // full-screen mode. In that case, we don't allow NSWindow to constrain
1023 NSRect contentRect = [self contentRectForFrameRect:frameRect];
1024 if (!screen_covered_by_rect(contentRect, [NSScreen screens]))
1025 frameRect = [super constrainFrameRect:frameRect toScreen:screen];
1029 - (BOOL) isExcludedFromWindowsMenu
1031 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
1034 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
1036 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
1037 return [self isKeyWindow] || (!self.disabled && !self.noActivate);
1038 return [super validateMenuItem:menuItem];
1041 /* We don't call this. It's the action method of the items in the Window menu. */
1042 - (void) makeKeyAndOrderFront:(id)sender
1044 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1045 [[WineApplicationController sharedController] windowGotFocus:self];
1048 - (void) sendEvent:(NSEvent*)event
1050 WineApplicationController* controller = [WineApplicationController sharedController];
1052 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
1053 interface control. For example, Control-Tab switches focus among
1054 views. We want to bypass that feature, so directly route key-down
1055 events to -keyDown:. */
1056 if ([event type] == NSKeyDown)
1057 [[self firstResponder] keyDown:event];
1060 if ([event type] == NSLeftMouseDown)
1062 NSWindowButton windowButton;
1063 BOOL broughtWindowForward = TRUE;
1065 /* Since our windows generally claim they can't be made key, clicks
1066 in their title bars are swallowed by the theme frame stuff. So,
1067 we hook directly into the event stream and assume that any click
1068 in the window will activate it, if Wine and the Win32 program
1070 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
1071 [controller windowGotFocus:self];
1073 /* Any left-click on our window anyplace other than the close or
1074 minimize buttons will bring it forward. */
1075 for (windowButton = NSWindowCloseButton;
1076 windowButton <= NSWindowMiniaturizeButton;
1079 NSButton* button = [[event window] standardWindowButton:windowButton];
1082 NSPoint point = [button convertPoint:[event locationInWindow] fromView:nil];
1083 if ([button mouse:point inRect:[button bounds]])
1085 broughtWindowForward = FALSE;
1091 if (broughtWindowForward)
1092 [controller wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1095 [super sendEvent:event];
1101 * ---------- NSResponder method overrides ----------
1103 - (void) mouseDown:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:1]; }
1104 - (void) rightMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
1105 - (void) otherMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
1107 - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:0]; }
1108 - (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
1109 - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
1111 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1112 - (void) keyUp:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
1114 - (void) flagsChanged:(NSEvent *)theEvent
1116 static const struct {
1120 { NX_ALPHASHIFTMASK, kVK_CapsLock },
1121 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
1122 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
1123 { NX_DEVICELCTLKEYMASK, kVK_Control },
1124 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
1125 { NX_DEVICELALTKEYMASK, kVK_Option },
1126 { NX_DEVICERALTKEYMASK, kVK_RightOption },
1127 { NX_DEVICELCMDKEYMASK, kVK_Command },
1128 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
1131 NSUInteger modifierFlags = [theEvent modifierFlags];
1133 int i, last_changed;
1135 fix_device_modifiers_by_generic(&modifierFlags);
1136 changed = modifierFlags ^ lastModifierFlags;
1139 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
1140 if (changed & modifiers[i].mask)
1143 for (i = 0; i <= last_changed; i++)
1145 if (changed & modifiers[i].mask)
1147 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
1149 if (i == last_changed)
1150 lastModifierFlags = modifierFlags;
1153 lastModifierFlags ^= modifiers[i].mask;
1154 fix_generic_modifiers_by_device(&lastModifierFlags);
1157 // Caps lock generates one event for each press-release action.
1158 // We need to simulate a pair of events for each actual event.
1159 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
1161 [self postKey:modifiers[i].keycode
1163 modifiers:lastModifierFlags
1164 event:(NSEvent*)theEvent];
1168 [self postKey:modifiers[i].keycode
1170 modifiers:lastModifierFlags
1171 event:(NSEvent*)theEvent];
1176 - (void) scrollWheel:(NSEvent *)theEvent
1179 macdrv_event* event;
1182 BOOL continuous = FALSE;
1184 cgevent = [theEvent CGEvent];
1185 pt = CGEventGetLocation(cgevent);
1187 event = macdrv_create_event(MOUSE_SCROLL, self);
1188 event->mouse_scroll.x = pt.x;
1189 event->mouse_scroll.y = pt.y;
1190 event->mouse_scroll.time_ms = [[WineApplicationController sharedController] ticksForEventTime:[theEvent timestamp]];
1192 if (CGEventGetIntegerValueField(cgevent, kCGScrollWheelEventIsContinuous))
1196 /* Continuous scroll wheel events come from high-precision scrolling
1197 hardware like Apple's Magic Mouse, Mighty Mouse, and trackpads.
1198 For these, we can get more precise data from the CGEvent API. */
1199 /* Axis 1 is vertical, axis 2 is horizontal. */
1200 x = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis2);
1201 y = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis1);
1205 double pixelsPerLine = 10;
1206 CGEventSourceRef source;
1208 /* The non-continuous values are in units of "lines", not pixels. */
1209 if ((source = CGEventCreateSourceFromEvent(cgevent)))
1211 pixelsPerLine = CGEventSourceGetPixelsPerLine(source);
1215 x = pixelsPerLine * [theEvent deltaX];
1216 y = pixelsPerLine * [theEvent deltaY];
1219 /* Mac: negative is right or down, positive is left or up.
1220 Win32: negative is left or down, positive is right or up.
1221 So, negate the X scroll value to translate. */
1224 /* The x,y values so far are in pixels. Win32 expects to receive some
1225 fraction of WHEEL_DELTA == 120. By my estimation, that's roughly
1226 6 times the pixel value. */
1227 event->mouse_scroll.x_scroll = 6 * x;
1228 event->mouse_scroll.y_scroll = 6 * y;
1232 /* For non-continuous "clicky" wheels, if there was any motion, make
1233 sure there was at least WHEEL_DELTA motion. This is so, at slow
1234 speeds where the system's acceleration curve is actually reducing the
1235 scroll distance, the user is sure to get some action out of each click.
1236 For example, this is important for rotating though weapons in a
1237 first-person shooter. */
1238 if (0 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 120)
1239 event->mouse_scroll.x_scroll = 120;
1240 else if (-120 < event->mouse_scroll.x_scroll && event->mouse_scroll.x_scroll < 0)
1241 event->mouse_scroll.x_scroll = -120;
1243 if (0 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 120)
1244 event->mouse_scroll.y_scroll = 120;
1245 else if (-120 < event->mouse_scroll.y_scroll && event->mouse_scroll.y_scroll < 0)
1246 event->mouse_scroll.y_scroll = -120;
1249 if (event->mouse_scroll.x_scroll || event->mouse_scroll.y_scroll)
1250 [queue postEvent:event];
1252 macdrv_release_event(event);
1257 * ---------- NSWindowDelegate methods ----------
1259 - (void)windowDidBecomeKey:(NSNotification *)notification
1261 WineApplicationController* controller = [WineApplicationController sharedController];
1262 NSEvent* event = [controller lastFlagsChanged];
1264 [self flagsChanged:event];
1266 if (causing_becomeKeyWindow) return;
1268 [controller windowGotFocus:self];
1271 - (void)windowDidDeminiaturize:(NSNotification *)notification
1273 if (!ignore_windowDeminiaturize)
1275 macdrv_event* event;
1277 /* Coalesce events by discarding any previous ones still in the queue. */
1278 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1279 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1282 event = macdrv_create_event(WINDOW_DID_UNMINIMIZE, self);
1283 [queue postEvent:event];
1284 macdrv_release_event(event);
1287 ignore_windowDeminiaturize = FALSE;
1289 [[WineApplicationController sharedController] wineWindow:self ordered:NSWindowAbove relativeTo:nil];
1292 - (void) windowDidEndLiveResize:(NSNotification *)notification
1294 [liveResizeDisplayTimer invalidate];
1295 [liveResizeDisplayTimer release];
1296 liveResizeDisplayTimer = nil;
1299 - (void)windowDidMove:(NSNotification *)notification
1301 [self windowDidResize:notification];
1304 - (void)windowDidResignKey:(NSNotification *)notification
1306 macdrv_event* event;
1308 if (causing_becomeKeyWindow) return;
1310 event = macdrv_create_event(WINDOW_LOST_FOCUS, self);
1311 [queue postEvent:event];
1312 macdrv_release_event(event);
1315 - (void)windowDidResize:(NSNotification *)notification
1317 macdrv_event* event;
1318 NSRect frame = [self contentRectForFrameRect:[self frame]];
1320 [[WineApplicationController sharedController] flipRect:&frame];
1322 /* Coalesce events by discarding any previous ones still in the queue. */
1323 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
1326 event = macdrv_create_event(WINDOW_FRAME_CHANGED, self);
1327 event->window_frame_changed.frame = NSRectToCGRect(frame);
1328 [queue postEvent:event];
1329 macdrv_release_event(event);
1332 - (BOOL)windowShouldClose:(id)sender
1334 macdrv_event* event = macdrv_create_event(WINDOW_CLOSE_REQUESTED, self);
1335 [queue postEvent:event];
1336 macdrv_release_event(event);
1340 - (void)windowWillMiniaturize:(NSNotification *)notification
1342 if (!ignore_windowMiniaturize)
1344 macdrv_event* event;
1346 /* Coalesce events by discarding any previous ones still in the queue. */
1347 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
1348 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
1351 event = macdrv_create_event(WINDOW_DID_MINIMIZE, self);
1352 [queue postEvent:event];
1353 macdrv_release_event(event);
1356 ignore_windowMiniaturize = FALSE;
1359 - (void) windowWillStartLiveResize:(NSNotification *)notification
1361 // There's a strange restriction in window redrawing during Cocoa-
1362 // managed window resizing. Only calls to -[NSView setNeedsDisplay...]
1363 // that happen synchronously when Cocoa tells us that our window size
1364 // has changed or asynchronously in a short interval thereafter provoke
1365 // the window to redraw. Calls to those methods that happen asynchronously
1366 // a half second or more after the last change of the window size aren't
1367 // heeded until the next resize-related user event (e.g. mouse movement).
1369 // Wine often has a significant delay between when it's been told that
1370 // the window has changed size and when it can flush completed drawing.
1371 // So, our windows would get stuck with incomplete drawing for as long
1372 // as the user holds the mouse button down and doesn't move it.
1374 // We address this by "manually" asking our windows to check if they need
1375 // redrawing every so often (during live resize only).
1376 [self windowDidEndLiveResize:nil];
1377 liveResizeDisplayTimer = [NSTimer scheduledTimerWithTimeInterval:1.0/30.0
1379 selector:@selector(displayIfNeeded)
1382 [liveResizeDisplayTimer retain];
1383 [[NSRunLoop currentRunLoop] addTimer:liveResizeDisplayTimer
1384 forMode:NSRunLoopCommonModes];
1389 * ---------- NSPasteboardOwner methods ----------
1391 - (void) pasteboard:(NSPasteboard *)sender provideDataForType:(NSString *)type
1393 macdrv_query* query = macdrv_create_query();
1394 query->type = QUERY_PASTEBOARD_DATA;
1395 query->window = (macdrv_window)[self retain];
1396 query->pasteboard_data.type = (CFStringRef)[type copy];
1398 [self.queue query:query timeout:3];
1399 macdrv_release_query(query);
1404 * ---------- NSDraggingDestination methods ----------
1406 - (NSDragOperation) draggingEntered:(id <NSDraggingInfo>)sender
1408 return [self draggingUpdated:sender];
1411 - (void) draggingExited:(id <NSDraggingInfo>)sender
1413 // This isn't really a query. We don't need any response. However, it
1414 // has to be processed in a similar manner as the other drag-and-drop
1415 // queries in order to maintain the proper order of operations.
1416 macdrv_query* query = macdrv_create_query();
1417 query->type = QUERY_DRAG_EXITED;
1418 query->window = (macdrv_window)[self retain];
1420 [self.queue query:query timeout:0.1];
1421 macdrv_release_query(query);
1424 - (NSDragOperation) draggingUpdated:(id <NSDraggingInfo>)sender
1426 NSDragOperation ret;
1427 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1428 NSPasteboard* pb = [sender draggingPasteboard];
1430 macdrv_query* query = macdrv_create_query();
1431 query->type = QUERY_DRAG_OPERATION;
1432 query->window = (macdrv_window)[self retain];
1433 query->drag_operation.x = pt.x;
1434 query->drag_operation.y = pt.y;
1435 query->drag_operation.offered_ops = [sender draggingSourceOperationMask];
1436 query->drag_operation.accepted_op = NSDragOperationNone;
1437 query->drag_operation.pasteboard = (CFTypeRef)[pb retain];
1439 [self.queue query:query timeout:3];
1440 ret = query->status ? query->drag_operation.accepted_op : NSDragOperationNone;
1441 macdrv_release_query(query);
1446 - (BOOL) performDragOperation:(id <NSDraggingInfo>)sender
1449 NSPoint pt = [[self contentView] convertPoint:[sender draggingLocation] fromView:nil];
1450 NSPasteboard* pb = [sender draggingPasteboard];
1452 macdrv_query* query = macdrv_create_query();
1453 query->type = QUERY_DRAG_DROP;
1454 query->window = (macdrv_window)[self retain];
1455 query->drag_drop.x = pt.x;
1456 query->drag_drop.y = pt.y;
1457 query->drag_drop.op = [sender draggingSourceOperationMask];
1458 query->drag_drop.pasteboard = (CFTypeRef)[pb retain];
1460 [self.queue query:query timeout:3 * 60 processEvents:YES];
1461 ret = query->status;
1462 macdrv_release_query(query);
1467 - (BOOL) wantsPeriodicDraggingUpdates
1475 /***********************************************************************
1476 * macdrv_create_cocoa_window
1478 * Create a Cocoa window with the given content frame and features (e.g.
1479 * title bar, close box, etc.).
1481 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
1482 CGRect frame, void* hwnd, macdrv_event_queue queue)
1484 __block WineWindow* window;
1487 window = [[WineWindow createWindowWithFeatures:wf
1488 windowFrame:NSRectFromCGRect(frame)
1490 queue:(WineEventQueue*)queue] retain];
1493 return (macdrv_window)window;
1496 /***********************************************************************
1497 * macdrv_destroy_cocoa_window
1499 * Destroy a Cocoa window.
1501 void macdrv_destroy_cocoa_window(macdrv_window w)
1503 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1504 WineWindow* window = (WineWindow*)w;
1506 [window.queue discardEventsMatchingMask:-1 forWindow:window];
1513 /***********************************************************************
1514 * macdrv_get_window_hwnd
1516 * Get the hwnd that was set for the window at creation.
1518 void* macdrv_get_window_hwnd(macdrv_window w)
1520 WineWindow* window = (WineWindow*)w;
1524 /***********************************************************************
1525 * macdrv_set_cocoa_window_features
1527 * Update a Cocoa window's features.
1529 void macdrv_set_cocoa_window_features(macdrv_window w,
1530 const struct macdrv_window_features* wf)
1532 WineWindow* window = (WineWindow*)w;
1535 [window setWindowFeatures:wf];
1539 /***********************************************************************
1540 * macdrv_set_cocoa_window_state
1542 * Update a Cocoa window's state.
1544 void macdrv_set_cocoa_window_state(macdrv_window w,
1545 const struct macdrv_window_state* state)
1547 WineWindow* window = (WineWindow*)w;
1550 [window setMacDrvState:state];
1554 /***********************************************************************
1555 * macdrv_set_cocoa_window_title
1557 * Set a Cocoa window's title.
1559 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1562 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1563 WineWindow* window = (WineWindow*)w;
1564 NSString* titleString;
1567 titleString = [NSString stringWithCharacters:title length:length];
1570 OnMainThreadAsync(^{
1571 [window setTitle:titleString];
1572 if ([window isVisible] && ![window isExcludedFromWindowsMenu])
1573 [NSApp changeWindowsItem:window title:titleString filename:NO];
1579 /***********************************************************************
1580 * macdrv_order_cocoa_window
1582 * Reorder a Cocoa window relative to other windows. If prev is
1583 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
1584 * it is ordered above that window. Otherwise, it is ordered to the
1587 * Returns true if the window has actually been ordered onto the screen
1588 * (i.e. if its frame intersects with a screen). Otherwise, false.
1590 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1593 WineWindow* window = (WineWindow*)w;
1594 __block BOOL on_screen;
1597 on_screen = [window orderBelow:(WineWindow*)prev
1598 orAbove:(WineWindow*)next];
1604 /***********************************************************************
1605 * macdrv_hide_cocoa_window
1607 * Hides a Cocoa window.
1609 void macdrv_hide_cocoa_window(macdrv_window w)
1611 WineWindow* window = (WineWindow*)w;
1614 [window doOrderOut];
1618 /***********************************************************************
1619 * macdrv_set_cocoa_window_frame
1621 * Move a Cocoa window. If the window has been moved out of the bounds
1622 * of the desktop, it is ordered out. (This routine won't ever order a
1623 * window in, though.)
1625 * Returns true if the window is on screen; false otherwise.
1627 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1629 WineWindow* window = (WineWindow*)w;
1630 __block BOOL on_screen;
1633 on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1639 /***********************************************************************
1640 * macdrv_get_cocoa_window_frame
1642 * Gets the frame of a Cocoa window.
1644 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1646 WineWindow* window = (WineWindow*)w;
1651 frame = [window contentRectForFrameRect:[window frame]];
1652 [[WineApplicationController sharedController] flipRect:&frame];
1653 *out_frame = NSRectToCGRect(frame);
1657 /***********************************************************************
1658 * macdrv_set_cocoa_parent_window
1660 * Sets the parent window for a Cocoa window. If parent is NULL, clears
1661 * the parent window.
1663 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1665 WineWindow* window = (WineWindow*)w;
1668 [window setMacDrvParentWindow:(WineWindow*)parent];
1672 /***********************************************************************
1673 * macdrv_set_window_surface
1675 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1677 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1678 WineWindow* window = (WineWindow*)w;
1681 window.surface = surface;
1682 window.surface_mutex = mutex;
1688 /***********************************************************************
1689 * macdrv_window_needs_display
1691 * Mark a window as needing display in a specified rect (in non-client
1692 * area coordinates).
1694 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1696 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1697 WineWindow* window = (WineWindow*)w;
1699 OnMainThreadAsync(^{
1700 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1706 /***********************************************************************
1707 * macdrv_set_window_shape
1709 * Sets the shape of a Cocoa window from an array of rectangles. If
1710 * rects is NULL, resets the window's shape to its frame.
1712 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1714 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1715 WineWindow* window = (WineWindow*)w;
1718 if (!rects || !count)
1725 path = [NSBezierPath bezierPath];
1726 for (i = 0; i < count; i++)
1727 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1728 window.shape = path;
1735 /***********************************************************************
1736 * macdrv_set_window_alpha
1738 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1740 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1741 WineWindow* window = (WineWindow*)w;
1743 [window setAlphaValue:alpha];
1748 /***********************************************************************
1749 * macdrv_set_window_color_key
1751 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1754 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1755 WineWindow* window = (WineWindow*)w;
1758 window.colorKeyed = TRUE;
1759 window.colorKeyRed = keyRed;
1760 window.colorKeyGreen = keyGreen;
1761 window.colorKeyBlue = keyBlue;
1762 [window checkTransparency];
1768 /***********************************************************************
1769 * macdrv_clear_window_color_key
1771 void macdrv_clear_window_color_key(macdrv_window w)
1773 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1774 WineWindow* window = (WineWindow*)w;
1777 window.colorKeyed = FALSE;
1778 [window checkTransparency];
1784 /***********************************************************************
1785 * macdrv_window_use_per_pixel_alpha
1787 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1789 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1790 WineWindow* window = (WineWindow*)w;
1793 window.usePerPixelAlpha = use_per_pixel_alpha;
1794 [window checkTransparency];
1800 /***********************************************************************
1801 * macdrv_give_cocoa_window_focus
1803 * Makes the Cocoa window "key" (gives it keyboard focus). This also
1804 * orders it front and, if its frame was not within the desktop bounds,
1805 * Cocoa will typically move it on-screen.
1807 void macdrv_give_cocoa_window_focus(macdrv_window w)
1809 WineWindow* window = (WineWindow*)w;
1812 [window makeFocused];
1816 /***********************************************************************
1817 * macdrv_create_view
1819 * Creates and returns a view in the specified rect of the window. The
1820 * caller is responsible for calling macdrv_dispose_view() on the view
1821 * when it is done with it.
1823 macdrv_view macdrv_create_view(macdrv_window w, CGRect rect)
1825 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1826 WineWindow* window = (WineWindow*)w;
1827 __block WineContentView* view;
1829 if (CGRectIsNull(rect)) rect = CGRectZero;
1832 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1834 view = [[WineContentView alloc] initWithFrame:NSRectFromCGRect(rect)];
1835 [view setAutoresizesSubviews:NO];
1836 [nc addObserver:view
1837 selector:@selector(updateGLContexts)
1838 name:NSViewGlobalFrameDidChangeNotification
1840 [nc addObserver:view
1841 selector:@selector(updateGLContexts)
1842 name:NSApplicationDidChangeScreenParametersNotification
1844 [[window contentView] addSubview:view];
1848 return (macdrv_view)view;
1851 /***********************************************************************
1852 * macdrv_dispose_view
1854 * Destroys a view previously returned by macdrv_create_view.
1856 void macdrv_dispose_view(macdrv_view v)
1858 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1859 WineContentView* view = (WineContentView*)v;
1862 NSNotificationCenter* nc = [NSNotificationCenter defaultCenter];
1864 [nc removeObserver:view
1865 name:NSViewGlobalFrameDidChangeNotification
1867 [nc removeObserver:view
1868 name:NSApplicationDidChangeScreenParametersNotification
1870 [view removeFromSuperview];
1877 /***********************************************************************
1878 * macdrv_set_view_window_and_frame
1880 * Move a view to a new window and/or position within its window. If w
1881 * is NULL, leave the view in its current window and just change its
1884 void macdrv_set_view_window_and_frame(macdrv_view v, macdrv_window w, CGRect rect)
1886 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1887 WineContentView* view = (WineContentView*)v;
1888 WineWindow* window = (WineWindow*)w;
1890 if (CGRectIsNull(rect)) rect = CGRectZero;
1893 BOOL changedWindow = (window && window != [view window]);
1894 NSRect newFrame = NSRectFromCGRect(rect);
1895 NSRect oldFrame = [view frame];
1899 [view removeFromSuperview];
1900 [[window contentView] addSubview:view];
1903 if (!NSEqualRects(oldFrame, newFrame))
1906 [[view superview] setNeedsDisplayInRect:oldFrame];
1907 if (NSEqualPoints(oldFrame.origin, newFrame.origin))
1908 [view setFrameSize:newFrame.size];
1909 else if (NSEqualSizes(oldFrame.size, newFrame.size))
1910 [view setFrameOrigin:newFrame.origin];
1912 [view setFrame:newFrame];
1913 [view setNeedsDisplay:YES];
1920 /***********************************************************************
1921 * macdrv_add_view_opengl_context
1923 * Add an OpenGL context to the list being tracked for each view.
1925 void macdrv_add_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1927 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1928 WineContentView* view = (WineContentView*)v;
1929 WineOpenGLContext *context = (WineOpenGLContext*)c;
1931 OnMainThreadAsync(^{
1932 [view addGLContext:context];
1938 /***********************************************************************
1939 * macdrv_remove_view_opengl_context
1941 * Add an OpenGL context to the list being tracked for each view.
1943 void macdrv_remove_view_opengl_context(macdrv_view v, macdrv_opengl_context c)
1945 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1946 WineContentView* view = (WineContentView*)v;
1947 WineOpenGLContext *context = (WineOpenGLContext*)c;
1949 OnMainThreadAsync(^{
1950 [view removeGLContext:context];
1956 /***********************************************************************
1957 * macdrv_window_background_color
1959 * Returns the standard Mac window background color as a 32-bit value of
1960 * the form 0x00rrggbb.
1962 uint32_t macdrv_window_background_color(void)
1964 static uint32_t result;
1965 static dispatch_once_t once;
1967 // Annoyingly, [NSColor windowBackgroundColor] refuses to convert to other
1968 // color spaces (RGB or grayscale). So, the only way to get RGB values out
1969 // of it is to draw with it.
1970 dispatch_once(&once, ^{
1972 unsigned char rgbx[4];
1973 unsigned char *planes = rgbx;
1974 NSBitmapImageRep *bitmap = [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:&planes
1981 colorSpaceName:NSCalibratedRGBColorSpace
1985 [NSGraphicsContext saveGraphicsState];
1986 [NSGraphicsContext setCurrentContext:[NSGraphicsContext graphicsContextWithBitmapImageRep:bitmap]];
1987 [[NSColor windowBackgroundColor] set];
1988 NSRectFill(NSMakeRect(0, 0, 1, 1));
1989 [NSGraphicsContext restoreGraphicsState];
1991 result = rgbx[0] << 16 | rgbx[1] << 8 | rgbx[2];
1998 /***********************************************************************
1999 * macdrv_send_text_input_event
2001 int macdrv_send_text_input_event(int pressed, unsigned int flags, int repeat, int keyc, void* data)
2006 WineWindow* window = (WineWindow*)[NSApp keyWindow];
2007 if (![window isKindOfClass:[WineWindow class]])
2009 window = (WineWindow*)[NSApp mainWindow];
2010 if (![window isKindOfClass:[WineWindow class]] && [[NSApp orderedWineWindows] count])
2012 window = [[NSApp orderedWineWindows] objectAtIndex:0];
2013 if (![window isKindOfClass:[WineWindow class]])
2020 NSUInteger localFlags = flags;
2024 window.imeData = data;
2025 fix_device_modifiers_by_generic(&localFlags);
2027 // An NSEvent created with +keyEventWithType:... is internally marked
2028 // as synthetic and doesn't get sent through input methods. But one
2029 // created from a CGEvent doesn't have that problem.
2030 c = CGEventCreateKeyboardEvent(NULL, keyc, pressed);
2031 CGEventSetFlags(c, localFlags);
2032 CGEventSetIntegerValueField(c, kCGKeyboardEventAutorepeat, repeat);
2033 event = [NSEvent eventWithCGEvent:c];
2036 window.commandDone = FALSE;
2037 ret = [[[window contentView] inputContext] handleEvent:event] && !window.commandDone;