summaryrefslogtreecommitdiff
path: root/src/video/cocoa/cocoa_v.mm
diff options
context:
space:
mode:
Diffstat (limited to 'src/video/cocoa/cocoa_v.mm')
-rw-r--r--src/video/cocoa/cocoa_v.mm416
1 files changed, 416 insertions, 0 deletions
diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm
new file mode 100644
index 000000000..7e09b3d7d
--- /dev/null
+++ b/src/video/cocoa/cocoa_v.mm
@@ -0,0 +1,416 @@
+/* $Id$ */
+
+/******************************************************************************
+ * Cocoa video driver *
+ * Known things left to do: *
+ * Nothing at the moment. *
+ ******************************************************************************/
+
+#ifdef WITH_COCOA
+
+#include <AvailabilityMacros.h>
+
+#import <Cocoa/Cocoa.h>
+#import <sys/time.h> /* gettimeofday */
+#import <sys/param.h> /* for MAXPATHLEN */
+#import <unistd.h>
+
+/**
+ * Important notice regarding all modifications!!!!!!!
+ * There are certain limitations because the file is objective C++.
+ * gdb has limitations.
+ * C++ and objective C code can't be joined in all cases (classes stuff).
+ * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information.
+ */
+
+
+/* Portions of CPS.h */
+struct CPSProcessSerNum {
+ UInt32 lo;
+ UInt32 hi;
+};
+
+extern "C" OSErr CPSGetCurrentProcess(CPSProcessSerNum* psn);
+extern "C" OSErr CPSEnableForegroundOperation(CPSProcessSerNum* psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+extern "C" OSErr CPSSetFrontProcess(CPSProcessSerNum* psn);
+
+/* Disables a warning. This is needed since the method exists but has been dropped from the header, supposedly as of 10.4. */
+#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
+@interface NSApplication(NSAppleMenu)
+- (void)setAppleMenu:(NSMenu *)menu;
+@end
+#endif
+
+
+/* Defined in stdbool.h */
+#ifndef __cplusplus
+# ifndef __BEOS__
+# undef bool
+# undef false
+# undef true
+# endif
+#endif
+
+
+#include "../../stdafx.h"
+#include "../../openttd.h"
+#include "../../debug.h"
+#include "../../macros.h"
+#include "../../os/macosx/splash.h"
+#include "../../variables.h"
+#include "../../gfx.h"
+#include "cocoa_v.h"
+#include "cocoa_keys.h"
+#include "../../blitter/factory.hpp"
+#include "../../fileio.h"
+
+#undef Point
+#undef Rect
+
+
+@interface OTTDMain : NSObject
+@end
+
+
+static NSAutoreleasePool *_ottd_autorelease_pool;
+static OTTDMain *_ottd_main;
+static bool _cocoa_video_started = false;
+static bool _cocoa_video_dialog = false;
+
+CocoaSubdriver* _cocoa_subdriver = NULL;
+
+
+
+/* The main class of the application, the application's delegate */
+@implementation OTTDMain
+/* Called when the internal event loop has just started running */
+- (void) applicationDidFinishLaunching: (NSNotification*) note
+{
+ /* Hand off to main application code */
+ QZ_GameLoop();
+
+ /* We're done, thank you for playing */
+ [ NSApp stop:_ottd_main ];
+}
+
+/* Display the in game quit confirmation dialog */
+- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*) sender
+{
+
+ HandleExitGameRequest();
+
+ return NSTerminateCancel; // NSTerminateLater ?
+}
+@end
+
+static void setApplicationMenu()
+{
+ /* warning: this code is very odd */
+ NSMenu *appleMenu;
+ NSMenuItem *menuItem;
+ NSString *title;
+ NSString *appName;
+
+ appName = @"OTTD";
+ appleMenu = [[NSMenu alloc] initWithTitle:appName];
+
+ /* Add menu items */
+ 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"];
+
+ menuItem = (NSMenuItem*)[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 */
+ [NSApp setAppleMenu:appleMenu];
+
+ /* Finally give up our references to the objects */
+ [appleMenu release];
+ [menuItem release];
+}
+
+/* Create a window menu */
+static void setupWindowMenu()
+{
+ NSMenu* windowMenu;
+ NSMenuItem* windowMenuItem;
+ NSMenuItem* menuItem;
+
+ windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
+
+ /* "Minimize" item */
+ menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
+ [windowMenu addItem:menuItem];
+ [menuItem release];
+
+ /* Put menu into the menubar */
+ windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
+ [windowMenuItem setSubmenu:windowMenu];
+ [[NSApp mainMenu] addItem:windowMenuItem];
+
+ /* Tell the application object that this is now the window menu */
+ [NSApp setWindowsMenu:windowMenu];
+
+ /* Finally give up our references to the objects */
+ [windowMenu release];
+ [windowMenuItem release];
+}
+
+static void setupApplication()
+{
+ CPSProcessSerNum PSN;
+
+ /* Ensure the application object is initialised */
+ [NSApplication sharedApplication];
+
+ /* Tell the dock about us */
+ if (!CPSGetCurrentProcess(&PSN) &&
+ !CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103) &&
+ !CPSSetFrontProcess(&PSN)) {
+ [NSApplication sharedApplication];
+ }
+
+ /* 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 void QZ_UpdateVideoModes()
+{
+ uint i, count;
+ OTTDPoint modes[32];
+
+ assert(_cocoa_subdriver != NULL);
+
+ count = _cocoa_subdriver->ListModes(modes, lengthof(modes));
+
+ for (i = 0; i < count; i++) {
+ _resolutions[i][0] = modes[i].x;
+ _resolutions[i][1] = modes[i].y;
+ }
+
+ _num_resolutions = count;
+}
+
+
+void QZ_GameSizeChanged()
+{
+ if (_cocoa_subdriver == NULL) return;
+
+ /* Tell the game that the resolution has changed */
+ _screen.width = _cocoa_subdriver->GetWidth();
+ _screen.height = _cocoa_subdriver->GetHeight();
+ _screen.pitch = _cocoa_subdriver->GetWidth();
+ _fullscreen = _cocoa_subdriver->IsFullscreen();
+
+ GameSizeChanged();
+}
+
+
+static CocoaSubdriver *QZ_CreateWindowSubdriver(int width, int height, int bpp)
+{
+ // For now there is only the quickdraw window mode subdriver, but a pure quartz one should be added.
+ return QZ_CreateWindowQuickdrawSubdriver(width, height, bpp);
+}
+
+
+static CocoaSubdriver *QZ_CreateSubdriver(int width, int height, int bpp, bool fullscreen, bool fallback)
+{
+ CocoaSubdriver *ret;
+
+ ret = fullscreen ? QZ_CreateFullscreenSubdriver(width, height, bpp) : QZ_CreateWindowSubdriver(width, height, bpp);
+ if (ret != NULL) return ret;
+
+ if (!fallback) return NULL;
+
+ /* Try again in 640x480 windowed */
+ DEBUG(driver, 0, "Setting video mode failed, falling back to 640x480 windowed mode.");
+ ret = QZ_CreateWindowSubdriver(640, 480, bpp);
+ if (ret != NULL) return ret;
+
+#ifdef _DEBUG
+ /* Try fullscreen too when in debug mode */
+ DEBUG(driver, 0, "Setting video mode failed, falling back to 640x480 fullscreen mode.");
+ ret = QZ_CreateFullscreenSubdriver(640, 480, bpp);
+ if (ret != NULL) return ret;
+#endif
+
+ return NULL;
+}
+
+
+static FVideoDriver_Cocoa iFVideoDriver_Cocoa;
+
+void VideoDriver_Cocoa::Stop()
+{
+ if (!_cocoa_video_started) return;
+
+ delete _cocoa_subdriver;
+ _cocoa_subdriver = NULL;
+
+ [_ottd_main release];
+
+ _cocoa_video_started = false;
+}
+
+const char *VideoDriver_Cocoa::Start(const char * const *parm)
+{
+ int width, height, bpp;
+
+ 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;
+
+ width = _cur_resolution[0];
+ height = _cur_resolution[1];
+ bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth();
+
+ _cocoa_subdriver = QZ_CreateSubdriver(width, height, bpp, _fullscreen, true);
+ if (_cocoa_subdriver == NULL) {
+ Stop();
+ return "Could not create subdriver";
+ }
+
+ QZ_GameSizeChanged();
+
+ QZ_UpdateVideoModes();
+
+ return NULL;
+}
+
+void VideoDriver_Cocoa::MakeDirty(int left, int top, int width, int height)
+{
+ assert(_cocoa_subdriver != NULL);
+
+ _cocoa_subdriver->MakeDirty(left, top, width, height);
+}
+
+void VideoDriver_Cocoa::MainLoop()
+{
+ /* Start the main event loop */
+ [NSApp run];
+}
+
+bool VideoDriver_Cocoa::ChangeResolution(int w, int h)
+{
+ bool ret;
+
+ assert(_cocoa_subdriver != NULL);
+
+ ret = _cocoa_subdriver->ChangeResolution(w, h);
+
+ QZ_GameSizeChanged();
+
+ QZ_UpdateVideoModes();
+
+ return ret;
+}
+
+void VideoDriver_Cocoa::ToggleFullscreen(bool full_screen)
+{
+ bool oldfs;
+
+ assert(_cocoa_subdriver != NULL);
+
+ oldfs = _cocoa_subdriver->IsFullscreen();
+ if (full_screen != oldfs) {
+ int width = _cocoa_subdriver->GetWidth();
+ int height = _cocoa_subdriver->GetHeight();
+ int bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth();
+
+ delete _cocoa_subdriver;
+ _cocoa_subdriver = NULL;
+
+ _cocoa_subdriver = QZ_CreateSubdriver(width, height, bpp, full_screen, false);
+ if (_cocoa_subdriver == NULL) {
+ _cocoa_subdriver = QZ_CreateSubdriver(width, height, bpp, oldfs, true);
+ if (_cocoa_subdriver == NULL) error("Cocoa: Failed to create subdriver");
+ }
+ }
+
+ QZ_GameSizeChanged();
+
+ QZ_UpdateVideoModes();
+}
+
+
+/* This is needed since sometimes assert is called before the videodriver is initialized */
+void CocoaDialog(const char* title, const char* message, const char* buttonLabel)
+{
+ bool wasstarted;
+
+ _cocoa_video_dialog = true;
+
+ wasstarted = _cocoa_video_started;
+ if (_video_driver == NULL) {
+ setupApplication(); // Setup application before showing dialog
+ } else if (!_cocoa_video_started && _video_driver->Start(NULL) != NULL) {
+ fprintf(stderr, "%s: %s\n", title, message);
+ return;
+ }
+
+ NSRunAlertPanel([NSString stringWithCString: title], [NSString stringWithCString: message], [NSString stringWithCString: buttonLabel], nil, nil);
+
+ if (!wasstarted && _video_driver != NULL) _video_driver->Stop();
+
+ _cocoa_video_dialog = false;
+}
+
+/* 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()
+{
+ char tmp[MAXPATHLEN];
+ CFURLRef url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
+ if (CFURLGetFileSystemRepresentation(url, true, (unsigned char*)tmp, MAXPATHLEN)) {
+ AppendPathSeparator(tmp, lengthof(tmp));
+ _searchpaths[SP_APPLICATION_BUNDLE_DIR] = strdup(tmp);
+ } else {
+ _searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
+ }
+
+ CFRelease(url);
+}
+
+/* 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];
+}
+
+void cocoaReleaseAutoreleasePool()
+{
+ [_ottd_autorelease_pool release];
+}
+
+#endif /* WITH_COCOA */