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 "cocoa_window.h"
23 #include "macdrv_cocoa.h"
25 #import "cocoa_event.h"
28 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
30 NSUInteger style_mask;
34 style_mask = NSTitledWindowMask;
35 if (wf->close_button) style_mask |= NSClosableWindowMask;
36 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
37 if (wf->resizable) style_mask |= NSResizableWindowMask;
38 if (wf->utility) style_mask |= NSUtilityWindowMask;
40 else style_mask = NSBorderlessWindowMask;
46 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
49 for (screen in screens)
51 if (NSIntersectsRect(frame, [screen frame]))
58 @interface WineContentView : NSView
62 @interface WineWindow ()
64 @property (nonatomic) BOOL disabled;
65 @property (nonatomic) BOOL noActivate;
66 @property (nonatomic) BOOL floating;
67 @property (retain, nonatomic) NSWindow* latentParentWindow;
69 @property (nonatomic) void* hwnd;
70 @property (retain, nonatomic) WineEventQueue* queue;
72 @property (nonatomic) void* surface;
73 @property (nonatomic) pthread_mutex_t* surface_mutex;
75 @property (copy, nonatomic) NSBezierPath* shape;
76 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
77 @property (readonly, nonatomic) BOOL needsTransparency;
79 @property (nonatomic) BOOL colorKeyed;
80 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
81 @property (nonatomic) BOOL usePerPixelAlpha;
83 + (void) flipRect:(NSRect*)rect;
88 @implementation WineContentView
95 - (void) drawRect:(NSRect)rect
97 WineWindow* window = (WineWindow*)[self window];
99 if (window.surface && window.surface_mutex &&
100 !pthread_mutex_lock(window.surface_mutex))
105 if (!get_surface_region_rects(window.surface, &rects, &count) || count)
110 imageRect = NSRectToCGRect(rect);
111 image = create_surface_image(window.surface, &imageRect, FALSE);
115 CGContextRef context;
119 NSBezierPath* surfaceClip = [NSBezierPath bezierPath];
121 for (i = 0; i < count; i++)
122 [surfaceClip appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
123 [surfaceClip addClip];
126 [window.shape addClip];
128 if (window.colorKeyed)
130 CGImageRef maskedImage;
131 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
132 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
133 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
134 maskedImage = CGImageCreateWithMaskingColors(image, components);
137 CGImageRelease(image);
142 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
143 CGContextSetBlendMode(context, kCGBlendModeCopy);
144 CGContextDrawImage(context, imageRect, image);
146 CGImageRelease(image);
148 if (window.shapeChangedSinceLastDraw || window.colorKeyed ||
149 window.usePerPixelAlpha)
151 window.shapeChangedSinceLastDraw = FALSE;
152 [window invalidateShadow];
157 pthread_mutex_unlock(window.surface_mutex);
164 @implementation WineWindow
166 @synthesize disabled, noActivate, floating, latentParentWindow, hwnd, queue;
167 @synthesize surface, surface_mutex;
168 @synthesize shape, shapeChangedSinceLastDraw;
169 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
170 @synthesize usePerPixelAlpha;
172 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
173 windowFrame:(NSRect)window_frame
175 queue:(WineEventQueue*)queue
178 WineContentView* contentView;
180 [self flipRect:&window_frame];
182 window = [[[self alloc] initWithContentRect:window_frame
183 styleMask:style_mask_for_features(wf)
184 backing:NSBackingStoreBuffered
185 defer:YES] autorelease];
187 if (!window) return nil;
188 window->normalStyleMask = [window styleMask];
190 /* Standardize windows to eliminate differences between titled and
191 borderless windows and between NSWindow and NSPanel. */
192 [window setHidesOnDeactivate:NO];
193 [window setReleasedWhenClosed:NO];
195 [window disableCursorRects];
196 [window setShowsResizeIndicator:NO];
197 [window setHasShadow:wf->shadow];
198 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
199 [window setDelegate:window];
201 window.queue = queue;
203 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
206 [contentView setAutoresizesSubviews:NO];
208 [window setContentView:contentView];
216 [latentParentWindow release];
221 + (void) flipRect:(NSRect*)rect
223 rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
226 - (void) adjustFeaturesForState
228 NSUInteger style = normalStyleMask;
231 style &= ~NSResizableWindowMask;
232 if (style != [self styleMask])
233 [self setStyleMask:style];
235 if (style & NSClosableWindowMask)
236 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
237 if (style & NSMiniaturizableWindowMask)
238 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
241 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
243 normalStyleMask = style_mask_for_features(wf);
244 [self adjustFeaturesForState];
245 [self setHasShadow:wf->shadow];
248 - (void) setMacDrvState:(const struct macdrv_window_state*)state
251 NSWindowCollectionBehavior behavior;
253 self.disabled = state->disabled;
254 self.noActivate = state->no_activate;
256 self.floating = state->floating;
257 level = state->floating ? NSFloatingWindowLevel : NSNormalWindowLevel;
258 if (level != [self level])
259 [self setLevel:level];
261 behavior = NSWindowCollectionBehaviorDefault;
262 if (state->excluded_by_expose)
263 behavior |= NSWindowCollectionBehaviorTransient;
265 behavior |= NSWindowCollectionBehaviorManaged;
266 if (state->excluded_by_cycle)
268 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
269 if ([self isVisible])
270 [NSApp removeWindowsItem:self];
274 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
275 if ([self isVisible])
276 [NSApp addWindowsItem:self title:[self title] filename:NO];
278 [self setCollectionBehavior:behavior];
281 /* Returns whether or not the window was ordered in, which depends on if
282 its frame intersects any screen. */
283 - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
285 BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
288 [NSApp transformProcessToForeground];
291 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
293 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
294 if (latentParentWindow)
296 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
297 self.latentParentWindow = nil;
300 if (![self isExcludedFromWindowsMenu])
301 [NSApp addWindowsItem:self title:[self title] filename:NO];
309 self.latentParentWindow = [self parentWindow];
310 [latentParentWindow removeChildWindow:self];
312 [NSApp removeWindowsItem:self];
315 - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
317 NSArray* screens = [NSScreen screens];
318 BOOL on_screen = [self isVisible];
319 NSRect frame, oldFrame;
321 if (![screens count]) return on_screen;
323 /* Origin is (left, top) in a top-down space. Need to convert it to
324 (left, bottom) in a bottom-up space. */
325 [[self class] flipRect:&contentRect];
329 on_screen = frame_intersects_screens(contentRect, screens);
334 oldFrame = [self frame];
335 frame = [self frameRectForContentRect:contentRect];
336 if (!NSEqualRects(frame, oldFrame))
338 if (NSEqualSizes(frame.size, oldFrame.size))
339 [self setFrameOrigin:frame.origin];
341 [self setFrame:frame display:YES];
347 - (void) setMacDrvParentWindow:(WineWindow*)parent
349 if ([self parentWindow] != parent)
351 [[self parentWindow] removeChildWindow:self];
352 self.latentParentWindow = nil;
353 if ([self isVisible] && parent)
354 [parent addChildWindow:self ordered:NSWindowAbove];
356 self.latentParentWindow = parent;
360 - (void) setDisabled:(BOOL)newValue
362 if (disabled != newValue)
365 [self adjustFeaturesForState];
369 - (BOOL) needsTransparency
371 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
374 - (void) checkTransparency
376 if (![self isOpaque] && !self.needsTransparency)
378 [self setBackgroundColor:[NSColor windowBackgroundColor]];
379 [self setOpaque:YES];
381 else if ([self isOpaque] && self.needsTransparency)
383 [self setBackgroundColor:[NSColor clearColor]];
388 - (void) setShape:(NSBezierPath*)newShape
390 if (shape == newShape) return;
391 if (shape && newShape && [shape isEqual:newShape]) return;
395 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
399 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
401 shape = [newShape copy];
402 self.shapeChangedSinceLastDraw = TRUE;
404 [self checkTransparency];
409 * ---------- NSWindow method overrides ----------
411 - (BOOL) canBecomeKeyWindow
413 if (self.disabled || self.noActivate) return NO;
417 - (BOOL) canBecomeMainWindow
419 return [self canBecomeKeyWindow];
422 - (BOOL) isExcludedFromWindowsMenu
424 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
427 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
429 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
430 return [self isKeyWindow] || (!self.disabled && !self.noActivate);
431 return [super validateMenuItem:menuItem];
436 * ---------- NSWindowDelegate methods ----------
438 - (BOOL)windowShouldClose:(id)sender
441 event.type = WINDOW_CLOSE_REQUESTED;
442 event.window = (macdrv_window)[self retain];
443 [queue postEvent:&event];
450 /***********************************************************************
451 * macdrv_create_cocoa_window
453 * Create a Cocoa window with the given content frame and features (e.g.
454 * title bar, close box, etc.).
456 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
457 CGRect frame, void* hwnd, macdrv_event_queue queue)
459 __block WineWindow* window;
462 window = [[WineWindow createWindowWithFeatures:wf
463 windowFrame:NSRectFromCGRect(frame)
465 queue:(WineEventQueue*)queue] retain];
468 return (macdrv_window)window;
471 /***********************************************************************
472 * macdrv_destroy_cocoa_window
474 * Destroy a Cocoa window.
476 void macdrv_destroy_cocoa_window(macdrv_window w)
478 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
479 WineWindow* window = (WineWindow*)w;
481 [window.queue discardEventsMatchingMask:-1 forWindow:window];
488 /***********************************************************************
489 * macdrv_get_window_hwnd
491 * Get the hwnd that was set for the window at creation.
493 void* macdrv_get_window_hwnd(macdrv_window w)
495 WineWindow* window = (WineWindow*)w;
499 /***********************************************************************
500 * macdrv_set_cocoa_window_features
502 * Update a Cocoa window's features.
504 void macdrv_set_cocoa_window_features(macdrv_window w,
505 const struct macdrv_window_features* wf)
507 WineWindow* window = (WineWindow*)w;
510 [window setWindowFeatures:wf];
514 /***********************************************************************
515 * macdrv_set_cocoa_window_state
517 * Update a Cocoa window's state.
519 void macdrv_set_cocoa_window_state(macdrv_window w,
520 const struct macdrv_window_state* state)
522 WineWindow* window = (WineWindow*)w;
525 [window setMacDrvState:state];
529 /***********************************************************************
530 * macdrv_set_cocoa_window_title
532 * Set a Cocoa window's title.
534 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
537 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
538 WineWindow* window = (WineWindow*)w;
539 NSString* titleString;
542 titleString = [NSString stringWithCharacters:title length:length];
546 [window setTitle:titleString];
547 if ([window isVisible] && ![window isExcludedFromWindowsMenu])
548 [NSApp changeWindowsItem:window title:titleString filename:NO];
554 /***********************************************************************
555 * macdrv_order_cocoa_window
557 * Reorder a Cocoa window relative to other windows. If prev is
558 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
559 * it is ordered above that window. Otherwise, it is ordered to the
562 * Returns true if the window has actually been ordered onto the screen
563 * (i.e. if its frame intersects with a screen). Otherwise, false.
565 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
568 WineWindow* window = (WineWindow*)w;
569 __block BOOL on_screen;
572 on_screen = [window orderBelow:(WineWindow*)prev
573 orAbove:(WineWindow*)next];
579 /***********************************************************************
580 * macdrv_hide_cocoa_window
582 * Hides a Cocoa window.
584 void macdrv_hide_cocoa_window(macdrv_window w)
586 WineWindow* window = (WineWindow*)w;
593 /***********************************************************************
594 * macdrv_set_cocoa_window_frame
596 * Move a Cocoa window. If the window has been moved out of the bounds
597 * of the desktop, it is ordered out. (This routine won't ever order a
598 * window in, though.)
600 * Returns true if the window is on screen; false otherwise.
602 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
604 WineWindow* window = (WineWindow*)w;
605 __block BOOL on_screen;
608 on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
614 /***********************************************************************
615 * macdrv_set_cocoa_parent_window
617 * Sets the parent window for a Cocoa window. If parent is NULL, clears
620 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
622 WineWindow* window = (WineWindow*)w;
625 [window setMacDrvParentWindow:(WineWindow*)parent];
629 /***********************************************************************
630 * macdrv_set_window_surface
632 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
634 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
635 WineWindow* window = (WineWindow*)w;
638 window.surface = surface;
639 window.surface_mutex = mutex;
645 /***********************************************************************
646 * macdrv_window_needs_display
648 * Mark a window as needing display in a specified rect (in non-client
651 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
653 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
654 WineWindow* window = (WineWindow*)w;
657 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
663 /***********************************************************************
664 * macdrv_set_window_shape
666 * Sets the shape of a Cocoa window from an array of rectangles. If
667 * rects is NULL, resets the window's shape to its frame.
669 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
671 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
672 WineWindow* window = (WineWindow*)w;
675 if (!rects || !count)
682 path = [NSBezierPath bezierPath];
683 for (i = 0; i < count; i++)
684 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
692 /***********************************************************************
693 * macdrv_set_window_alpha
695 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
697 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
698 WineWindow* window = (WineWindow*)w;
700 [window setAlphaValue:alpha];
705 /***********************************************************************
706 * macdrv_set_window_color_key
708 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
711 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
712 WineWindow* window = (WineWindow*)w;
715 window.colorKeyed = TRUE;
716 window.colorKeyRed = keyRed;
717 window.colorKeyGreen = keyGreen;
718 window.colorKeyBlue = keyBlue;
719 [window checkTransparency];
725 /***********************************************************************
726 * macdrv_clear_window_color_key
728 void macdrv_clear_window_color_key(macdrv_window w)
730 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
731 WineWindow* window = (WineWindow*)w;
734 window.colorKeyed = FALSE;
735 [window checkTransparency];
741 /***********************************************************************
742 * macdrv_window_use_per_pixel_alpha
744 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
746 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
747 WineWindow* window = (WineWindow*)w;
750 window.usePerPixelAlpha = use_per_pixel_alpha;
751 [window checkTransparency];