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"
27 static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf)
29 NSUInteger style_mask;
33 style_mask = NSTitledWindowMask;
34 if (wf->close_button) style_mask |= NSClosableWindowMask;
35 if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask;
36 if (wf->resizable) style_mask |= NSResizableWindowMask;
37 if (wf->utility) style_mask |= NSUtilityWindowMask;
39 else style_mask = NSBorderlessWindowMask;
45 static BOOL frame_intersects_screens(NSRect frame, NSArray* screens)
48 for (screen in screens)
50 if (NSIntersectsRect(frame, [screen frame]))
57 @interface WineContentView : NSView
61 @interface WineWindow ()
63 @property (nonatomic) BOOL disabled;
64 @property (nonatomic) BOOL noActivate;
65 @property (nonatomic) BOOL floating;
66 @property (retain, nonatomic) NSWindow* latentParentWindow;
68 @property (nonatomic) void* surface;
69 @property (nonatomic) pthread_mutex_t* surface_mutex;
71 @property (copy, nonatomic) NSBezierPath* shape;
72 @property (nonatomic) BOOL shapeChangedSinceLastDraw;
73 @property (readonly, nonatomic) BOOL needsTransparency;
75 @property (nonatomic) BOOL colorKeyed;
76 @property (nonatomic) CGFloat colorKeyRed, colorKeyGreen, colorKeyBlue;
77 @property (nonatomic) BOOL usePerPixelAlpha;
79 + (void) flipRect:(NSRect*)rect;
84 @implementation WineContentView
91 - (void) drawRect:(NSRect)rect
93 WineWindow* window = (WineWindow*)[self window];
95 if (window.surface && window.surface_mutex &&
96 !pthread_mutex_lock(window.surface_mutex))
101 if (!get_surface_region_rects(window.surface, &rects, &count) || count)
106 imageRect = NSRectToCGRect(rect);
107 image = create_surface_image(window.surface, &imageRect, FALSE);
111 CGContextRef context;
115 NSBezierPath* surfaceClip = [NSBezierPath bezierPath];
117 for (i = 0; i < count; i++)
118 [surfaceClip appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
119 [surfaceClip addClip];
122 [window.shape addClip];
124 if (window.colorKeyed)
126 CGImageRef maskedImage;
127 CGFloat components[] = { window.colorKeyRed - 0.5, window.colorKeyRed + 0.5,
128 window.colorKeyGreen - 0.5, window.colorKeyGreen + 0.5,
129 window.colorKeyBlue - 0.5, window.colorKeyBlue + 0.5 };
130 maskedImage = CGImageCreateWithMaskingColors(image, components);
133 CGImageRelease(image);
138 context = (CGContextRef)[[NSGraphicsContext currentContext] graphicsPort];
139 CGContextSetBlendMode(context, kCGBlendModeCopy);
140 CGContextDrawImage(context, imageRect, image);
142 CGImageRelease(image);
144 if (window.shapeChangedSinceLastDraw || window.colorKeyed ||
145 window.usePerPixelAlpha)
147 window.shapeChangedSinceLastDraw = FALSE;
148 [window invalidateShadow];
153 pthread_mutex_unlock(window.surface_mutex);
160 @implementation WineWindow
162 @synthesize disabled, noActivate, floating, latentParentWindow;
163 @synthesize surface, surface_mutex;
164 @synthesize shape, shapeChangedSinceLastDraw;
165 @synthesize colorKeyed, colorKeyRed, colorKeyGreen, colorKeyBlue;
166 @synthesize usePerPixelAlpha;
168 + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf
169 windowFrame:(NSRect)window_frame
172 WineContentView* contentView;
174 [self flipRect:&window_frame];
176 window = [[[self alloc] initWithContentRect:window_frame
177 styleMask:style_mask_for_features(wf)
178 backing:NSBackingStoreBuffered
179 defer:YES] autorelease];
181 if (!window) return nil;
182 window->normalStyleMask = [window styleMask];
184 /* Standardize windows to eliminate differences between titled and
185 borderless windows and between NSWindow and NSPanel. */
186 [window setHidesOnDeactivate:NO];
187 [window setReleasedWhenClosed:NO];
189 [window disableCursorRects];
190 [window setShowsResizeIndicator:NO];
191 [window setHasShadow:wf->shadow];
192 [window setColorSpace:[NSColorSpace genericRGBColorSpace]];
193 [window setDelegate:window];
195 contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease];
198 [contentView setAutoresizesSubviews:NO];
200 [window setContentView:contentView];
207 [latentParentWindow release];
212 + (void) flipRect:(NSRect*)rect
214 rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect);
217 - (void) adjustFeaturesForState
219 NSUInteger style = normalStyleMask;
222 style &= ~NSResizableWindowMask;
223 if (style != [self styleMask])
224 [self setStyleMask:style];
226 if (style & NSClosableWindowMask)
227 [[self standardWindowButton:NSWindowCloseButton] setEnabled:!self.disabled];
228 if (style & NSMiniaturizableWindowMask)
229 [[self standardWindowButton:NSWindowMiniaturizeButton] setEnabled:!self.disabled];
232 - (void) setWindowFeatures:(const struct macdrv_window_features*)wf
234 normalStyleMask = style_mask_for_features(wf);
235 [self adjustFeaturesForState];
236 [self setHasShadow:wf->shadow];
239 - (void) setMacDrvState:(const struct macdrv_window_state*)state
242 NSWindowCollectionBehavior behavior;
244 self.disabled = state->disabled;
245 self.noActivate = state->no_activate;
247 self.floating = state->floating;
248 level = state->floating ? NSFloatingWindowLevel : NSNormalWindowLevel;
249 if (level != [self level])
250 [self setLevel:level];
252 behavior = NSWindowCollectionBehaviorDefault;
253 if (state->excluded_by_expose)
254 behavior |= NSWindowCollectionBehaviorTransient;
256 behavior |= NSWindowCollectionBehaviorManaged;
257 if (state->excluded_by_cycle)
259 behavior |= NSWindowCollectionBehaviorIgnoresCycle;
260 if ([self isVisible])
261 [NSApp removeWindowsItem:self];
265 behavior |= NSWindowCollectionBehaviorParticipatesInCycle;
266 if ([self isVisible])
267 [NSApp addWindowsItem:self title:[self title] filename:NO];
269 [self setCollectionBehavior:behavior];
272 /* Returns whether or not the window was ordered in, which depends on if
273 its frame intersects any screen. */
274 - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next
276 BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]);
279 [NSApp transformProcessToForeground];
282 [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]];
284 [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]];
285 if (latentParentWindow)
287 [latentParentWindow addChildWindow:self ordered:NSWindowAbove];
288 self.latentParentWindow = nil;
291 if (![self isExcludedFromWindowsMenu])
292 [NSApp addWindowsItem:self title:[self title] filename:NO];
300 self.latentParentWindow = [self parentWindow];
301 [latentParentWindow removeChildWindow:self];
303 [NSApp removeWindowsItem:self];
306 - (BOOL) setFrameIfOnScreen:(NSRect)contentRect
308 NSArray* screens = [NSScreen screens];
309 BOOL on_screen = [self isVisible];
310 NSRect frame, oldFrame;
312 if (![screens count]) return on_screen;
314 /* Origin is (left, top) in a top-down space. Need to convert it to
315 (left, bottom) in a bottom-up space. */
316 [[self class] flipRect:&contentRect];
320 on_screen = frame_intersects_screens(contentRect, screens);
325 oldFrame = [self frame];
326 frame = [self frameRectForContentRect:contentRect];
327 if (!NSEqualRects(frame, oldFrame))
329 if (NSEqualSizes(frame.size, oldFrame.size))
330 [self setFrameOrigin:frame.origin];
332 [self setFrame:frame display:YES];
338 - (void) setMacDrvParentWindow:(WineWindow*)parent
340 if ([self parentWindow] != parent)
342 [[self parentWindow] removeChildWindow:self];
343 self.latentParentWindow = nil;
344 if ([self isVisible] && parent)
345 [parent addChildWindow:self ordered:NSWindowAbove];
347 self.latentParentWindow = parent;
351 - (void) setDisabled:(BOOL)newValue
353 if (disabled != newValue)
356 [self adjustFeaturesForState];
360 - (BOOL) needsTransparency
362 return self.shape || self.colorKeyed || self.usePerPixelAlpha;
365 - (void) checkTransparency
367 if (![self isOpaque] && !self.needsTransparency)
369 [self setBackgroundColor:[NSColor windowBackgroundColor]];
370 [self setOpaque:YES];
372 else if ([self isOpaque] && self.needsTransparency)
374 [self setBackgroundColor:[NSColor clearColor]];
379 - (void) setShape:(NSBezierPath*)newShape
381 if (shape == newShape) return;
382 if (shape && newShape && [shape isEqual:newShape]) return;
386 [[self contentView] setNeedsDisplayInRect:[shape bounds]];
390 [[self contentView] setNeedsDisplayInRect:[newShape bounds]];
392 shape = [newShape copy];
393 self.shapeChangedSinceLastDraw = TRUE;
395 [self checkTransparency];
400 * ---------- NSWindow method overrides ----------
402 - (BOOL) canBecomeKeyWindow
404 if (self.disabled || self.noActivate) return NO;
408 - (BOOL) canBecomeMainWindow
410 return [self canBecomeKeyWindow];
413 - (BOOL) isExcludedFromWindowsMenu
415 return !([self collectionBehavior] & NSWindowCollectionBehaviorParticipatesInCycle);
418 - (BOOL) validateMenuItem:(NSMenuItem *)menuItem
420 if ([menuItem action] == @selector(makeKeyAndOrderFront:))
421 return [self isKeyWindow] || (!self.disabled && !self.noActivate);
422 return [super validateMenuItem:menuItem];
427 * ---------- NSWindowDelegate methods ----------
429 - (BOOL)windowShouldClose:(id)sender
437 /***********************************************************************
438 * macdrv_create_cocoa_window
440 * Create a Cocoa window with the given content frame and features (e.g.
441 * title bar, close box, etc.).
443 macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf,
446 __block WineWindow* window;
449 window = [[WineWindow createWindowWithFeatures:wf
450 windowFrame:NSRectFromCGRect(frame)] retain];
453 return (macdrv_window)window;
456 /***********************************************************************
457 * macdrv_destroy_cocoa_window
459 * Destroy a Cocoa window.
461 void macdrv_destroy_cocoa_window(macdrv_window w)
463 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
464 WineWindow* window = (WineWindow*)w;
472 /***********************************************************************
473 * macdrv_set_cocoa_window_features
475 * Update a Cocoa window's features.
477 void macdrv_set_cocoa_window_features(macdrv_window w,
478 const struct macdrv_window_features* wf)
480 WineWindow* window = (WineWindow*)w;
483 [window setWindowFeatures:wf];
487 /***********************************************************************
488 * macdrv_set_cocoa_window_state
490 * Update a Cocoa window's state.
492 void macdrv_set_cocoa_window_state(macdrv_window w,
493 const struct macdrv_window_state* state)
495 WineWindow* window = (WineWindow*)w;
498 [window setMacDrvState:state];
502 /***********************************************************************
503 * macdrv_set_cocoa_window_title
505 * Set a Cocoa window's title.
507 void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title,
510 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
511 WineWindow* window = (WineWindow*)w;
512 NSString* titleString;
515 titleString = [NSString stringWithCharacters:title length:length];
519 [window setTitle:titleString];
520 if ([window isVisible] && ![window isExcludedFromWindowsMenu])
521 [NSApp changeWindowsItem:window title:titleString filename:NO];
527 /***********************************************************************
528 * macdrv_order_cocoa_window
530 * Reorder a Cocoa window relative to other windows. If prev is
531 * non-NULL, it is ordered below that window. Else, if next is non-NULL,
532 * it is ordered above that window. Otherwise, it is ordered to the
535 * Returns true if the window has actually been ordered onto the screen
536 * (i.e. if its frame intersects with a screen). Otherwise, false.
538 int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev,
541 WineWindow* window = (WineWindow*)w;
542 __block BOOL on_screen;
545 on_screen = [window orderBelow:(WineWindow*)prev
546 orAbove:(WineWindow*)next];
552 /***********************************************************************
553 * macdrv_hide_cocoa_window
555 * Hides a Cocoa window.
557 void macdrv_hide_cocoa_window(macdrv_window w)
559 WineWindow* window = (WineWindow*)w;
566 /***********************************************************************
567 * macdrv_set_cocoa_window_frame
569 * Move a Cocoa window. If the window has been moved out of the bounds
570 * of the desktop, it is ordered out. (This routine won't ever order a
571 * window in, though.)
573 * Returns true if the window is on screen; false otherwise.
575 int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame)
577 WineWindow* window = (WineWindow*)w;
578 __block BOOL on_screen;
581 on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)];
587 /***********************************************************************
588 * macdrv_set_cocoa_parent_window
590 * Sets the parent window for a Cocoa window. If parent is NULL, clears
593 void macdrv_set_cocoa_parent_window(macdrv_window w, macdrv_window parent)
595 WineWindow* window = (WineWindow*)w;
598 [window setMacDrvParentWindow:(WineWindow*)parent];
602 /***********************************************************************
603 * macdrv_set_window_surface
605 void macdrv_set_window_surface(macdrv_window w, void *surface, pthread_mutex_t *mutex)
607 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
608 WineWindow* window = (WineWindow*)w;
611 window.surface = surface;
612 window.surface_mutex = mutex;
618 /***********************************************************************
619 * macdrv_window_needs_display
621 * Mark a window as needing display in a specified rect (in non-client
624 void macdrv_window_needs_display(macdrv_window w, CGRect rect)
626 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
627 WineWindow* window = (WineWindow*)w;
630 [[window contentView] setNeedsDisplayInRect:NSRectFromCGRect(rect)];
636 /***********************************************************************
637 * macdrv_set_window_shape
639 * Sets the shape of a Cocoa window from an array of rectangles. If
640 * rects is NULL, resets the window's shape to its frame.
642 void macdrv_set_window_shape(macdrv_window w, const CGRect *rects, int count)
644 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
645 WineWindow* window = (WineWindow*)w;
648 if (!rects || !count)
655 path = [NSBezierPath bezierPath];
656 for (i = 0; i < count; i++)
657 [path appendBezierPathWithRect:NSRectFromCGRect(rects[i])];
665 /***********************************************************************
666 * macdrv_set_window_alpha
668 void macdrv_set_window_alpha(macdrv_window w, CGFloat alpha)
670 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
671 WineWindow* window = (WineWindow*)w;
673 [window setAlphaValue:alpha];
678 /***********************************************************************
679 * macdrv_set_window_color_key
681 void macdrv_set_window_color_key(macdrv_window w, CGFloat keyRed, CGFloat keyGreen,
684 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
685 WineWindow* window = (WineWindow*)w;
688 window.colorKeyed = TRUE;
689 window.colorKeyRed = keyRed;
690 window.colorKeyGreen = keyGreen;
691 window.colorKeyBlue = keyBlue;
692 [window checkTransparency];
698 /***********************************************************************
699 * macdrv_clear_window_color_key
701 void macdrv_clear_window_color_key(macdrv_window w)
703 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
704 WineWindow* window = (WineWindow*)w;
707 window.colorKeyed = FALSE;
708 [window checkTransparency];
714 /***********************************************************************
715 * macdrv_window_use_per_pixel_alpha
717 void macdrv_window_use_per_pixel_alpha(macdrv_window w, int use_per_pixel_alpha)
719 NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
720 WineWindow* window = (WineWindow*)w;
723 window.usePerPixelAlpha = use_per_pixel_alpha;
724 [window checkTransparency];