diff options
author | Michael Lutz <michi@icosahedron.de> | 2020-12-26 16:07:47 +0100 |
---|---|---|
committer | Michael Lutz <michi@icosahedron.de> | 2021-01-03 13:25:32 +0100 |
commit | 9ccef816f90e9a7e3042cd89141ba3c0956d7bb9 (patch) | |
tree | 906ca656f585147070658dc14211b31c0689a83e /src/video/cocoa/cocoa_v.mm | |
parent | ab7da117e066983d076f540b9722e1b79cf01d98 (diff) | |
download | openttd-9ccef816f90e9a7e3042cd89141ba3c0956d7bb9.tar.xz |
Codechange: [OSX] Re-arrange the OSX video driver code by combining all drawing code and moving the window/event handling to a different file.
This is just a code move/rename, not a functionality change.
Diffstat (limited to 'src/video/cocoa/cocoa_v.mm')
-rw-r--r-- | src/video/cocoa/cocoa_v.mm | 1151 |
1 files changed, 413 insertions, 738 deletions
diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm index e4a0a0c6f..c01d9c42e 100644 --- a/src/video/cocoa/cocoa_v.mm +++ b/src/video/cocoa/cocoa_v.mm @@ -26,13 +26,16 @@ #include "../../openttd.h" #include "../../debug.h" +#include "../../rev.h" #include "../../core/geometry_type.hpp" #include "cocoa_v.h" +#include "cocoa_wnd.h" #include "../../blitter/factory.hpp" -#include "../../fileio_func.h" #include "../../gfx_func.h" #include "../../window_func.h" #include "../../window_gui.h" +#include "../../core/math_func.hpp" +#include "../../framerate_type.h" #include <array> #import <sys/param.h> /* for MAXPATHLEN */ @@ -45,188 +48,16 @@ * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information. */ +/* On some old versions of MAC OS this may not be defined. + * Those versions generally only produce code for PPC. So it should be safe to + * set this to 0. */ +#ifndef kCGBitmapByteOrder32Host +#define kCGBitmapByteOrder32Host 0 +#endif -@interface OTTDMain : NSObject <NSApplicationDelegate> -@end - - -static NSAutoreleasePool *_ottd_autorelease_pool; -static OTTDMain *_ottd_main; -static bool _cocoa_video_started = false; -static bool _cocoa_video_dialog = false; - +bool _cocoa_video_started = false; CocoaSubdriver *_cocoa_subdriver = NULL; -static NSString *OTTDMainLaunchGameEngine = @"ottdmain_launch_game_engine"; - - -/** - * The main class of the application, the application's delegate. - */ -@implementation OTTDMain -/** - * Stop the game engine. Must be called on main thread. - */ -- (void)stopEngine -{ - [ NSApp stop:self ]; - - /* Send an empty event to return from the run loop. Without that, application is stuck waiting for an event. */ - NSEvent *event = [ NSEvent otherEventWithType:NSApplicationDefined location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0 ]; - [ NSApp postEvent:event atStart:YES ]; -} - -/** - * Start the game loop. - */ -- (void)launchGameEngine: (NSNotification*) note -{ - /* Setup cursor for the current _game_mode. */ - [ _cocoa_subdriver->cocoaview resetCursorRects ]; - - /* Hand off to main application code. */ - QZ_GameLoop(); - - /* We are done, thank you for playing. */ - [ self performSelectorOnMainThread:@selector(stopEngine) withObject:nil waitUntilDone:FALSE ]; -} - -/** - * Called when the internal event loop has just started running. - */ -- (void) applicationDidFinishLaunching: (NSNotification*) note -{ - /* Add a notification observer so we can restart the game loop later on if necessary. */ - [ [ NSNotificationCenter defaultCenter ] addObserver:self selector:@selector(launchGameEngine:) name:OTTDMainLaunchGameEngine object:nil ]; - - /* Start game loop. */ - [ [ NSNotificationCenter defaultCenter ] postNotificationName:OTTDMainLaunchGameEngine object:nil ]; -} - -/** - * Display the in game quit confirmation dialog. - */ -- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*) sender -{ - HandleExitGameRequest(); - - return NSTerminateCancel; // NSTerminateLater ? -} - -/** - * Remove ourself as a notification observer. - */ -- (void)unregisterObserver -{ - [ [ NSNotificationCenter defaultCenter ] removeObserver:self ]; -} -@end - -/** - * Initialize the application menu shown in top bar. - */ -static void setApplicationMenu() -{ - NSString *appName = @"OpenTTD"; - NSMenu *appleMenu = [ [ NSMenu alloc ] initWithTitle:appName ]; - - /* Add menu items */ - NSString *title = [ @"About " stringByAppendingString:appName ]; - [ appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@"" ]; - - [ appleMenu addItem:[ NSMenuItem separatorItem ] ]; - - title = [ @"Hide " stringByAppendingString:appName ]; - [ appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h" ]; - - NSMenuItem *menuItem = [ appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h" ]; - [ menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask | NSCommandKeyMask) ]; - - [ appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@"" ]; - - [ appleMenu addItem:[ NSMenuItem separatorItem ] ]; - - title = [ @"Quit " stringByAppendingString:appName ]; - [ appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q" ]; - - /* Put menu into the menubar */ - menuItem = [ [ NSMenuItem alloc ] initWithTitle:@"" action:nil keyEquivalent:@"" ]; - [ menuItem setSubmenu:appleMenu ]; - [ [ NSApp mainMenu ] addItem:menuItem ]; - - /* Tell the application object that this is now the application menu. - * This interesting Objective-C construct is used because not all SDK - * versions define this method publicly. */ - if ([ NSApp respondsToSelector:@selector(setAppleMenu:) ]) { - [ NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu ]; - } - - /* Finally give up our references to the objects */ - [ appleMenu release ]; - [ menuItem release ]; -} - -/** - * Create a window menu. - */ -static void setupWindowMenu() -{ - NSMenu *windowMenu = [ [ NSMenu alloc ] initWithTitle:@"Window" ]; - - /* "Minimize" item */ - [ windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m" ]; - - /* Put menu into the menubar */ - NSMenuItem *menuItem = [ [ NSMenuItem alloc ] initWithTitle:@"Window" action:nil keyEquivalent:@"" ]; - [ menuItem setSubmenu:windowMenu ]; - [ [ NSApp mainMenu ] addItem:menuItem ]; - - if (MacOSVersionIsAtLeast(10, 7, 0)) { - /* The OS will change the name of this menu item automatically */ - [ windowMenu addItemWithTitle:@"Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"^f" ]; - } - - /* Tell the application object that this is now the window menu */ - [ NSApp setWindowsMenu:windowMenu ]; - - /* Finally give up our references to the objects */ - [ windowMenu release ]; - [ menuItem release ]; -} - -/** - * Startup the application. - */ -static void setupApplication() -{ - ProcessSerialNumber psn = { 0, kCurrentProcess }; - - /* Ensure the application object is initialised */ - [ NSApplication sharedApplication ]; - - /* Tell the dock about us */ - OSStatus returnCode = TransformProcessType(&psn, kProcessTransformToForegroundApplication); - if (returnCode != 0) DEBUG(driver, 0, "Could not change to foreground application. Error %d", (int)returnCode); - - /* Disable the system-wide tab feature as we only have one window. */ - if ([ NSWindow respondsToSelector:@selector(setAllowsAutomaticWindowTabbing:) ]) { - /* We use nil instead of NO as withObject requires an id. */ - [ NSWindow performSelector:@selector(setAllowsAutomaticWindowTabbing:) withObject:nil]; - } - - /* Become the front process, important when start from the command line. */ - [ [ NSApplication sharedApplication ] activateIgnoringOtherApps:YES ]; - - /* Set up the menubar */ - [ NSApp setMainMenu:[ [ NSMenu alloc ] init ] ]; - setApplicationMenu(); - setupWindowMenu(); - - /* Create OTTDMain and make it the app delegate */ - _ottd_main = [ [ OTTDMain alloc ] init ]; - [ NSApp setDelegate:_ottd_main ]; -} - static bool ModeSorter(const OTTD_Point &p1, const OTTD_Point &p2) { @@ -384,13 +215,11 @@ void VideoDriver_Cocoa::Stop() { if (!_cocoa_video_started) return; - [ _ottd_main unregisterObserver ]; + CocoaExitApplication(); delete _cocoa_subdriver; _cocoa_subdriver = NULL; - [ _ottd_main release ]; - _cocoa_video_started = false; } @@ -404,10 +233,8 @@ const char *VideoDriver_Cocoa::Start(const StringList &parm) if (_cocoa_video_started) return "Already started"; _cocoa_video_started = true; - setupApplication(); - /* Don't create a window or enter fullscreen if we're just going to show a dialog. */ - if (_cocoa_video_dialog) return NULL; + if (!CocoaSetupApplication()) return NULL; int width = _cur_resolution.width; int height = _cur_resolution.height; @@ -505,695 +332,543 @@ void VideoDriver_Cocoa::EditBoxLostFocus() HandleTextInput(NULL, true); } -/** - * Catch asserts prior to initialization of the videodriver. - * - * @param title Window title. - * @param message Message text. - * @param buttonLabel Button text. - * - * @note This is needed since sometimes assert is called before the videodriver is initialized . - */ -void CocoaDialog(const char *title, const char *message, const char *buttonLabel) -{ - _cocoa_video_dialog = true; - - bool wasstarted = _cocoa_video_started; - if (VideoDriver::GetInstance() == NULL) { - setupApplication(); // Setup application before showing dialog - } else if (!_cocoa_video_started && VideoDriver::GetInstance()->Start(StringList()) != NULL) { - fprintf(stderr, "%s: %s\n", title, message); - return; - } +#ifdef ENABLE_COCOA_QUARTZ - NSAlert *alert = [ [ NSAlert alloc ] init ]; - [ alert setAlertStyle: NSCriticalAlertStyle ]; - [ alert setMessageText:[ NSString stringWithUTF8String:title ] ]; - [ alert setInformativeText:[ NSString stringWithUTF8String:message ] ]; - [ alert addButtonWithTitle: [ NSString stringWithUTF8String:buttonLabel ] ]; - [ alert runModal ]; - [ alert release ]; +class WindowQuartzSubdriver; - if (!wasstarted && VideoDriver::GetInstance() != NULL) VideoDriver::GetInstance()->Stop(); +/* Subclass of OTTD_CocoaView to fix Quartz rendering */ +@interface OTTD_QuartzView : OTTD_CocoaView +- (void)setDriver:(WindowQuartzSubdriver*)drv; +- (void)drawRect:(NSRect)invalidRect; +@end - _cocoa_video_dialog = false; -} +class WindowQuartzSubdriver : public CocoaSubdriver { +private: + /** + * This function copies 8bpp pixels from the screen buffer in 32bpp windowed mode. + * + * @param left The x coord for the left edge of the box to blit. + * @param top The y coord for the top edge of the box to blit. + * @param right The x coord for the right edge of the box to blit. + * @param bottom The y coord for the bottom edge of the box to blit. + */ + void BlitIndexedToView32(int left, int top, int right, int bottom); -/** Set the application's bundle directory. - * - * This is needed since OS X application bundles do not have a - * current directory and the data files are 'somewhere' in the bundle. - */ -void cocoaSetApplicationBundleDir() -{ - extern std::array<std::string, NUM_SEARCHPATHS> _searchpaths; + virtual void GetDeviceInfo(); + virtual bool SetVideoMode(int width, int height, int bpp); - char tmp[MAXPATHLEN]; - CFAutoRelease<CFURLRef> url(CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle())); - if (CFURLGetFileSystemRepresentation(url.get(), true, (unsigned char*)tmp, MAXPATHLEN)) { - _searchpaths[SP_APPLICATION_BUNDLE_DIR] = tmp; - AppendPathSeparator(_searchpaths[SP_APPLICATION_BUNDLE_DIR]); - } else { - _searchpaths[SP_APPLICATION_BUNDLE_DIR].clear(); - } -} +public: + WindowQuartzSubdriver(); + virtual ~WindowQuartzSubdriver(); -/** - * Setup autorelease for the application pool. - * - * These are called from main() to prevent a _NSAutoreleaseNoPool error when - * exiting before the cocoa video driver has been loaded - */ -void cocoaSetupAutoreleasePool() -{ - _ottd_autorelease_pool = [ [ NSAutoreleasePool alloc ] init ]; -} + virtual void Draw(bool force_update); + virtual void MakeDirty(int left, int top, int width, int height); + virtual void UpdatePalette(uint first_color, uint num_colors); -/** - * Autorelease the application pool. - */ -void cocoaReleaseAutoreleasePool() -{ - [ _ottd_autorelease_pool release ]; -} + virtual uint ListModes(OTTD_Point *modes, uint max_modes); + virtual bool ChangeResolution(int w, int h, int bpp); -/** - * Re-implement the system cursor in order to allow hiding and showing it nicely - */ -@implementation NSCursor (OTTD_CocoaCursor) -+ (NSCursor *) clearCocoaCursor -{ - /* RAW 16x16 transparent GIF */ - unsigned char clearGIFBytes[] = { - 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00, - 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, - 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED, 0x0F, 0xA3, 0x9C, 0xB4, - 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B}; - NSData *clearGIFData = [ NSData dataWithBytesNoCopy:&clearGIFBytes[0] length:55 freeWhenDone:NO ]; - NSImage *clearImg = [ [ NSImage alloc ] initWithData:clearGIFData ]; - return [ [ NSCursor alloc ] initWithImage:clearImg hotSpot:NSMakePoint(0.0,0.0) ]; -} -@end + virtual bool IsFullscreen() { return false; } + virtual bool ToggleFullscreen(); /* Full screen mode on OSX 10.7 */ + + virtual int GetWidth() { return window_width; } + virtual int GetHeight() { return window_height; } + virtual void *GetPixelBuffer() { return buffer_depth == 8 ? pixel_buffer : window_buffer; } + + /* Convert local coordinate to window server (CoreGraphics) coordinate */ + virtual CGPoint PrivateLocalToCG(NSPoint *p); + + virtual NSPoint GetMouseLocation(NSEvent *event); + virtual bool MouseIsInsideView(NSPoint *pt); + + virtual bool IsActive() { return active; } + void SetPortAlphaOpaque(); + bool WindowResized(); +}; -@implementation OTTD_CocoaWindow -- (void)setDriver:(CocoaSubdriver*)drv +@implementation OTTD_QuartzView + +- (void)setDriver:(WindowQuartzSubdriver*)drv { driver = drv; } -/** - * Minimize the window - */ -- (void)miniaturize:(id)sender +- (void)drawRect:(NSRect)invalidRect { - /* make the alpha channel opaque so anim won't have holes in it */ - driver->SetPortAlphaOpaque(); + if (driver->cgcontext == NULL) return; - /* window is hidden now */ - driver->active = false; + CGContextRef viewContext = (CGContextRef)[ [ NSGraphicsContext currentContext ] graphicsPort ]; + CGContextSetShouldAntialias(viewContext, FALSE); + CGContextSetInterpolationQuality(viewContext, kCGInterpolationNone); - [ super miniaturize:sender ]; -} + /* The obtained 'rect' is actually a union of all dirty rects, let's ask for an explicit list of rects instead */ + const NSRect *dirtyRects; + NSInteger dirtyRectCount; + [ self getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount ]; -/** - * This method fires just before the window deminaturizes from the Dock. - * We'll save the current visible surface, let the window manager redraw any - * UI elements, and restore the surface. This way, no expose event - * is required, and the deminiaturize works perfectly. - */ -- (void)display -{ - driver->SetPortAlphaOpaque(); + /* We need an Image in order to do blitting, but as we don't touch the context between this call and drawing no copying will actually be done here */ + CGImageRef fullImage = CGBitmapContextCreateImage(driver->cgcontext); - /* save current visible surface */ - [ self cacheImageInRect:[ driver->cocoaview frame ] ]; - - /* let the window manager redraw controls, border, etc */ - [ super display ]; + /* Calculate total area we are blitting */ + uint32 blitArea = 0; + for (int n = 0; n < dirtyRectCount; n++) { + blitArea += (uint32)(dirtyRects[n].size.width * dirtyRects[n].size.height); + } - /* restore visible surface */ - [ self restoreCachedImage ]; + /* + * This might be completely stupid, but in my extremely subjective opinion it feels faster + * The point is, if we're blitting less than 50% of the dirty rect union then it's still a good idea to blit each dirty + * rect separately but if we blit more than that, it's just cheaper to blit the entire union in one pass. + * Feel free to remove or find an even better value than 50% ... / blackis + */ + NSRect frameRect = [ self frame ]; + if (blitArea / (float)(invalidRect.size.width * invalidRect.size.height) > 0.5f) { + NSRect rect = invalidRect; + CGRect clipRect; + CGRect blitRect; + + blitRect.origin.x = rect.origin.x; + blitRect.origin.y = rect.origin.y; + blitRect.size.width = rect.size.width; + blitRect.size.height = rect.size.height; + + clipRect.origin.x = rect.origin.x; + clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height; + + clipRect.size.width = rect.size.width; + clipRect.size.height = rect.size.height; + + /* Blit dirty part of image */ + CGImageRef clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect); + CGContextDrawImage(viewContext, blitRect, clippedImage); + CGImageRelease(clippedImage); + } else { + for (int n = 0; n < dirtyRectCount; n++) { + NSRect rect = dirtyRects[n]; + CGRect clipRect; + CGRect blitRect; + + blitRect.origin.x = rect.origin.x; + blitRect.origin.y = rect.origin.y; + blitRect.size.width = rect.size.width; + blitRect.size.height = rect.size.height; + + clipRect.origin.x = rect.origin.x; + clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height; + + clipRect.size.width = rect.size.width; + clipRect.size.height = rect.size.height; + + /* Blit dirty part of image */ + CGImageRef clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect); + CGContextDrawImage(viewContext, blitRect, clippedImage); + CGImageRelease(clippedImage); + } + } - /* window is visible again */ - driver->active = true; + CGImageRelease(fullImage); } -/** - * Define the rectangle we draw our window in - */ -- (void)setFrame:(NSRect)frameRect display:(BOOL)flag -{ - [ super setFrame:frameRect display:flag ]; - /* Don't do anything if the window is currently being created */ - if (driver->setup) return; +@end - if (!driver->WindowResized()) error("Cocoa: Failed to resize window."); -} -/** - * Handle hiding of the application - */ -- (void)appDidHide:(NSNotification*)note -{ - driver->active = false; -} -/** - * Fade-in the application and restore display plane - */ -- (void)appWillUnhide:(NSNotification*)note + +void WindowQuartzSubdriver::GetDeviceInfo() { - driver->SetPortAlphaOpaque (); + /* Initialize the video settings; this data persists between mode switches + * and gather some information that is useful to know about the display */ + + /* Use the new API when compiling for OSX 10.6 or later */ + CGDisplayModeRef cur_mode = CGDisplayCopyDisplayMode(kCGDirectMainDisplay); + if (cur_mode == NULL) { return; } + + this->device_width = CGDisplayModeGetWidth(cur_mode); + this->device_height = CGDisplayModeGetHeight(cur_mode); + + CGDisplayModeRelease(cur_mode); } -/** - * Unhide and restore display plane and re-activate driver + +/** Switch to full screen mode on OSX 10.7 + * @return Whether we switched to full screen */ -- (void)appDidUnhide:(NSNotification*)note +bool WindowQuartzSubdriver::ToggleFullscreen() { - driver->active = true; + if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) { + [ this->window performSelector:@selector(toggleFullScreen:) withObject:this->window ]; + return true; + } + + return false; } -/** - * Initialize event system for the application rectangle - */ -- (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag + +bool WindowQuartzSubdriver::SetVideoMode(int width, int height, int bpp) { - /* Make our window subclass receive these application notifications */ - [ [ NSNotificationCenter defaultCenter ] addObserver:self - selector:@selector(appDidHide:) name:NSApplicationDidHideNotification object:NSApp ]; + this->setup = true; + this->GetDeviceInfo(); - [ [ NSNotificationCenter defaultCenter ] addObserver:self - selector:@selector(appDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp ]; + if (width > this->device_width) width = this->device_width; + if (height > this->device_height) height = this->device_height; - [ [ NSNotificationCenter defaultCenter ] addObserver:self - selector:@selector(appWillUnhide:) name:NSApplicationWillUnhideNotification object:NSApp ]; + NSRect contentRect = NSMakeRect(0, 0, width, height); - return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]; -} + /* Check if we should recreate the window */ + if (this->window == nil) { + OTTD_CocoaWindowDelegate *delegate; -@end + /* Set the window style */ + unsigned int style = NSTitledWindowMask; + style |= (NSMiniaturizableWindowMask | NSClosableWindowMask); + style |= NSResizableWindowMask; + /* Manually create a window, avoids having a nib file resource */ + this->window = [ [ OTTD_CocoaWindow alloc ] + initWithContentRect:contentRect + styleMask:style + backing:NSBackingStoreBuffered + defer:NO ]; + if (this->window == nil) { + DEBUG(driver, 0, "Could not create the Cocoa window."); + this->setup = false; + return false; + } -/** - * Count the number of UTF-16 code points in a range of an UTF-8 string. - * @param from Start of the range. - * @param to End of the range. - * @return Number of UTF-16 code points in the range. - */ -static NSUInteger CountUtf16Units(const char *from, const char *to) -{ - NSUInteger i = 0; + /* Add built in full-screen support when available (OS X 10.7 and higher) + * This code actually compiles for 10.5 and later, but only makes sense in conjunction + * with the quartz fullscreen support as found only in 10.7 and later + */ + if ([ this->window respondsToSelector:@selector(toggleFullScreen:) ]) { + NSWindowCollectionBehavior behavior = [ this->window collectionBehavior ]; + behavior |= NSWindowCollectionBehaviorFullScreenPrimary; + [ this->window setCollectionBehavior:behavior ]; - while (from < to) { - WChar c; - size_t len = Utf8Decode(&c, from); - i += len < 4 ? 1 : 2; // Watch for surrogate pairs. - from += len; - } + NSButton* fullscreenButton = [ this->window standardWindowButton:NSWindowFullScreenButton ]; + [ fullscreenButton setAction:@selector(toggleFullScreen:) ]; + [ fullscreenButton setTarget:this->window ]; - return i; -} + [ this->window setCollectionBehavior: NSWindowCollectionBehaviorFullScreenPrimary ]; + } -/** - * Advance an UTF-8 string by a number of equivalent UTF-16 code points. - * @param str UTF-8 string. - * @param count Number of UTF-16 code points to advance the string by. - * @return Advanced string pointer. - */ -static const char *Utf8AdvanceByUtf16Units(const char *str, NSUInteger count) -{ - for (NSUInteger i = 0; i < count && *str != '\0'; ) { - WChar c; - size_t len = Utf8Decode(&c, str); - i += len < 4 ? 1 : 2; // Watch for surrogates. - str += len; - } + [ this->window setDriver:this ]; - return str; -} + char caption[50]; + snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision); + NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption ]; + [ this->window setTitle:nsscaption ]; + [ this->window setMiniwindowTitle:nsscaption ]; + [ nsscaption release ]; -@implementation OTTD_CocoaView -/** - * Initialize the driver - */ -- (void)setDriver:(CocoaSubdriver*)drv -{ - driver = drv; -} -/** - * Define the opaqueness of the window / screen - * @return opaqueness of window / screen - */ -- (BOOL)isOpaque -{ - return YES; -} -/** - * Draws a rectangle on the screen. - * It's overwritten by the individual drivers but must be defined - */ -- (void)drawRect:(NSRect)invalidRect -{ - return; -} -/** - * Allow to handle events - */ -- (BOOL)acceptsFirstResponder -{ - return YES; -} -/** - * Actually handle events - */ -- (BOOL)becomeFirstResponder -{ - return YES; -} -/** - * Define the rectangle where we draw our application window - */ -- (void)setTrackingRect -{ - NSPoint loc = [ self convertPoint:[ [ self window ] mouseLocationOutsideOfEventStream ] fromView:nil ]; - BOOL inside = ([ self hitTest:loc ]==self); - if (inside) [ [ self window ] makeFirstResponder:self ]; - trackingtag = [ self addTrackingRect:[ self visibleRect ] owner:self userData:nil assumeInside:inside ]; -} -/** - * Return responsibility for the application window to system - */ -- (void)clearTrackingRect -{ - [ self removeTrackingRect:trackingtag ]; -} -/** - * Declare responsibility for the cursor within our application rect - */ -- (void)resetCursorRects -{ - [ super resetCursorRects ]; - [ self clearTrackingRect ]; - [ self setTrackingRect ]; - [ self addCursorRect:[ self bounds ] cursor:(_game_mode == GM_BOOTSTRAP ? [ NSCursor arrowCursor ] : [ NSCursor clearCocoaCursor ]) ]; -} -/** - * Prepare for moving the application window - */ -- (void)viewWillMoveToWindow:(NSWindow *)win -{ - if (!win && [ self window ]) [ self clearTrackingRect ]; -} -/** - * Restore our responsibility for our application window after moving - */ -- (void)viewDidMoveToWindow -{ - if ([ self window ]) [ self setTrackingRect ]; -} -/** - * Make OpenTTD aware that it has control over the mouse - */ -- (void)mouseEntered:(NSEvent *)theEvent -{ - _cursor.in_window = true; -} -/** - * Make OpenTTD aware that it has NOT control over the mouse - */ -- (void)mouseExited:(NSEvent *)theEvent -{ - if (_cocoa_subdriver != NULL) UndrawMouseCursor(); - _cursor.in_window = false; -} + [ this->window setContentMinSize:NSMakeSize(64.0f, 64.0f) ]; + [ this->window setAcceptsMouseMovedEvents:YES ]; + [ this->window setViewsNeedDisplay:NO ]; -/** Insert the given text at the given range. */ -- (void)insertText:(id)aString replacementRange:(NSRange)replacementRange -{ - if (!EditBoxInGlobalFocus()) return; + delegate = [ [ OTTD_CocoaWindowDelegate alloc ] init ]; + [ delegate setDriver:this ]; + [ this->window setDelegate:[ delegate autorelease ] ]; + } else { + /* We already have a window, just change its size */ + [ this->window setContentSize:contentRect.size ]; - NSString *s = [ aString isKindOfClass:[ NSAttributedString class ] ] ? [ aString string ] : (NSString *)aString; + /* Ensure frame height - title bar height >= view height */ + contentRect.size.height = Clamp(height, 0, (int)[ this->window frame ].size.height - 22 /* 22 is the height of title bar of window*/); - const char *insert_point = NULL; - const char *replace_range = NULL; - if (replacementRange.location != NSNotFound) { - /* Calculate the part to be replaced. */ - insert_point = Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), replacementRange.location); - replace_range = Utf8AdvanceByUtf16Units(insert_point, replacementRange.length); + if (this->cocoaview != nil) { + height = (int)contentRect.size.height; + [ this->cocoaview setFrameSize:contentRect.size ]; + } } - HandleTextInput(NULL, true); - HandleTextInput([ s UTF8String ], false, NULL, insert_point, replace_range); -} + this->window_width = width; + this->window_height = height; + this->buffer_depth = bpp; -/** Insert the given text at the caret. */ -- (void)insertText:(id)aString -{ - [ self insertText:aString replacementRange:NSMakeRange(NSNotFound, 0) ]; -} + [ (OTTD_CocoaWindow *)this->window center ]; -/** Set a new marked text and reposition the caret. */ -- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange replacementRange:(NSRange)replacementRange -{ - if (!EditBoxInGlobalFocus()) return; - - NSString *s = [ aString isKindOfClass:[ NSAttributedString class ] ] ? [ aString string ] : (NSString *)aString; - - const char *utf8 = [ s UTF8String ]; - if (utf8 != NULL) { - const char *insert_point = NULL; - const char *replace_range = NULL; - if (replacementRange.location != NSNotFound) { - /* Calculate the part to be replaced. */ - NSRange marked = [ self markedRange ]; - insert_point = Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), replacementRange.location + (marked.location != NSNotFound ? marked.location : 0u)); - replace_range = Utf8AdvanceByUtf16Units(insert_point, replacementRange.length); + /* Only recreate the view if it doesn't already exist */ + if (this->cocoaview == nil) { + this->cocoaview = [ [ OTTD_QuartzView alloc ] initWithFrame:contentRect ]; + if (this->cocoaview == nil) { + DEBUG(driver, 0, "Could not create the Quartz view."); + this->setup = false; + return false; } - /* Convert caret index into a pointer in the UTF-8 string. */ - const char *selection = Utf8AdvanceByUtf16Units(utf8, selRange.location); + [ this->cocoaview setDriver:this ]; - HandleTextInput(utf8, true, selection, insert_point, replace_range); + [ (NSView*)this->cocoaview setAutoresizingMask:NSViewWidthSizable | NSViewHeightSizable ]; + [ this->window setContentView:cocoaview ]; + [ this->cocoaview release ]; + [ this->window makeKeyAndOrderFront:nil ]; } -} -/** Set a new marked text and reposition the caret. */ -- (void)setMarkedText:(id)aString selectedRange:(NSRange)selRange -{ - [ self setMarkedText:aString selectedRange:selRange replacementRange:NSMakeRange(NSNotFound, 0) ]; -} + [ this->window setColorSpace:[ NSColorSpace sRGBColorSpace ] ]; + this->color_space = CGColorSpaceCreateWithName(kCGColorSpaceSRGB); + if (this->color_space == nullptr) this->color_space = CGColorSpaceCreateDeviceRGB(); + if (this->color_space == nullptr) error("Could not get a valid colour space for drawing."); -/** Unmark the current marked text. */ -- (void)unmarkText -{ - HandleTextInput(NULL, true); -} + bool ret = WindowResized(); + this->UpdatePalette(0, 256); -/** Get the caret position. */ -- (NSRange)selectedRange -{ - if (!EditBoxInGlobalFocus()) return NSMakeRange(NSNotFound, 0); + this->setup = false; - NSUInteger start = CountUtf16Units(_focused_window->GetFocusedText(), _focused_window->GetCaret()); - return NSMakeRange(start, 0); + return ret; } -/** Get the currently marked range. */ -- (NSRange)markedRange +void WindowQuartzSubdriver::BlitIndexedToView32(int left, int top, int right, int bottom) { - if (!EditBoxInGlobalFocus()) return NSMakeRange(NSNotFound, 0); - - size_t mark_len; - const char *mark = _focused_window->GetMarkedText(&mark_len); - if (mark != NULL) { - NSUInteger start = CountUtf16Units(_focused_window->GetFocusedText(), mark); - NSUInteger len = CountUtf16Units(mark, mark + mark_len); + const uint32 *pal = this->palette; + const uint8 *src = (uint8*)this->pixel_buffer; + uint32 *dst = (uint32*)this->window_buffer; + uint width = this->window_width; + uint pitch = this->window_width; - return NSMakeRange(start, len); + for (int y = top; y < bottom; y++) { + for (int x = left; x < right; x++) { + dst[y * pitch + x] = pal[src[y * width + x]]; + } } - - return NSMakeRange(NSNotFound, 0); } -/** Is any text marked? */ -- (BOOL)hasMarkedText -{ - if (!EditBoxInGlobalFocus()) return NO; - - size_t len; - return _focused_window->GetMarkedText(&len) != NULL; -} -/** Get a string corresponding to the given range. */ -- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange +WindowQuartzSubdriver::WindowQuartzSubdriver() { - if (!EditBoxInGlobalFocus()) return nil; + this->window_width = 0; + this->window_height = 0; + this->buffer_depth = 0; + this->window_buffer = NULL; + this->pixel_buffer = NULL; + this->active = false; + this->setup = false; - NSString *s = [ NSString stringWithUTF8String:_focused_window->GetFocusedText() ]; - NSRange valid_range = NSIntersectionRange(NSMakeRange(0, [ s length ]), theRange); + this->window = nil; + this->cocoaview = nil; - if (actualRange != NULL) *actualRange = valid_range; - if (valid_range.length == 0) return nil; + this->cgcontext = NULL; - return [ [ [ NSAttributedString alloc ] initWithString:[ s substringWithRange:valid_range ] ] autorelease ]; + this->num_dirty_rects = MAX_DIRTY_RECTS; } -/** Get a string corresponding to the given range. */ -- (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange +WindowQuartzSubdriver::~WindowQuartzSubdriver() { - return [ self attributedSubstringForProposedRange:theRange actualRange:NULL ]; -} + /* Release window mode resources */ + if (this->window != nil) [ this->window close ]; -/** Get the current edit box string. */ -- (NSAttributedString *)attributedString -{ - if (!EditBoxInGlobalFocus()) return [ [ [ NSAttributedString alloc ] initWithString:@"" ] autorelease ]; + CGContextRelease(this->cgcontext); - return [ [ [ NSAttributedString alloc ] initWithString:[ NSString stringWithUTF8String:_focused_window->GetFocusedText() ] ] autorelease ]; + CGColorSpaceRelease(this->color_space); + free(this->window_buffer); + free(this->pixel_buffer); } -/** Get the character that is rendered at the given point. */ -- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint +void WindowQuartzSubdriver::Draw(bool force_update) { - if (!EditBoxInGlobalFocus()) return NSNotFound; - - NSPoint view_pt = [ self convertRect:[ [ self window ] convertRectFromScreen:NSMakeRect(thePoint.x, thePoint.y, 0, 0) ] fromView:nil ].origin; - - Point pt = { (int)view_pt.x, (int)[ self frame ].size.height - (int)view_pt.y }; + PerformanceMeasurer framerate(PFE_VIDEO); - const char *ch = _focused_window->GetTextCharacterAtPosition(pt); - if (ch == NULL) return NSNotFound; + /* Check if we need to do anything */ + if (this->num_dirty_rects == 0 || [ this->window isMiniaturized ]) return; - return CountUtf16Units(_focused_window->GetFocusedText(), ch); -} + if (this->num_dirty_rects >= MAX_DIRTY_RECTS) { + this->num_dirty_rects = 1; + this->dirty_rects[0].left = 0; + this->dirty_rects[0].top = 0; + this->dirty_rects[0].right = this->window_width; + this->dirty_rects[0].bottom = this->window_height; + } -/** Get the bounding rect for the given range. */ -- (NSRect)firstRectForCharacterRange:(NSRange)aRange -{ - if (!EditBoxInGlobalFocus()) return NSMakeRect(0, 0, 0, 0); + /* Build the region of dirty rectangles */ + for (int i = 0; i < this->num_dirty_rects; i++) { + /* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */ + if (this->buffer_depth == 8) { + BlitIndexedToView32( + this->dirty_rects[i].left, + this->dirty_rects[i].top, + this->dirty_rects[i].right, + this->dirty_rects[i].bottom + ); + } - /* Convert range to UTF-8 string pointers. */ - const char *start = Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), aRange.location); - const char *end = aRange.length != 0 ? Utf8AdvanceByUtf16Units(_focused_window->GetFocusedText(), aRange.location + aRange.length) : start; + NSRect dirtyrect; + dirtyrect.origin.x = this->dirty_rects[i].left; + dirtyrect.origin.y = this->window_height - this->dirty_rects[i].bottom; + dirtyrect.size.width = this->dirty_rects[i].right - this->dirty_rects[i].left; + dirtyrect.size.height = this->dirty_rects[i].bottom - this->dirty_rects[i].top; - /* Get the bounding rect for the text range.*/ - Rect r = _focused_window->GetTextBoundingRect(start, end); - NSRect view_rect = NSMakeRect(_focused_window->left + r.left, [ self frame ].size.height - _focused_window->top - r.bottom, r.right - r.left, r.bottom - r.top); + /* Normally drawRect will be automatically called by Mac OS X during next update cycle, + * and then blitting will occur. If force_update is true, it will be done right now. */ + [ this->cocoaview setNeedsDisplayInRect:dirtyrect ]; + if (force_update) [ this->cocoaview displayIfNeeded ]; + } - return [ [ self window ] convertRectToScreen:[ self convertRect:view_rect toView:nil ] ]; + this->num_dirty_rects = 0; } -/** Get the bounding rect for the given range. */ -- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange +void WindowQuartzSubdriver::MakeDirty(int left, int top, int width, int height) { - return [ self firstRectForCharacterRange:aRange ]; + if (this->num_dirty_rects < MAX_DIRTY_RECTS) { + dirty_rects[this->num_dirty_rects].left = left; + dirty_rects[this->num_dirty_rects].top = top; + dirty_rects[this->num_dirty_rects].right = left + width; + dirty_rects[this->num_dirty_rects].bottom = top + height; + } + this->num_dirty_rects++; } -/** Get all string attributes that we can process for marked text. */ -- (NSArray*)validAttributesForMarkedText +void WindowQuartzSubdriver::UpdatePalette(uint first_color, uint num_colors) { - return [ NSArray array ]; -} + if (this->buffer_depth != 8) return; -/** Delete single character left of the cursor. */ -- (void)deleteBackward:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_BACKSPACE, 0); -} + for (uint i = first_color; i < first_color + num_colors; i++) { + uint32 clr = 0xff000000; + clr |= (uint32)_cur_palette.palette[i].r << 16; + clr |= (uint32)_cur_palette.palette[i].g << 8; + clr |= (uint32)_cur_palette.palette[i].b; + this->palette[i] = clr; + } -/** Delete word left of the cursor. */ -- (void)deleteWordBackward:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_BACKSPACE | WKC_CTRL, 0); + this->num_dirty_rects = MAX_DIRTY_RECTS; } -/** Delete single character right of the cursor. */ -- (void)deleteForward:(id)sender +uint WindowQuartzSubdriver::ListModes(OTTD_Point *modes, uint max_modes) { - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_DELETE, 0); + return QZ_ListModes(modes, max_modes, kCGDirectMainDisplay, this->buffer_depth); } -/** Delete word right of the cursor. */ -- (void)deleteWordForward:(id)sender +bool WindowQuartzSubdriver::ChangeResolution(int w, int h, int bpp) { - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_DELETE | WKC_CTRL, 0); -} + int old_width = this->window_width; + int old_height = this->window_height; + int old_bpp = this->buffer_depth; -/** Move cursor one character left. */ -- (void)moveLeft:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_LEFT, 0); -} + if (this->SetVideoMode(w, h, bpp)) return true; + if (old_width != 0 && old_height != 0) this->SetVideoMode(old_width, old_height, old_bpp); -/** Move cursor one word left. */ -- (void)moveWordLeft:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_LEFT | WKC_CTRL, 0); + return false; } -/** Move cursor one character right. */ -- (void)moveRight:(id)sender +/* Convert local coordinate to window server (CoreGraphics) coordinate */ +CGPoint WindowQuartzSubdriver::PrivateLocalToCG(NSPoint *p) { - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_RIGHT, 0); -} -/** Move cursor one word right. */ -- (void)moveWordRight:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_RIGHT | WKC_CTRL, 0); -} + p->y = this->window_height - p->y; + *p = [ this->cocoaview convertPoint:*p toView:nil ]; + *p = [ this->window convertRectToScreen:NSMakeRect(p->x, p->y, 0, 0) ].origin; -/** Move cursor one line up. */ -- (void)moveUp:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_UP, 0); -} + p->y = this->device_height - p->y; -/** Move cursor one line down. */ -- (void)moveDown:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_DOWN, 0); -} + CGPoint cgp; + cgp.x = p->x; + cgp.y = p->y; -/** MScroll one line up. */ -- (void)moveUpAndModifySelection:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_UP | WKC_SHIFT, 0); + return cgp; } -/** Scroll one line down. */ -- (void)moveDownAndModifySelection:(id)sender +NSPoint WindowQuartzSubdriver::GetMouseLocation(NSEvent *event) { - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_DOWN | WKC_SHIFT, 0); -} + NSPoint pt; -/** Move cursor to the start of the line. */ -- (void)moveToBeginningOfLine:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_HOME, 0); -} + if ( [ event window ] == nil) { + pt = [ this->cocoaview convertPoint:[ [ this->cocoaview window ] convertRectFromScreen:NSMakeRect([ event locationInWindow ].x, [ event locationInWindow ].y, 0, 0) ].origin fromView:nil ]; + } else { + pt = [ event locationInWindow ]; + } -/** Move cursor to the end of the line. */ -- (void)moveToEndOfLine:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_END, 0); -} + pt.y = this->window_height - pt.y; -/** Scroll one page up. */ -- (void)scrollPageUp:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_PAGEUP, 0); + return pt; } -/** Scroll one page down. */ -- (void)scrollPageDown:(id)sender +bool WindowQuartzSubdriver::MouseIsInsideView(NSPoint *pt) { - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_PAGEDOWN, 0); + return [ cocoaview mouse:*pt inRect:[ this->cocoaview bounds ] ]; } -/** Move cursor (and selection) one page up. */ -- (void)pageUpAndModifySelection:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_PAGEUP | WKC_SHIFT, 0); -} -/** Move cursor (and selection) one page down. */ -- (void)pageDownAndModifySelection:(id)sender +/* This function makes the *game region* of the window 100% opaque. + * The genie effect uses the alpha component. Otherwise, + * it doesn't seem to matter what value it has. + */ +void WindowQuartzSubdriver::SetPortAlphaOpaque() { - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_PAGEDOWN | WKC_SHIFT, 0); -} + uint32 *pixels = (uint32*)this->window_buffer; + uint32 pitch = this->window_width; -/** Scroll to the beginning of the document. */ -- (void)scrollToBeginningOfDocument:(id)sender -{ - /* For compatibility with OTTD on Win/Linux. */ - [ self moveToBeginningOfLine:sender ]; + for (int y = 0; y < this->window_height; y++) + for (int x = 0; x < this->window_width; x++) { + pixels[y * pitch + x] |= 0xFF000000; + } } -/** Scroll to the end of the document. */ -- (void)scrollToEndOfDocument:(id)sender +bool WindowQuartzSubdriver::WindowResized() { - /* For compatibility with OTTD on Win/Linux. */ - [ self moveToEndOfLine:sender ]; -} + if (this->window == nil || this->cocoaview == nil) return true; -/** Return was pressed. */ -- (void)insertNewline:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_RETURN, '\r'); -} + NSRect newframe = [ this->cocoaview frame ]; -/** Escape was pressed. */ -- (void)cancelOperation:(id)sender -{ - if (EditBoxInGlobalFocus()) HandleKeypress(WKC_ESC, 0); -} + this->window_width = (int)newframe.size.width; + this->window_height = (int)newframe.size.height; -/** Invoke the selector if we implement it. */ -- (void)doCommandBySelector:(SEL)aSelector -{ - if ([ self respondsToSelector:aSelector ]) [ self performSelector:aSelector ]; -} + /* Create Core Graphics Context */ + free(this->window_buffer); + this->window_buffer = (uint32*)malloc(this->window_width * this->window_height * 4); -@end + CGContextRelease(this->cgcontext); + this->cgcontext = CGBitmapContextCreate( + this->window_buffer, // data + this->window_width, // width + this->window_height, // height + 8, // bits per component + this->window_width * 4, // bytes per row + this->color_space, // color space + kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host + ); + assert(this->cgcontext != NULL); + CGContextSetShouldAntialias(this->cgcontext, FALSE); + CGContextSetAllowsAntialiasing(this->cgcontext, FALSE); + CGContextSetInterpolationQuality(this->cgcontext, kCGInterpolationNone); + if (this->buffer_depth == 8) { + free(this->pixel_buffer); + this->pixel_buffer = malloc(this->window_width * this->window_height); + if (this->pixel_buffer == NULL) { + DEBUG(driver, 0, "Failed to allocate pixel buffer"); + return false; + } + } -@implementation OTTD_CocoaWindowDelegate -/** Initialize the video driver */ -- (void)setDriver:(CocoaSubdriver*)drv -{ - driver = drv; -} -/** Handle closure requests */ -- (BOOL)windowShouldClose:(id)sender -{ - HandleExitGameRequest(); + QZ_GameSizeChanged(); - return NO; -} -/** Handle key acceptance */ -- (void)windowDidBecomeKey:(NSNotification*)aNotification -{ - driver->active = true; -} -/** Resign key acceptance */ -- (void)windowDidResignKey:(NSNotification*)aNotification -{ - driver->active = false; -} -/** Handle becoming main window */ -- (void)windowDidBecomeMain:(NSNotification*)aNotification -{ - driver->active = true; -} -/** Resign being main window */ -- (void)windowDidResignMain:(NSNotification*)aNotification -{ - driver->active = false; + /* Redraw screen */ + this->num_dirty_rects = MAX_DIRTY_RECTS; + + return true; } -/** Window entered fullscreen mode (10.7). */ -- (void)windowDidEnterFullScreen:(NSNotification *)aNotification + + +CocoaSubdriver *QZ_CreateWindowQuartzSubdriver(int width, int height, int bpp) { - NSPoint loc = [ driver->cocoaview convertPoint:[ [ aNotification object ] mouseLocationOutsideOfEventStream ] fromView:nil ]; - BOOL inside = ([ driver->cocoaview hitTest:loc ] == driver->cocoaview); - - if (inside) { - /* We don't care about the event, but the compiler does. */ - NSEvent *e = [ [ NSEvent alloc ] init ]; - [ driver->cocoaview mouseEntered:e ]; - [ e release ]; + if (!MacOSVersionIsAtLeast(10, 7, 0)) { + DEBUG(driver, 0, "The cocoa quartz subdriver requires Mac OS X 10.7 or later."); + return NULL; } -} -/** The colour profile of the screen the window is on changed. */ -- (void)windowDidChangeScreenProfile:(NSNotification *)aNotification -{ - if (!driver->setup) driver->WindowResized(); -} -@end + if (bpp != 8 && bpp != 32) { + DEBUG(driver, 0, "The cocoa quartz subdriver only supports 8 and 32 bpp."); + return NULL; + } + + WindowQuartzSubdriver *ret = new WindowQuartzSubdriver(); + + if (!ret->ChangeResolution(width, height, bpp)) { + delete ret; + return NULL; + } + + return ret; +} +#endif /* ENABLE_COCOA_QUARTZ */ #endif /* WITH_COCOA */ |