/* * MACDRV Cocoa window code * * Copyright 2011, 2012, 2013 Ken Thomases for CodeWeavers Inc. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */ #import "cocoa_window.h" #include "macdrv_cocoa.h" #import "cocoa_app.h" static NSUInteger style_mask_for_features(const struct macdrv_window_features* wf) { NSUInteger style_mask; if (wf->title_bar) { style_mask = NSTitledWindowMask; if (wf->close_button) style_mask |= NSClosableWindowMask; if (wf->minimize_button) style_mask |= NSMiniaturizableWindowMask; if (wf->resizable) style_mask |= NSResizableWindowMask; if (wf->utility) style_mask |= NSUtilityWindowMask; } else style_mask = NSBorderlessWindowMask; return style_mask; } static BOOL frame_intersects_screens(NSRect frame, NSArray* screens) { NSScreen* screen; for (screen in screens) { if (NSIntersectsRect(frame, [screen frame])) return TRUE; } return FALSE; } @interface WineContentView : NSView @end @interface WineWindow () + (void) flipRect:(NSRect*)rect; @end @implementation WineContentView - (BOOL) isFlipped { return YES; } @end @implementation WineWindow + (WineWindow*) createWindowWithFeatures:(const struct macdrv_window_features*)wf windowFrame:(NSRect)window_frame { WineWindow* window; WineContentView* contentView; [self flipRect:&window_frame]; window = [[[self alloc] initWithContentRect:window_frame styleMask:style_mask_for_features(wf) backing:NSBackingStoreBuffered defer:YES] autorelease]; /* Standardize windows to eliminate differences between titled and borderless windows and between NSWindow and NSPanel. */ [window setHidesOnDeactivate:NO]; [window setReleasedWhenClosed:NO]; [window disableCursorRects]; [window setShowsResizeIndicator:NO]; [window setHasShadow:wf->shadow]; [window setDelegate:window]; contentView = [[[WineContentView alloc] initWithFrame:NSZeroRect] autorelease]; if (!contentView) return nil; [contentView setAutoresizesSubviews:NO]; [window setContentView:contentView]; return window; } + (void) flipRect:(NSRect*)rect { rect->origin.y = NSMaxY([[[NSScreen screens] objectAtIndex:0] frame]) - NSMaxY(*rect); } - (void) setWindowFeatures:(const struct macdrv_window_features*)wf { [self setStyleMask:style_mask_for_features(wf)]; [self setHasShadow:wf->shadow]; } /* Returns whether or not the window was ordered in, which depends on if its frame intersects any screen. */ - (BOOL) orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next { BOOL on_screen = frame_intersects_screens([self frame], [NSScreen screens]); if (on_screen) { [NSApp transformProcessToForeground]; if (prev) [self orderWindow:NSWindowBelow relativeTo:[prev windowNumber]]; else [self orderWindow:NSWindowAbove relativeTo:[next windowNumber]]; } return on_screen; } - (BOOL) setFrameIfOnScreen:(NSRect)contentRect { NSArray* screens = [NSScreen screens]; BOOL on_screen = [self isVisible]; NSRect frame, oldFrame; if (![screens count]) return on_screen; /* Origin is (left, top) in a top-down space. Need to convert it to (left, bottom) in a bottom-up space. */ [[self class] flipRect:&contentRect]; if (on_screen) { on_screen = frame_intersects_screens(contentRect, screens); if (!on_screen) [self orderOut:nil]; } oldFrame = [self frame]; frame = [self frameRectForContentRect:contentRect]; if (!NSEqualRects(frame, oldFrame)) { if (NSEqualSizes(frame.size, oldFrame.size)) [self setFrameOrigin:frame.origin]; else [self setFrame:frame display:YES]; } return on_screen; } /* * ---------- NSWindow method overrides ---------- */ - (BOOL) canBecomeKeyWindow { return YES; } - (BOOL) canBecomeMainWindow { return [self canBecomeKeyWindow]; } /* * ---------- NSWindowDelegate methods ---------- */ - (BOOL)windowShouldClose:(id)sender { return NO; } @end /*********************************************************************** * macdrv_create_cocoa_window * * Create a Cocoa window with the given content frame and features (e.g. * title bar, close box, etc.). */ macdrv_window macdrv_create_cocoa_window(const struct macdrv_window_features* wf, CGRect frame) { __block WineWindow* window; OnMainThread(^{ window = [[WineWindow createWindowWithFeatures:wf windowFrame:NSRectFromCGRect(frame)] retain]; }); return (macdrv_window)window; } /*********************************************************************** * macdrv_destroy_cocoa_window * * Destroy a Cocoa window. */ void macdrv_destroy_cocoa_window(macdrv_window w) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; WineWindow* window = (WineWindow*)w; [window close]; [window release]; [pool release]; } /*********************************************************************** * macdrv_set_cocoa_window_features * * Update a Cocoa window's features. */ void macdrv_set_cocoa_window_features(macdrv_window w, const struct macdrv_window_features* wf) { WineWindow* window = (WineWindow*)w; OnMainThread(^{ [window setWindowFeatures:wf]; }); } /*********************************************************************** * macdrv_set_cocoa_window_title * * Set a Cocoa window's title. */ void macdrv_set_cocoa_window_title(macdrv_window w, const unsigned short* title, size_t length) { NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init]; WineWindow* window = (WineWindow*)w; NSString* titleString; if (title) titleString = [NSString stringWithCharacters:title length:length]; else titleString = @""; OnMainThreadAsync(^{ [window setTitle:titleString]; }); [pool release]; } /*********************************************************************** * macdrv_order_cocoa_window * * Reorder a Cocoa window relative to other windows. If prev is * non-NULL, it is ordered below that window. Else, if next is non-NULL, * it is ordered above that window. Otherwise, it is ordered to the * front. * * Returns true if the window has actually been ordered onto the screen * (i.e. if its frame intersects with a screen). Otherwise, false. */ int macdrv_order_cocoa_window(macdrv_window w, macdrv_window prev, macdrv_window next) { WineWindow* window = (WineWindow*)w; __block BOOL on_screen; OnMainThread(^{ on_screen = [window orderBelow:(WineWindow*)prev orAbove:(WineWindow*)next]; }); return on_screen; } /*********************************************************************** * macdrv_hide_cocoa_window * * Hides a Cocoa window. */ void macdrv_hide_cocoa_window(macdrv_window w) { WineWindow* window = (WineWindow*)w; OnMainThread(^{ [window orderOut:nil]; }); } /*********************************************************************** * macdrv_set_cocoa_window_frame * * Move a Cocoa window. If the window has been moved out of the bounds * of the desktop, it is ordered out. (This routine won't ever order a * window in, though.) * * Returns true if the window is on screen; false otherwise. */ int macdrv_set_cocoa_window_frame(macdrv_window w, const CGRect* new_frame) { WineWindow* window = (WineWindow*)w; __block BOOL on_screen; OnMainThread(^{ on_screen = [window setFrameIfOnScreen:NSRectFromCGRect(*new_frame)]; }); return on_screen; }