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"
30 /* Additional Mac virtual keycode, to complement those in Carbon's <HIToolbox/Events.h>. */
32 kVK_RightCommand = 0x36, /* Invented for Wine; was unused */
36 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
38 NSUInteger style_mask;
42 style_mask = NSTitledWindowMask;
43 if (wf->close_button) style_mask |= NSClosableWindowMask;
44 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
45 if (wf->resizable) style_mask |= NSResizableWindowMask;
46 if (wf->utility) style_mask |= NSUtilityWindowMask;
48 else style_mask = NSBorderlessWindowMask;
54 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
57 for (screen in screens)
59 if (NSIntersectsRect(frame, [screen frame]))
66 /* We rely on the supposedly device-dependent modifier flags to distinguish the
67 keys on the left side of the keyboard from those on the right. Some event
68 sources don't set those device-depdendent flags. If we see a device-independent
69 flag for a modifier without either corresponding device-dependent flag, assume
71 static inline void fix_device_modifiers_by_generic(NSUInteger* modifiers)
73 if ((*modifiers & (NX_COMMANDMASK | NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK)) == NX_COMMANDMASK)
74 *modifiers |= NX_DEVICELCMDKEYMASK;
75 if ((*modifiers & (NX_SHIFTMASK | NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK)) == NX_SHIFTMASK)
76 *modifiers |= NX_DEVICELSHIFTKEYMASK;
77 if ((*modifiers & (NX_CONTROLMASK | NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK)) == NX_CONTROLMASK)
78 *modifiers |= NX_DEVICELCTLKEYMASK;
79 if ((*modifiers & (NX_ALTERNATEMASK | NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK)) == NX_ALTERNATEMASK)
80 *modifiers |= NX_DEVICELALTKEYMASK;
83 /* As we manipulate individual bits of a modifier mask, we can end up with
84 inconsistent sets of flags. In particular, we might set or clear one of the
85 left/right-specific bits, but not the corresponding non-side-specific bit.
86 Fix that. If either side-specific bit is set, set the non-side-specific bit,
87 otherwise clear it. */
88 static inline void fix_generic_modifiers_by_device(NSUInteger* modifiers)
90 if (*modifiers & (NX_DEVICELCMDKEYMASK | NX_DEVICERCMDKEYMASK))
91 *modifiers |= NX_COMMANDMASK;
93 *modifiers &= ~NX_COMMANDMASK;
94 if (*modifiers & (NX_DEVICELSHIFTKEYMASK | NX_DEVICERSHIFTKEYMASK))
95 *modifiers |= NX_SHIFTMASK;
97 *modifiers &= ~NX_SHIFTMASK;
98 if (*modifiers & (NX_DEVICELCTLKEYMASK | NX_DEVICERCTLKEYMASK))
99 *modifiers |= NX_CONTROLMASK;
101 *modifiers &= ~NX_CONTROLMASK;
102 if (*modifiers & (NX_DEVICELALTKEYMASK | NX_DEVICERALTKEYMASK))
103 *modifiers |= NX_ALTERNATEMASK;
105 *modifiers &= ~NX_ALTERNATEMASK;
109 @interface WineContentView : NSView
113 @interface WineWindow ()
115 @property (nonatomic) BOOL disabled;
116 @property (nonatomic) BOOL noActivate;
117 @property (nonatomic) BOOL floating;
118 @property (retain, nonatomic) NSWindow* latentParentWindow;
120 @property (nonatomic) void* hwnd;
121 @property (retain, readwrite, nonatomic) WineEventQueue* queue;
123 @property (nonatomic) void* surface;
124 @property (nonatomic) pthread_mutex_t* surface_mutex;
126 @property (copy, nonatomic) NSBezierPath* shape;
127 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
128 @property (readonly, nonatomic) BOOL needsTransparency;
130 @property (nonatomic) BOOL colorKeyed;
131 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
132 @property (nonatomic) BOOL usePerPixelAlpha;
134 + (void) flipRect:(NSRect*)rect;
139 @implementation WineContentView
146 - (void) drawRect:(NSRect)rect
148 WineWindow* window = (WineWindow*)[self window];
150 if (window.surface && window.surface_mutex &&
151 !pthread_mutex_lock(window.surface_mutex))
156 if (!get_surface_region_rects(window.surface, &rects, &count) || count)
161 imageRect = NSRectToCGRect(rect);
162 image = create_surface_image(window.surface, &imageRect, FALSE);
166 CGContextRef context;
170 NSBezierPath* surfaceClip = [NSBezierPath bezierPath];
172 for (i = 0; i < count; i++)
173 [surfaceClip appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
174 [surfaceClip addClip];
177 [window.shape addClip];
179 if (window.colorKeyed)
181 CGImageRef maskedImage;
182 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
183 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
184 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
185 maskedImage = CGImageCreateWithMaskingColors(image, components);
188 CGImageRelease(image);
193 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
194 CGContextSetBlendMode(context, kCGBlendModeCopy);
195 CGContextDrawImage(context, imageRect, image);
197 CGImageRelease(image);
199 if (window.shapeChangedSinceLastDraw || window.colorKeyed ||
200 window.usePerPixelAlpha)
202 window.shapeChangedSinceLastDraw = FALSE;
203 [window invalidateShadow];
208 pthread_mutex_unlock(window.surface_mutex);
212 /* By default, NSView will swallow right-clicks in an attempt to support contextual
213 menus. We need to bypass that and allow the event to make it to the window. */
214 - (void) rightMouseDown:(NSEvent*)theEvent
216 [[self window] rightMouseDown:theEvent];
219 - (BOOL) acceptsFirstMouse:(NSEvent*)theEvent
227 @implementation WineWindow
229 @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
230 @synthesize surface, surface_mutex;
231 @synthesize shape, shapeChangedSinceLastDraw;
232 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
233 @synthesize usePerPixelAlpha;
235 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
236 windowFrame:(NSRect)window_frame
238 queue:(WineEventQueue*)queue
241 WineContentView* contentView;
242 NSTrackingArea* trackingArea;
244 [self flipRect:&window_frame];
246 window = [[[self alloc] initWithContentRect:window_frame
247 styleMask:style_mask_for_features(wf)
248 backing:NSBackingStoreBuffered
249 defer:YES] autorelease];
251 if (!window) return nil;
252 window->normalStyleMask = [window styleMask];
253 window->forceNextMouseMoveAbsolute = TRUE;
255 /* Standardize windows to eliminate differences between titled and
256 borderless windows and between NSWindow and NSPanel. */
257 [window setHidesOnDeactivate:NO];
258 [window setReleasedWhenClosed:NO];
260 [window disableCursorRects];
261 [window setShowsResizeIndicator:NO];
262 [window setHasShadow:wf->shadow];
263 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
264 [window setDelegate:window];
266 window.queue = queue;
268 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
271 [contentView setAutoresizesSubviews:NO];
273 trackingArea = [[[NSTrackingArea alloc] initWithRect:[contentView bounds]
274 options:(NSTrackingMouseEnteredAndExited |
275 NSTrackingMouseMoved |
276 NSTrackingActiveAlways |
277 NSTrackingInVisibleRect)
279 userInfo:nil] autorelease];
282 [contentView addTrackingArea:trackingArea];
284 [window setContentView:contentView];
292 [latentParentWindow release];
297 + (void) flipRect:(NSRect*)rect
299 rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
302 - (void) adjustFeaturesForState
304 NSUInteger style = normalStyleMask;
307 style &= ~NSResizableWindowMask;
308 if (style != [self styleMask])
309 [self setStyleMask:style];
311 if (style & NSClosableWindowMask)
312 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
313 if (style & NSMiniaturizableWindowMask)
314 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
317 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
319 normalStyleMask = style_mask_for_features(wf);
320 [self adjustFeaturesForState];
321 [self setHasShadow:wf->shadow];
324 - (void) setMacDrvState:(const struct macdrv_window_state*)state
327 NSWindowCollectionBehavior behavior;
329 self.disabled = state->disabled;
330 self.noActivate = state->no_activate;
332 self.floating = state->floating;
333 level = state->floating ? NSFloatingWindowLevel : NSNormalWindowLevel;
334 if (level != [self level])
335 [self setLevel:level];
337 behavior = NSWindowCollectionBehaviorDefault;
338 if (state->excluded_by_expose)
339 behavior |= NSWindowCollectionBehaviorTransient;
341 behavior |= NSWindowCollectionBehaviorManaged;
342 if (state->excluded_by_cycle)
344 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
345 if ([self isVisible])
346 [NSApp removeWindowsItem:self];
350 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
351 if ([self isVisible])
352 [NSApp addWindowsItem:self title:[self title] filename:NO];
354 [self setCollectionBehavior:behavior];
356 if (state->minimized && ![self isMiniaturized])
358 ignore_windowMiniaturize = TRUE;
359 [self miniaturize:nil];
361 else if (!state->minimized && [self isMiniaturized])
363 ignore_windowDeminiaturize = TRUE;
364 [self deminiaturize:nil];
367 /* Whatever events regarding minimization might have been in the queue are now stale. */
368 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
369 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
373 /* Returns whether or not the window was ordered in, which depends on if
374 its frame intersects any screen. */
375 - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
377 BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
380 [NSApp transformProcessToForeground];
383 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
385 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
386 if (latentParentWindow)
388 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
389 self.latentParentWindow = nil;
392 /* Cocoa may adjust the frame when the window is ordered onto the screen.
393 Generate a frame-changed event just in case. The back end will ignore
394 it if nothing actually changed. */
395 [self windowDidResize:nil];
397 if (![self isExcludedFromWindowsMenu])
398 [NSApp addWindowsItem:self title:[self title] filename:NO];
406 self.latentParentWindow = [self parentWindow];
407 [latentParentWindow removeChildWindow:self];
408 forceNextMouseMoveAbsolute = TRUE;
410 [NSApp removeWindowsItem:self];
413 - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
415 NSArray* screens = [NSScreen screens];
416 BOOL on_screen = [self isVisible];
417 NSRect frame, oldFrame;
419 if (![screens count]) return on_screen;
421 /* Origin is (left, top) in a top-down space. Need to convert it to
422 (left, bottom) in a bottom-up space. */
423 [[self class] flipRect:&contentRect];
427 on_screen = frame_intersects_screens(contentRect, screens);
432 oldFrame = [self frame];
433 frame = [self frameRectForContentRect:contentRect];
434 if (!NSEqualRects(frame, oldFrame))
436 if (NSEqualSizes(frame.size, oldFrame.size))
437 [self setFrameOrigin:frame.origin];
439 [self setFrame:frame display:YES];
444 /* In case Cocoa adjusted the frame we tried to set, generate a frame-changed
445 event. The back end will ignore it if nothing actually changed. */
446 [self windowDidResize:nil];
452 - (void) setMacDrvParentWindow:(WineWindow*)parent
454 if ([self parentWindow] != parent)
456 [[self parentWindow] removeChildWindow:self];
457 self.latentParentWindow = nil;
458 if ([self isVisible] && parent)
459 [parent addChildWindow:self ordered:NSWindowAbove];
461 self.latentParentWindow = parent;
465 - (void) setDisabled:(BOOL)newValue
467 if (disabled != newValue)
470 [self adjustFeaturesForState];
474 - (BOOL) needsTransparency
476 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
479 - (void) checkTransparency
481 if (![self isOpaque] && !self.needsTransparency)
483 [self setBackgroundColor:[NSColor windowBackgroundColor]];
484 [self setOpaque:YES];
486 else if ([self isOpaque] && self.needsTransparency)
488 [self setBackgroundColor:[NSColor clearColor]];
493 - (void) setShape:(NSBezierPath*)newShape
495 if (shape == newShape) return;
496 if (shape && newShape && [shape isEqual:newShape]) return;
500 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
504 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
506 shape = [newShape copy];
507 self.shapeChangedSinceLastDraw = TRUE;
509 [self checkTransparency];
512 - (void) postMouseButtonEvent:(NSEvent *)theEvent pressed:(int)pressed
514 CGPoint pt = CGEventGetLocation([theEvent CGEvent]);
517 event.type = MOUSE_BUTTON;
518 event.window = (macdrv_window)[self retain];
519 event.mouse_button.button = [theEvent buttonNumber];
520 event.mouse_button.pressed = pressed;
521 event.mouse_button.x = pt.x;
522 event.mouse_button.y = pt.y;
523 event.mouse_button.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
525 [queue postEvent:&event];
532 [NSApp transformProcessToForeground];
534 /* If a borderless window is offscreen, orderFront: won't move
535 it onscreen like it would for a titled window. Do that ourselves. */
536 screens = [NSScreen screens];
537 if (!([self styleMask] & NSTitledWindowMask) && ![self isVisible] &&
538 !frame_intersects_screens([self frame], screens))
540 NSScreen* primaryScreen = [screens objectAtIndex:0];
541 NSRect frame = [primaryScreen frame];
542 [self setFrameTopLeftPoint:NSMakePoint(NSMinX(frame), NSMaxY(frame))];
543 frame = [self constrainFrameRect:[self frame] toScreen:primaryScreen];
544 [self setFrame:frame display:YES];
547 [self orderFront:nil];
548 causing_becomeKeyWindow = TRUE;
549 [self makeKeyWindow];
550 causing_becomeKeyWindow = FALSE;
551 if (latentParentWindow)
553 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
554 self.latentParentWindow = nil;
556 if (![self isExcludedFromWindowsMenu])
557 [NSApp addWindowsItem:self title:[self title] filename:NO];
559 /* Cocoa may adjust the frame when the window is ordered onto the screen.
560 Generate a frame-changed event just in case. The back end will ignore
561 it if nothing actually changed. */
562 [self windowDidResize:nil];
565 - (void) postKey:(uint16_t)keyCode
566 pressed:(BOOL)pressed
567 modifiers:(NSUInteger)modifiers
568 event:(NSEvent*)theEvent
572 WineApplication* app = (WineApplication*)NSApp;
574 event.type = pressed ? KEY_PRESS : KEY_RELEASE;
575 event.window = (macdrv_window)[self retain];
576 event.key.keycode = keyCode;
577 event.key.modifiers = modifiers;
578 event.key.time_ms = [app ticksForEventTime:[theEvent timestamp]];
580 if ((cgevent = [theEvent CGEvent]))
582 CGEventSourceKeyboardType keyboardType = CGEventGetIntegerValueField(cgevent,
583 kCGKeyboardEventKeyboardType);
584 if (keyboardType != app.keyboardType)
586 app.keyboardType = keyboardType;
587 [app keyboardSelectionDidChange];
591 [queue postEvent:&event];
594 - (void) postKeyEvent:(NSEvent *)theEvent
596 [self flagsChanged:theEvent];
597 [self postKey:[theEvent keyCode]
598 pressed:[theEvent type] == NSKeyDown
599 modifiers:[theEvent modifierFlags]
603 - (void) postMouseMovedEvent:(NSEvent *)theEvent
607 if (forceNextMouseMoveAbsolute)
609 CGPoint point = CGEventGetLocation([theEvent CGEvent]);
611 event.type = MOUSE_MOVED_ABSOLUTE;
612 event.mouse_moved.x = point.x;
613 event.mouse_moved.y = point.y;
618 forceNextMouseMoveAbsolute = FALSE;
622 /* Add event delta to accumulated delta error */
623 /* deltaY is already flipped */
624 mouseMoveDeltaX += [theEvent deltaX];
625 mouseMoveDeltaY += [theEvent deltaY];
627 event.type = MOUSE_MOVED;
628 event.mouse_moved.x = mouseMoveDeltaX;
629 event.mouse_moved.y = mouseMoveDeltaY;
631 /* Keep the remainder after integer truncation. */
632 mouseMoveDeltaX -= event.mouse_moved.x;
633 mouseMoveDeltaY -= event.mouse_moved.y;
636 if (event.type == MOUSE_MOVED_ABSOLUTE || event.mouse_moved.x || event.mouse_moved.y)
638 event.window = (macdrv_window)[self retain];
639 event.mouse_moved.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
641 [queue postEvent:&event];
647 * ---------- NSWindow method overrides ----------
649 - (BOOL) canBecomeKeyWindow
651 if (causing_becomeKeyWindow) return YES;
652 if (self.disabled || self.noActivate) return NO;
653 return [self isKeyWindow];
656 - (BOOL) canBecomeMainWindow
658 return [self canBecomeKeyWindow];
661 - (BOOL) isExcludedFromWindowsMenu
663 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
666 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
668 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
669 return [self isKeyWindow] || (!self.disabled && !self.noActivate);
670 return [super validateMenuItem:menuItem];
673 /* We don't call this. It's the action method of the items in the Window menu. */
674 - (void) makeKeyAndOrderFront:(id)sender
676 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
677 [NSApp windowGotFocus:self];
680 - (void) sendEvent:(NSEvent*)event
682 /* NSWindow consumes certain key-down events as part of Cocoa's keyboard
683 interface control. For example, Control-Tab switches focus among
684 views. We want to bypass that feature, so directly route key-down
685 events to -keyDown:. */
686 if ([event type] == NSKeyDown)
687 [[self firstResponder] keyDown:event];
690 if ([event type] == NSLeftMouseDown)
692 /* Since our windows generally claim they can't be made key, clicks
693 in their title bars are swallowed by the theme frame stuff. So,
694 we hook directly into the event stream and assume that any click
695 in the window will activate it, if Wine and the Win32 program
697 if (![self isKeyWindow] && !self.disabled && !self.noActivate)
698 [NSApp windowGotFocus:self];
701 [super sendEvent:event];
707 * ---------- NSResponder method overrides ----------
709 - (void) mouseDown:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:1]; }
710 - (void) rightMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
711 - (void) otherMouseDown:(NSEvent *)theEvent { [self mouseDown:theEvent]; }
713 - (void) mouseUp:(NSEvent *)theEvent { [self postMouseButtonEvent:theEvent pressed:0]; }
714 - (void) rightMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
715 - (void) otherMouseUp:(NSEvent *)theEvent { [self mouseUp:theEvent]; }
717 - (void) keyDown:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
718 - (void) keyUp:(NSEvent *)theEvent { [self postKeyEvent:theEvent]; }
720 - (void) flagsChanged:(NSEvent *)theEvent
722 static const struct {
726 { NX_ALPHASHIFTMASK, kVK_CapsLock },
727 { NX_DEVICELSHIFTKEYMASK, kVK_Shift },
728 { NX_DEVICERSHIFTKEYMASK, kVK_RightShift },
729 { NX_DEVICELCTLKEYMASK, kVK_Control },
730 { NX_DEVICERCTLKEYMASK, kVK_RightControl },
731 { NX_DEVICELALTKEYMASK, kVK_Option },
732 { NX_DEVICERALTKEYMASK, kVK_RightOption },
733 { NX_DEVICELCMDKEYMASK, kVK_Command },
734 { NX_DEVICERCMDKEYMASK, kVK_RightCommand },
737 NSUInteger modifierFlags = [theEvent modifierFlags];
741 fix_device_modifiers_by_generic(&modifierFlags);
742 changed = modifierFlags ^ lastModifierFlags;
745 for (i = 0; i < sizeof(modifiers)/sizeof(modifiers[0]); i++)
746 if (changed & modifiers[i].mask)
749 for (i = 0; i <= last_changed; i++)
751 if (changed & modifiers[i].mask)
753 BOOL pressed = (modifierFlags & modifiers[i].mask) != 0;
755 if (i == last_changed)
756 lastModifierFlags = modifierFlags;
759 lastModifierFlags ^= modifiers[i].mask;
760 fix_generic_modifiers_by_device(&lastModifierFlags);
763 // Caps lock generates one event for each press-release action.
764 // We need to simulate a pair of events for each actual event.
765 if (modifiers[i].mask == NX_ALPHASHIFTMASK)
767 [self postKey:modifiers[i].keycode
769 modifiers:lastModifierFlags
770 event:(NSEvent*)theEvent];
774 [self postKey:modifiers[i].keycode
776 modifiers:lastModifierFlags
777 event:(NSEvent*)theEvent];
782 - (void) mouseEntered:(NSEvent *)theEvent { forceNextMouseMoveAbsolute = TRUE; }
783 - (void) mouseExited:(NSEvent *)theEvent { forceNextMouseMoveAbsolute = TRUE; }
785 - (void) mouseMoved:(NSEvent *)theEvent { [self postMouseMovedEvent:theEvent]; }
786 - (void) mouseDragged:(NSEvent *)theEvent { [self postMouseMovedEvent:theEvent]; }
787 - (void) rightMouseDragged:(NSEvent *)theEvent { [self postMouseMovedEvent:theEvent]; }
788 - (void) otherMouseDragged:(NSEvent *)theEvent { [self postMouseMovedEvent:theEvent]; }
790 - (void) scrollWheel:(NSEvent *)theEvent
796 BOOL continuous = FALSE;
798 cgevent = [theEvent CGEvent];
799 pt = CGEventGetLocation(cgevent);
801 event.type = MOUSE_SCROLL;
802 event.window = (macdrv_window)[self retain];
803 event.mouse_scroll.x = pt.x;
804 event.mouse_scroll.y = pt.y;
805 event.mouse_scroll.time_ms = [NSApp ticksForEventTime:[theEvent timestamp]];
807 if (CGEventGetIntegerValueField(cgevent, kCGScrollWheelEventIsContinuous))
811 /* Continuous scroll wheel events come from high-precision scrolling
812 hardware like Apple's Magic Mouse, Mighty Mouse, and trackpads.
813 For these, we can get more precise data from the CGEvent API. */
814 /* Axis 1 is vertical, axis 2 is horizontal. */
815 x = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis2);
816 y = CGEventGetDoubleValueField(cgevent, kCGScrollWheelEventPointDeltaAxis1);
820 double pixelsPerLine = 10;
821 CGEventSourceRef source;
823 /* The non-continuous values are in units of "lines", not pixels. */
824 if ((source = CGEventCreateSourceFromEvent(cgevent)))
826 pixelsPerLine = CGEventSourceGetPixelsPerLine(source);
830 x = pixelsPerLine * [theEvent deltaX];
831 y = pixelsPerLine * [theEvent deltaY];
834 /* Mac: negative is right or down, positive is left or up.
835 Win32: negative is left or down, positive is right or up.
836 So, negate the X scroll value to translate. */
839 /* The x,y values so far are in pixels. Win32 expects to receive some
840 fraction of WHEEL_DELTA == 120. By my estimation, that's roughly
841 6 times the pixel value. */
842 event.mouse_scroll.x_scroll = 6 * x;
843 event.mouse_scroll.y_scroll = 6 * y;
847 /* For non-continuous "clicky" wheels, if there was any motion, make
848 sure there was at least WHEEL_DELTA motion. This is so, at slow
849 speeds where the system's acceleration curve is actually reducing the
850 scroll distance, the user is sure to get some action out of each click.
851 For example, this is important for rotating though weapons in a
852 first-person shooter. */
853 if (0 < event.mouse_scroll.x_scroll && event.mouse_scroll.x_scroll < 120)
854 event.mouse_scroll.x_scroll = 120;
855 else if (-120 < event.mouse_scroll.x_scroll && event.mouse_scroll.x_scroll < 0)
856 event.mouse_scroll.x_scroll = -120;
858 if (0 < event.mouse_scroll.y_scroll && event.mouse_scroll.y_scroll < 120)
859 event.mouse_scroll.y_scroll = 120;
860 else if (-120 < event.mouse_scroll.y_scroll && event.mouse_scroll.y_scroll < 0)
861 event.mouse_scroll.y_scroll = -120;
864 if (event.mouse_scroll.x_scroll || event.mouse_scroll.y_scroll)
865 [queue postEvent:&event];
870 * ---------- NSWindowDelegate methods ----------
872 - (void)windowDidBecomeKey:(NSNotification *)notification
874 NSEvent* event = [NSApp lastFlagsChanged];
876 [self flagsChanged:event];
878 if (causing_becomeKeyWindow) return;
880 [NSApp windowGotFocus:self];
883 - (void)windowDidDeminiaturize:(NSNotification *)notification
885 if (!ignore_windowDeminiaturize)
889 /* Coalesce events by discarding any previous ones still in the queue. */
890 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
891 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
894 event.type = WINDOW_DID_UNMINIMIZE;
895 event.window = (macdrv_window)[self retain];
896 [queue postEvent:&event];
899 ignore_windowDeminiaturize = FALSE;
902 - (void)windowDidMove:(NSNotification *)notification
904 [self windowDidResize:notification];
907 - (void)windowDidResignKey:(NSNotification *)notification
911 if (causing_becomeKeyWindow) return;
913 event.type = WINDOW_LOST_FOCUS;
914 event.window = (macdrv_window)[self retain];
915 [queue postEvent:&event];
918 - (void)windowDidResize:(NSNotification *)notification
921 NSRect frame = [self contentRectForFrameRect:[self frame]];
923 [[self class] flipRect:&frame];
925 /* Coalesce events by discarding any previous ones still in the queue. */
926 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_FRAME_CHANGED)
929 event.type = WINDOW_FRAME_CHANGED;
930 event.window = (macdrv_window)[self retain];
931 event.window_frame_changed.frame = NSRectToCGRect(frame);
932 [queue postEvent:&event];
935 - (BOOL)windowShouldClose:(id)sender
938 event.type = WINDOW_CLOSE_REQUESTED;
939 event.window = (macdrv_window)[self retain];
940 [queue postEvent:&event];
944 - (void)windowWillMiniaturize:(NSNotification *)notification
946 if (!ignore_windowMiniaturize)
950 /* Coalesce events by discarding any previous ones still in the queue. */
951 [queue discardEventsMatchingMask:event_mask_for_type(WINDOW_DID_MINIMIZE) |
952 event_mask_for_type(WINDOW_DID_UNMINIMIZE)
955 event.type = WINDOW_DID_MINIMIZE;
956 event.window = (macdrv_window)[self retain];
957 [queue postEvent:&event];
960 ignore_windowMiniaturize = FALSE;
966 /***********************************************************************
967 * macdrv_create_cocoa_window
969 * Create a Cocoa window with the given content frame and features (e.g.
970 * title bar, close box, etc.).
972 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
973 CGRect frame, void* hwnd, macdrv_event_queue queue)
975 __block WineWindow* window;
978 window = [[WineWindow createWindowWithFeatures:wf
979 windowFrame:NSRectFromCGRect(frame)
981 queue:(WineEventQueue*)queue] retain];
984 return (macdrv_window)window;
987 /***********************************************************************
988 * macdrv_destroy_cocoa_window
990 * Destroy a Cocoa window.
992 void macdrv_destroy_cocoa_window(macdrv_window w)
994 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
995 WineWindow* window = (WineWindow*)w;
997 [window.queue discardEventsMatchingMask:-1 forWindow:window];
1004 /***********************************************************************
1005 * macdrv_get_window_hwnd
1007 * Get the hwnd that was set for the window at creation.
1009 void* macdrv_get_window_hwnd(macdrv_window w)
1011 WineWindow* window = (WineWindow*)w;
1015 /***********************************************************************
1016 * macdrv_set_cocoa_window_features
1018 * Update a Cocoa window's features.
1020 void macdrv_set_cocoa_window_features(macdrv_window w,
1021 const struct macdrv_window_features* wf)
1023 WineWindow* window = (WineWindow*)w;
1026 [window setWindowFeatures:wf];
1030 /***********************************************************************
1031 * macdrv_set_cocoa_window_state
1033 * Update a Cocoa window's state.
1035 void macdrv_set_cocoa_window_state(macdrv_window w,
1036 const struct macdrv_window_state* state)
1038 WineWindow* window = (WineWindow*)w;
1041 [window setMacDrvState:state];
1045 /***********************************************************************
1046 * macdrv_set_cocoa_window_title
1048 * Set a Cocoa window's title.
1050 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
1053 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1054 WineWindow* window = (WineWindow*)w;
1055 NSString* titleString;
1058 titleString = [NSString stringWithCharacters:title length:length];
1061 OnMainThreadAsync(^{
1062 [window setTitle:titleString];
1063 if ([window isVisible] && ![window isExcludedFromWindowsMenu])
1064 [NSApp changeWindowsItem:window title:titleString filename:NO];
1070 /***********************************************************************
1071 * macdrv_order_cocoa_window
1073 * Reorder a Cocoa window relative to other windows. If prev is
1074 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
1075 * it is ordered above that window. Otherwise, it is ordered to the
1078 * Returns true if the window has actually been ordered onto the screen
1079 * (i.e. if its frame intersects with a screen). Otherwise, false.
1081 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
1084 WineWindow* window = (WineWindow*)w;
1085 __block BOOL on_screen;
1088 on_screen = [window orderBelow:(WineWindow*)prev
1089 orAbove:(WineWindow*)next];
1095 /***********************************************************************
1096 * macdrv_hide_cocoa_window
1098 * Hides a Cocoa window.
1100 void macdrv_hide_cocoa_window(macdrv_window w)
1102 WineWindow* window = (WineWindow*)w;
1105 [window doOrderOut];
1109 /***********************************************************************
1110 * macdrv_set_cocoa_window_frame
1112 * Move a Cocoa window. If the window has been moved out of the bounds
1113 * of the desktop, it is ordered out. (This routine won't ever order a
1114 * window in, though.)
1116 * Returns true if the window is on screen; false otherwise.
1118 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
1120 WineWindow* window = (WineWindow*)w;
1121 __block BOOL on_screen;
1124 on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
1130 /***********************************************************************
1131 * macdrv_get_cocoa_window_frame
1133 * Gets the frame of a Cocoa window.
1135 void macdrv_get_cocoa_window_frame(macdrv_window w, CGRect* out_frame)
1137 WineWindow* window = (WineWindow*)w;
1142 frame = [window contentRectForFrameRect:[window frame]];
1143 [[window class] flipRect:&frame];
1144 *out_frame = NSRectToCGRect(frame);
1148 /***********************************************************************
1149 * macdrv_set_cocoa_parent_window
1151 * Sets the parent window for a Cocoa window. If parent is NULL, clears
1152 * the parent window.
1154 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
1156 WineWindow* window = (WineWindow*)w;
1159 [window setMacDrvParentWindow:(WineWindow*)parent];
1163 /***********************************************************************
1164 * macdrv_set_window_surface
1166 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
1168 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1169 WineWindow* window = (WineWindow*)w;
1172 window.surface = surface;
1173 window.surface_mutex = mutex;
1179 /***********************************************************************
1180 * macdrv_window_needs_display
1182 * Mark a window as needing display in a specified rect (in non-client
1183 * area coordinates).
1185 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
1187 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1188 WineWindow* window = (WineWindow*)w;
1190 OnMainThreadAsync(^{
1191 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
1197 /***********************************************************************
1198 * macdrv_set_window_shape
1200 * Sets the shape of a Cocoa window from an array of rectangles. If
1201 * rects is NULL, resets the window's shape to its frame.
1203 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
1205 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1206 WineWindow* window = (WineWindow*)w;
1209 if (!rects || !count)
1216 path = [NSBezierPath bezierPath];
1217 for (i = 0; i < count; i++)
1218 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
1219 window.shape = path;
1226 /***********************************************************************
1227 * macdrv_set_window_alpha
1229 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
1231 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1232 WineWindow* window = (WineWindow*)w;
1234 [window setAlphaValue:alpha];
1239 /***********************************************************************
1240 * macdrv_set_window_color_key
1242 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
1245 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1246 WineWindow* window = (WineWindow*)w;
1249 window.colorKeyed = TRUE;
1250 window.colorKeyRed = keyRed;
1251 window.colorKeyGreen = keyGreen;
1252 window.colorKeyBlue = keyBlue;
1253 [window checkTransparency];
1259 /***********************************************************************
1260 * macdrv_clear_window_color_key
1262 void macdrv_clear_window_color_key(macdrv_window w)
1264 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1265 WineWindow* window = (WineWindow*)w;
1268 window.colorKeyed = FALSE;
1269 [window checkTransparency];
1275 /***********************************************************************
1276 * macdrv_window_use_per_pixel_alpha
1278 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
1280 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
1281 WineWindow* window = (WineWindow*)w;
1284 window.usePerPixelAlpha = use_per_pixel_alpha;
1285 [window checkTransparency];
1291 /***********************************************************************
1292 * macdrv_give_cocoa_window_focus
1294 * Makes the Cocoa window "key" (gives it keyboard focus). This also
1295 * orders it front and, if its frame was not within the desktop bounds,
1296 * Cocoa will typically move it on-screen.
1298 void macdrv_give_cocoa_window_focus(macdrv_window w)
1300 WineWindow* window = (WineWindow*)w;
1303 [window makeFocused];