(svn r3281) -Feature: [OSX] added native cocoa sound and video drivers (egladil)
authorbjarni
Sat, 10 Dec 2005 11:16:45 +0000
changeset 2736 1ea068235989
parent 2735 6f1b148a676b
child 2737 f16e0a808897
(svn r3281) -Feature: [OSX] added native cocoa sound and video drivers (egladil)
you can still use SDL drivers if you like and you have to run "make upgradeconf" to start using the cocoa drivers (or manually write WITH_COCOA:=1)
since SDL breaks the cocoa drivers, you can't compile with both SDL and cocoa support
Using cocoa drivers makes it easier to make universal binaries and it solves:
-FS#18 [OSX] SDL is weird in universal binaries
-FS#2 [OSX] lazy pointer crash on exit
-FS#10 [OSX] linking error when linking statically to SDL 1.2.8 (needless to explain this, but it means it should be able to compile statically with the default settings now)
-[ 1215073 ] Switching to large size out of fullscreen crashes
Using SDL drivers will still have those issues though
Makefile
driver.c
fileio.c
fileio.h
makefiledir/Makefile.config_writer
makefiledir/Makefile.libdetection
os/macosx/Makefile
os/macosx/macos.m
os/macosx/splash.c
os/macosx/splash.h
os/macosx/splash.png
sound/cocoa_s.c
sound/cocoa_s.h
unix.c
video/cocoa_v.h
video/cocoa_v.m
--- a/Makefile	Sat Dec 10 07:29:31 2005 +0000
+++ b/Makefile	Sat Dec 10 11:16:45 2005 +0000
@@ -43,6 +43,7 @@
 # WITH_ZLIB: savegames using zlib
 # WITH_PNG: screenshots using PNG
 # WITH_SDL: SDL video driver support
+# WITH_COCOA: Cocoa video driver support
 #
 # Summary of other defines:
 # MANUAL_CONFIG: do not use Makefile.config, config options set manually
@@ -206,6 +207,14 @@
 endif
 endif
 
+ifdef WITH_COCOA
+ifdef WITH_SDL
+$(error You can not use both the SDL video driver and the Cocoa video driver at the same time)
+endif
+ifdef DEDICATED
+$(error You can not use the Cocoa video driver in a dedicated server)
+endif
+else
 # Force SDL on UNIX platforms
 ifndef WITH_SDL
 ifdef UNIX
@@ -214,6 +223,7 @@
 endif
 endif
 endif
+endif
 
 # remove the dependancy for sdl if DEDICALTED is used
 # and add -lpthread to LDFLAGS, because SDL normally adds that...
@@ -502,7 +512,6 @@
 STRGEN_FLAGS=
 endif
 
-
 # OSX specific setup
 ifdef OSX
 	# set the endian flag for OSX, that can't fail
@@ -519,6 +528,11 @@
 		LIBS += -framework QuickTime
 	endif
 
+	ifdef WITH_COCOA
+		CDEFS += -DWITH_COCOA
+		LIBS += -F/System/Library/Frameworks -framework Cocoa -framework Carbon -framework AudioUnit
+	endif
+
 	# OSX path setup
 	ifndef SECOND_DATA_PATH
 		SECOND_DATA_PATH:="$(OSXAPP)/Contents/Data/"
@@ -744,10 +758,15 @@
 endif
 
 ifdef OSX
-  SRCS += os/macosx/macos.m
-  ifndef DEDICATED
-    SRCS += music/qtmidi.c
-  endif
+	SRCS += os/macosx/macos.m
+	ifndef DEDICATED
+		SRCS += music/qtmidi.c
+	endif
+	ifdef WITH_COCOA
+		SRCS += video/cocoa_v.m
+		SRCS += sound/cocoa_s.c
+		SRCS += os/macosx/splash.c
+	endif
 endif
 
 ifdef BEOS
--- a/driver.c	Sat Dec 10 07:29:31 2005 +0000
+++ b/driver.c	Sat Dec 10 11:16:45 2005 +0000
@@ -18,11 +18,13 @@
 
 #include "sound/null_s.h"
 #include "sound/sdl_s.h"
+#include "sound/cocoa_s.h"
 #include "sound/win32_s.h"
 
 #include "video/dedicated_v.h"
 #include "video/null_v.h"
 #include "video/sdl_v.h"
+#include "video/cocoa_v.h"
 #include "video/win32_v.h"
 
 typedef struct DriverDesc {
@@ -71,6 +73,9 @@
 #ifdef WITH_SDL
 	M("sdl",   "SDL Sound Driver",     &_sdl_sound_driver),
 #endif
+#ifdef WITH_COCOA
+	M("cocoa", "Cocoa Sound Driver",   &_cocoa_sound_driver),
+#endif
 	M("null",  "Null Sound Driver",    &_null_sound_driver),
 	M(NULL, NULL, NULL)
 };
@@ -82,6 +87,9 @@
 #ifdef WITH_SDL
 	M("sdl",        "SDL Video Driver",       &_sdl_video_driver),
 #endif
+#ifdef WITH_COCOA
+	M("cocoa",      "Cocoa Video Driver",       &_cocoa_video_driver),
+#endif
 	M("null",       "Null Video Driver",      &_null_video_driver),
 #ifdef ENABLE_NETWORK
 	M("dedicated",  "Dedicated Video Driver", &_dedicated_video_driver),
--- a/fileio.c	Sat Dec 10 07:29:31 2005 +0000
+++ b/fileio.c	Sat Dec 10 11:16:45 2005 +0000
@@ -141,6 +141,37 @@
 	}
 }
 
+FILE *FioFOpenFile(const char *filename)
+{
+	FILE *f;
+	char buf[MAX_PATH];
+
+	sprintf(buf, "%s%s", _path.data_dir, filename);
+
+	f = fopen(buf, "rb");
+#if !defined(WIN32)
+	if (f == NULL) {
+		char *s;
+		// Make lower case and try again
+		for(s=buf + strlen(_path.data_dir) - 1; *s != 0; s++)
+			*s = tolower(*s);
+		f = fopen(buf, "rb");
+
+#if defined SECOND_DATA_DIR
+		// tries in the 2nd data directory
+		if (f == NULL) {
+			sprintf(buf, "%s%s", _path.second_data_dir, filename);
+			for(s=buf + strlen(_path.second_data_dir) - 1; *s != 0; s++)
+				*s = tolower(*s);
+			f = fopen(buf, "rb");
+		}
+#endif
+	}
+#endif
+
+	return f;
+}
+
 void FioOpenFile(int slot, const char *filename)
 {
 	FILE *f;
--- a/fileio.h	Sat Dec 10 07:29:31 2005 +0000
+++ b/fileio.h	Sat Dec 10 11:16:45 2005 +0000
@@ -10,6 +10,7 @@
 uint16 FioReadWord(void);
 uint32 FioReadDword(void);
 void FioCloseAll(void);
+FILE *FioFOpenFile(const char *filename);
 void FioOpenFile(int slot, const char *filename);
 void FioReadBlock(void *ptr, uint size);
 void FioSkipBytes(int n);
--- a/makefiledir/Makefile.config_writer	Sat Dec 10 07:29:31 2005 +0000
+++ b/makefiledir/Makefile.config_writer	Sat Dec 10 11:16:45 2005 +0000
@@ -72,6 +72,7 @@
 	$(call CONFIG_LINE,WITH_SDL:=$(WITH_SDL))
 	$(call CONFIG_LINE,WITH_PNG:=$(WITH_PNG))
 	$(call CONFIG_LINE,STATIC_ZLIB_PATH:=$(STATIC_ZLIB_PATH))
+	$(call CONFIG_LINE,WITH_COCOA:=$(WITH_COCOA))
 	$(call CONFIG_LINE,)
 
 	$(call CONFIG_LINE,\# Lib paths for OSX. Read os/MacOSX/Makefile for more info)
--- a/makefiledir/Makefile.libdetection	Sat Dec 10 07:29:31 2005 +0000
+++ b/makefiledir/Makefile.libdetection	Sat Dec 10 11:16:45 2005 +0000
@@ -93,6 +93,14 @@
 endif
 endif
 
+ifdef OSX
+# we prefer to use cocoa drivers rather than SDL drivers
+# if you really want SDL drivers, you can always modify Makefile.config
+WITH_COCOA:=1
+WITH_SDL:=
+endif
+
+
 # workaround
 # cygwin have problems with libpng, so we will just disable it for now until the problem is solved
 ifdef CYGWIN
--- a/os/macosx/Makefile	Sat Dec 10 07:29:31 2005 +0000
+++ b/os/macosx/Makefile	Sat Dec 10 11:16:45 2005 +0000
@@ -45,6 +45,7 @@
 	$(Q)cp os/macosx/openttd.icns "$(OSXAPP)"/Contents/Resources/openttd.icns
 	$(Q)os/macosx/plistgen.sh "$(OSXAPP)" "$(REV)"
 	$(Q)cp data/* "$(OSXAPP)"/Contents/Data/
+	$(Q)cp os/macosx/splash.png "$(OSXAPP)"/Contents/Data/
 	$(Q)cp lang/*.lng "$(OSXAPP)"/Contents/Lang/
 	$(Q)cp $(TTD) "$(OSXAPP)"/Contents/MacOS/$(TTD)
 	$(COPY_x86_SDL_LIB)
--- a/os/macosx/macos.m	Sat Dec 10 07:29:31 2005 +0000
+++ b/os/macosx/macos.m	Sat Dec 10 11:16:45 2005 +0000
@@ -15,12 +15,12 @@
 
 void ShowMacAssertDialog ( const char *function, const char *file, const int line, const char *expression )
 {
-	const char *buffer = 
+	const char *buffer =
 			[[NSString stringWithFormat:@"An assertion has failed and OpenTTD must quit.\n%s in %s (line %d)\n\"%s\"\n\nYou should report this error the OpenTTD developers if you think you found a bug.",
 			function, file, line, expression] cStringUsingEncoding:NSASCIIStringEncoding];
 	NSLog(@"%s", buffer);
 	ShowMacDialog( "Assertion Failed", buffer, "Quit" );
-	
+
 	// abort so that a debugger has a chance to notice
 	abort();
 }
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/os/macosx/splash.c	Sat Dec 10 11:16:45 2005 +0000
@@ -0,0 +1,145 @@
+
+#include "../../stdafx.h"
+#include "../../openttd.h"
+#include "../../variables.h"
+#include "../../macros.h"
+#include "../../debug.h"
+#include "../../functions.h"
+#include "../../gfx.h"
+#include "../../fileio.h"
+
+#include "splash.h"
+
+#ifdef WITH_PNG
+
+#include <png.h>
+
+static void PNGAPI png_my_error(png_structp png_ptr, png_const_charp message)
+{
+	DEBUG(misc, 0) ("ERROR(libpng): %s - %s", message, (char *)png_get_error_ptr(png_ptr));
+	longjmp(png_ptr->jmpbuf, 1);
+}
+
+static void PNGAPI png_my_warning(png_structp png_ptr, png_const_charp message)
+{
+	DEBUG(misc, 0) ("WARNING(libpng): %s - %s", message, (char *)png_get_error_ptr(png_ptr));
+}
+
+void DisplaySplashImage(void)
+{
+	png_byte header[8];
+	FILE *f;
+	png_structp png_ptr;
+	png_infop info_ptr, end_info;
+	uint width, height, bit_depth, color_type;
+	png_colorp palette;
+	int num_palette;
+	png_bytep *row_pointers;
+	uint8 *src, *dst;
+	uint y;
+	uint xoff, yoff;
+	int i;
+
+	f = FioFOpenFile(SPLASH_IMAGE_FILE);
+	if (f == NULL) return;
+
+	fread(header, 1, 8, f);
+	if (png_sig_cmp(header, 0, 8) != 0) {
+		fclose(f);
+		return;
+	}
+
+	png_ptr = png_create_read_struct (PNG_LIBPNG_VER_STRING, (png_voidp) NULL, png_my_error, png_my_warning);
+
+	if (png_ptr == NULL) {
+		fclose(f);
+		return;
+	}
+
+	info_ptr = png_create_info_struct(png_ptr);
+	if (info_ptr == NULL) {
+		png_destroy_read_struct(&png_ptr, (png_infopp)NULL, (png_infopp)NULL);
+		fclose(f);
+		return;
+	}
+
+	end_info = png_create_info_struct(png_ptr);
+	if (end_info == NULL) {
+		png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp)NULL);
+		fclose(f);
+		return;
+	}
+
+	if (setjmp(png_jmpbuf(png_ptr))) {
+		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+		fclose(f);
+		return;
+	}
+
+	png_init_io(png_ptr, f);
+	png_set_sig_bytes(png_ptr, 8);
+
+	png_read_png(png_ptr, info_ptr, PNG_TRANSFORM_IDENTITY, NULL);
+
+	width            = png_get_image_width(png_ptr, info_ptr);
+	height           = png_get_image_height(png_ptr, info_ptr);
+	bit_depth        = png_get_bit_depth(png_ptr, info_ptr);
+	color_type       = png_get_color_type(png_ptr, info_ptr);
+
+	if(color_type != PNG_COLOR_TYPE_PALETTE || bit_depth != 8) {
+		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+		fclose(f);
+		return;
+	}
+
+	if(!png_get_valid(png_ptr, info_ptr, PNG_INFO_PLTE)) {
+		png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+		fclose(f);
+		return;
+	}
+
+	png_get_PLTE(png_ptr, info_ptr, &palette, &num_palette);
+
+	row_pointers = png_get_rows(png_ptr, info_ptr);
+
+	memset(_screen.dst_ptr, 0xff, _screen.pitch * _screen.height);
+
+	if(width > (uint) _screen.width)
+		width = _screen.width;
+	if(height > (uint) _screen.height)
+		height = _screen.height;
+
+	xoff = (_screen.width - width) / 2;
+	yoff = (_screen.height - height) / 2;
+	for(y = 0; y < height; y++) {
+		src = row_pointers[y];
+		dst = ((uint8 *) _screen.dst_ptr) + (yoff + y) * _screen.pitch + xoff;
+
+		memcpy(dst, src, width);
+	}
+
+	for (i = 0; i < num_palette; i++) {
+		_cur_palette[i].r = palette[i].red;
+		_cur_palette[i].g = palette[i].green;
+		_cur_palette[i].b = palette[i].blue;
+	}
+
+	_cur_palette[0xff].r = 0;
+	_cur_palette[0xff].g = 0;
+	_cur_palette[0xff].b = 0;
+
+	_pal_first_dirty = 0;
+	_pal_last_dirty = 0xff;
+
+	png_destroy_read_struct(&png_ptr, &info_ptr, &end_info);
+	fclose(f);
+	return;
+}
+
+
+
+#else // WITH_PNG
+
+void DisplaySplashImage(void) {}
+
+#endif // WITH_PNG
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/os/macosx/splash.h	Sat Dec 10 11:16:45 2005 +0000
@@ -0,0 +1,9 @@
+
+#ifndef SPLASH_H
+#define SPLASH_H
+
+#define SPLASH_IMAGE_FILE		"splash.png"
+
+void DisplaySplashImage(void);
+
+#endif
Binary file os/macosx/splash.png has changed
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sound/cocoa_s.c	Sat Dec 10 11:16:45 2005 +0000
@@ -0,0 +1,144 @@
+/******************************************************************************************
+ *                             Cocoa sound driver                                         *
+ * Known things left to do:                                                               *
+ * - Might need to do endian checking for it to work on both ppc and x86                  *
+ ******************************************************************************************/
+
+#ifdef WITH_COCOA
+
+#include <AudioUnit/AudioUnit.h>
+
+/* Name conflict */
+#define Rect		OTTDRect
+#define Point		OTTDPoint
+#define WindowClass	OTTDWindowClass
+/* 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 "../driver.h"
+#include "../mixer.h"
+#include "../sdl.h"
+
+#include "cocoa_s.h"
+
+#undef WindowClass
+#undef Point
+#undef Rect
+
+
+static AudioUnit _outputAudioUnit;
+
+/* The CoreAudio callback */
+static OSStatus audioCallback (void *inRefCon,  AudioUnitRenderActionFlags inActionFlags, const AudioTimeStamp *inTimeStamp, UInt32 inBusNumber, AudioBuffer *ioData)
+{
+	MxMixSamples(_mixer, ioData->mData, ioData->mDataByteSize / 4);
+
+	return noErr;
+}
+
+
+static const char *CocoaSoundStart(const char * const *parm)
+{
+	Component comp;
+	ComponentDescription desc;
+	struct AudioUnitInputCallback callback;
+	AudioStreamBasicDescription requestedDesc;
+
+	DEBUG(driver, 1)("cocoa_s: CocoaSoundStart");
+
+	/* Setup a AudioStreamBasicDescription with the requested format */
+	requestedDesc.mFormatID = kAudioFormatLinearPCM;
+	requestedDesc.mFormatFlags = kLinearPCMFormatFlagIsPacked;
+	requestedDesc.mChannelsPerFrame = 2;
+	requestedDesc.mSampleRate = GetDriverParamInt(parm, "hz", 11025);
+
+	requestedDesc.mBitsPerChannel = 16;
+	requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsSignedInteger;
+#if 1		// Big endian?
+	requestedDesc.mFormatFlags |= kLinearPCMFormatFlagIsBigEndian;
+#endif
+
+	requestedDesc.mFramesPerPacket = 1;
+	requestedDesc.mBytesPerFrame = requestedDesc.mBitsPerChannel * requestedDesc.mChannelsPerFrame / 8;
+	requestedDesc.mBytesPerPacket = requestedDesc.mBytesPerFrame * requestedDesc.mFramesPerPacket;
+
+
+	/* Locate the default output audio unit */
+	desc.componentType = kAudioUnitComponentType;
+	desc.componentSubType = kAudioUnitSubType_Output;
+	desc.componentManufacturer = kAudioUnitID_DefaultOutput;
+	desc.componentFlags = 0;
+	desc.componentFlagsMask = 0;
+
+	comp = FindNextComponent (NULL, &desc);
+	if (comp == NULL)
+		return "cocoa_s: Failed to start CoreAudio: FindNextComponent returned NULL";
+
+	/* Open & initialize the default output audio unit */
+	if(OpenAComponent(comp, &_outputAudioUnit) != noErr)
+		return "cocoa_s: Failed to start CoreAudio: OpenAComponent";
+
+	if(AudioUnitInitialize(_outputAudioUnit) != noErr)
+		return "cocoa_s: Failed to start CoreAudio: AudioUnitInitialize";
+
+	/* Set the input format of the audio unit. */
+	if(AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Input, 0, &requestedDesc, sizeof (requestedDesc)) != noErr)
+		return "cocoa_s: Failed to start CoreAudio:  AudioUnitSetProperty (kAudioUnitProperty_StreamFormat)";
+
+	/* Set the audio callback */
+	callback.inputProc = audioCallback;
+	callback.inputProcRefCon = NULL;
+	if(AudioUnitSetProperty(_outputAudioUnit,  kAudioUnitProperty_SetInputCallback,  kAudioUnitScope_Input,  0, &callback,  sizeof(callback)) != noErr)
+		return "cocoa_s: Failed to start CoreAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback)";
+
+	/* Finally, start processing of the audio unit */
+	if(AudioOutputUnitStart (_outputAudioUnit) != noErr)
+		return "cocoa_s: Failed to start CoreAudio: AudioOutputUnitStart";
+
+	/* We're running! */
+	return NULL;
+}
+
+
+static void CocoaSoundStop(void)
+{
+	struct AudioUnitInputCallback callback;
+
+	DEBUG(driver, 1)("cocoa_s: CocoaSoundStop");
+
+	/* stop processing the audio unit */
+	if(AudioOutputUnitStop(_outputAudioUnit) != noErr) {
+		DEBUG(driver, 1)("cocoa_s: Core_CloseAudio: AudioOutputUnitStop failed");
+		return;
+	}
+
+	/* Remove the input callback */
+	callback.inputProc = 0;
+	callback.inputProcRefCon = 0;
+	if(AudioUnitSetProperty(_outputAudioUnit, kAudioUnitProperty_SetInputCallback, kAudioUnitScope_Input, 0, &callback, sizeof(callback)) != noErr) {
+		DEBUG(driver, 1)("cocoa_s: Core_CloseAudio: AudioUnitSetProperty (kAudioUnitProperty_SetInputCallback) failed");
+		return;
+	}
+
+	if (CloseComponent(_outputAudioUnit) != noErr) {
+		DEBUG(driver, 1)("cocoa_s: Core_CloseAudio: CloseComponent failed");
+		return;
+	}
+}
+
+
+const HalSoundDriver _cocoa_sound_driver = {
+	CocoaSoundStart,
+	CocoaSoundStop,
+};
+
+#endif /* WITH_COCOA */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/sound/cocoa_s.h	Sat Dec 10 11:16:45 2005 +0000
@@ -0,0 +1,9 @@
+
+#ifndef SOUND_COCOA_H
+#define SOUND_COCOA_H
+
+#include "../hal.h"
+
+const HalSoundDriver _cocoa_sound_driver;
+
+#endif
--- a/unix.c	Sat Dec 10 07:29:31 2005 +0000
+++ b/unix.c	Sat Dec 10 11:16:45 2005 +0000
@@ -444,8 +444,26 @@
 #endif
 }
 
+#ifdef WITH_COCOA
+void cocoaSetWorkingDirectory(void);
+void cocoaSetupAutoreleasePool(void);
+void cocoaReleaseAutoreleasePool(void);
+#endif
+
 int CDECL main(int argc, char* argv[])
 {
+	int ret;
+
+#ifdef WITH_COCOA
+	cocoaSetupAutoreleasePool();
+    /* This is passed if we are launched by double-clicking */
+	if(argc >= 2 && strncmp (argv[1], "-psn", 4) == 0) {
+		argv[1] = NULL;
+		argc = 1;
+		cocoaSetWorkingDirectory();
+	}
+#endif
+
 	// change the working directory to enable doubleclicking in UIs
 #if defined(__BEOS__) || defined(__linux__)
 	ChangeWorkingDirectory(argv[0]);
@@ -456,7 +474,13 @@
 
 	signal(SIGPIPE, SIG_IGN);
 
-	return ttd_main(argc, argv);
+	ret = ttd_main(argc, argv);
+
+#ifdef WITH_COCOA
+	cocoaReleaseAutoreleasePool();
+#endif
+
+	return ret;
 }
 
 void DeterminePaths(void)
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/video/cocoa_v.h	Sat Dec 10 11:16:45 2005 +0000
@@ -0,0 +1,130 @@
+
+#ifndef VIDEO_COCOA_H
+#define VIDEO_COCOA_H
+
+#include "../hal.h"
+
+extern const HalVideoDriver _cocoa_video_driver;
+
+/* From SDL_QuartzKeys.h */
+/* These are the Macintosh key scancode constants -- from Inside Macintosh */
+
+#define QZ_ESCAPE		0x35
+#define QZ_F1			0x7A
+#define QZ_F2			0x78
+#define QZ_F3			0x63
+#define QZ_F4			0x76
+#define QZ_F5			0x60
+#define QZ_F6			0x61
+#define QZ_F7			0x62
+#define QZ_F8			0x64
+#define QZ_F9			0x65
+#define QZ_F10			0x6D
+#define QZ_F11			0x67
+#define QZ_F12			0x6F
+#define QZ_PRINT		0x69
+#define QZ_SCROLLOCK    0x6B
+#define QZ_PAUSE		0x71
+#define QZ_POWER		0x7F
+#define QZ_BACKQUOTE	0x32
+#define QZ_1			0x12
+#define QZ_2			0x13
+#define QZ_3			0x14
+#define QZ_4			0x15
+#define QZ_5			0x17
+#define QZ_6			0x16
+#define QZ_7			0x1A
+#define QZ_8			0x1C
+#define QZ_9			0x19
+#define QZ_0			0x1D
+#define QZ_MINUS		0x1B
+#define QZ_EQUALS		0x18
+#define QZ_BACKSPACE	0x33
+#define QZ_INSERT		0x72
+#define QZ_HOME			0x73
+#define QZ_PAGEUP		0x74
+#define QZ_NUMLOCK		0x47
+#define QZ_KP_EQUALS	0x51
+#define QZ_KP_DIVIDE	0x4B
+#define QZ_KP_MULTIPLY	0x43
+#define QZ_TAB			0x30
+#define QZ_q			0x0C
+#define QZ_w			0x0D
+#define QZ_e			0x0E
+#define QZ_r			0x0F
+#define QZ_t			0x11
+#define QZ_y			0x10
+#define QZ_u			0x20
+#define QZ_i			0x22
+#define QZ_o			0x1F
+#define QZ_p			0x23
+#define QZ_LEFTBRACKET	0x21
+#define QZ_RIGHTBRACKET	0x1E
+#define QZ_BACKSLASH	0x2A
+#define QZ_DELETE		0x75
+#define QZ_END			0x77
+#define QZ_PAGEDOWN		0x79
+#define QZ_KP7			0x59
+#define QZ_KP8			0x5B
+#define QZ_KP9			0x5C
+#define QZ_KP_MINUS		0x4E
+#define QZ_CAPSLOCK		0x39
+#define QZ_a			0x00
+#define QZ_s			0x01
+#define QZ_d			0x02
+#define QZ_f			0x03
+#define QZ_g			0x05
+#define QZ_h			0x04
+#define QZ_j			0x26
+#define QZ_k			0x28
+#define QZ_l			0x25
+#define QZ_SEMICOLON	0x29
+#define QZ_QUOTE		0x27
+#define QZ_RETURN		0x24
+#define QZ_KP4			0x56
+#define QZ_KP5			0x57
+#define QZ_KP6			0x58
+#define QZ_KP_PLUS		0x45
+#define QZ_LSHIFT		0x38
+#define QZ_z			0x06
+#define QZ_x			0x07
+#define QZ_c			0x08
+#define QZ_v			0x09
+#define QZ_b			0x0B
+#define QZ_n			0x2D
+#define QZ_m			0x2E
+#define QZ_COMMA		0x2B
+#define QZ_PERIOD		0x2F
+#define QZ_SLASH		0x2C
+#if 1	/* Panther now defines right side keys */
+#define QZ_RSHIFT		0x3C
+#endif
+#define QZ_UP			0x7E
+#define QZ_KP1			0x53
+#define QZ_KP2			0x54
+#define QZ_KP3			0x55
+#define QZ_KP_ENTER		0x4C
+#define QZ_LCTRL		0x3B
+#define QZ_LALT			0x3A
+#define QZ_LMETA		0x37
+#define QZ_SPACE		0x31
+#if 1	/* Panther now defines right side keys */
+#define QZ_RMETA		0x36
+#define QZ_RALT			0x3D
+#define QZ_RCTRL		0x3E
+#endif
+#define QZ_LEFT			0x7B
+#define QZ_DOWN			0x7D
+#define QZ_RIGHT		0x7C
+#define QZ_KP0			0x52
+#define QZ_KP_PERIOD	0x41
+
+/* Wierd, these keys are on my iBook under MacOS X */
+#define QZ_IBOOK_ENTER		0x34
+#define QZ_IBOOK_LEFT		0x3B
+#define QZ_IBOOK_RIGHT		0x3C
+#define QZ_IBOOK_DOWN		0x3D
+#define QZ_IBOOK_UP			0x3E
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/video/cocoa_v.m	Sat Dec 10 11:16:45 2005 +0000
@@ -0,0 +1,2113 @@
+/******************************************************************************************
+ *                             Cocoa video driver                                         *
+ * Known things left to do:                                                               *
+ *  Nothing at the moment.                                                                *
+ ******************************************************************************************/
+
+#ifdef WITH_COCOA
+
+#import <Cocoa/Cocoa.h>
+#import <sys/time.h> /* gettimeofday */
+#import <sys/param.h> /* for MAXPATHLEN */
+#import <unistd.h>
+
+/* Portions of CPS.h */
+typedef struct CPSProcessSerNum
+{
+	UInt32          lo;
+	UInt32          hi;
+} CPSProcessSerNum;
+
+extern OSErr    CPSGetCurrentProcess( CPSProcessSerNum *psn);
+extern OSErr    CPSEnableForegroundOperation( CPSProcessSerNum *psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
+extern OSErr    CPSSetFrontProcess( CPSProcessSerNum *psn);
+
+/* From Menus.h (according to Xcode Developer Documentation) */
+extern void ShowMenuBar(void);
+extern void HideMenuBar(void);
+
+/* Disables a warning. This is needed since the method exists but has been dropped from the header, supposedly as of 10.4. */
+@interface NSApplication(NSAppleMenu)
+- (void)setAppleMenu:(NSMenu *)menu;
+@end
+
+
+/* Name conflict */
+#define Rect		OTTDRect
+#define Point		OTTDPoint
+/* Defined in ppc/param.h or i386/param.h included from sys/param.h */
+#undef ALIGN
+/* 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 "../functions.h"
+#include "../gfx.h"
+#include "../macros.h"
+#include "../sdl.h"
+#include "../window.h"
+#include "../network.h"
+#include "../variables.h"
+#include "../os/macosx/splash.h"
+
+#include "cocoa_v.h"
+
+#undef Point
+#undef Rect
+
+
+/* Subclass of NSWindow to fix genie effect and support resize events  */
+@interface OTTD_QuartzWindow : NSWindow
+- (void)miniaturize:(id)sender;
+- (void)display;
+- (void)setFrame:(NSRect)frameRect display:(BOOL)flag;
+- (void)appDidHide:(NSNotification*)note;
+- (void)appWillUnhide:(NSNotification*)note;
+- (void)appDidUnhide:(NSNotification*)note;
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag;
+@end
+
+/* Delegate for our NSWindow to send ask for quit on close */
+@interface OTTD_QuartzWindowDelegate : NSObject
+- (BOOL)windowShouldClose:(id)sender;
+@end
+
+@interface OTTDMain : NSObject
+@end
+
+
+/*
+		Structure for rez switch gamma fades
+		We can hide the monitor flicker by setting the gamma tables to 0
+*/
+#define QZ_GAMMA_TABLE_SIZE 256
+
+typedef struct {
+	CGGammaValue red[QZ_GAMMA_TABLE_SIZE];
+	CGGammaValue green[QZ_GAMMA_TABLE_SIZE];
+	CGGammaValue blue[QZ_GAMMA_TABLE_SIZE];
+} OTTD_QuartzGammaTable;
+
+/*
+    Add methods to get at private members of NSScreen.
+    Since there is a bug in Apple's screen switching code
+    that does not update this variable when switching
+    to fullscreen, we'll set it manually (but only for the
+    main screen).
+*/
+@interface NSScreen (NSScreenAccess)
+		- (void) setFrame:(NSRect)frame;
+@end
+
+@implementation NSScreen (NSScreenAccess)
+- (void) setFrame:(NSRect)frame;
+{
+	_frame = frame;
+}
+@end
+
+
+static void QZ_Draw(void);
+static void QZ_UnsetVideoMode (void);
+static void QZ_UpdatePalette(uint start, uint count);
+static void QZ_WarpCursor (int x, int y);
+void QZ_ShowMouse (void);
+void QZ_HideMouse (void);
+static void CocoaVideoFullScreen(bool full_screen);
+
+
+static NSAutoreleasePool *_ottd_autorelease_pool;
+static OTTDMain *_ottd_main;
+
+
+static struct CocoaVideoData {
+	bool isset;
+	bool issetting;
+
+	CGDirectDisplayID  display_id;         /* 0 == main display (only support single display) */
+	CFDictionaryRef    mode;               /* current mode of the display */
+	CFDictionaryRef    save_mode;          /* original mode of the display */
+	CFArrayRef         mode_list;          /* list of available fullscreen modes */
+	CGDirectPaletteRef palette;            /* palette of an 8-bit display */
+
+	uint32 device_width;
+	uint32 device_height;
+	uint32 device_bpp;
+
+	void *realpixels;
+	uint8 *pixels;
+	uint32 width;
+	uint32 height;
+	uint32 pitch;
+	bool fullscreen;
+
+	unsigned int current_mods;
+	bool tab_is_down;
+	bool emulating_right_button;
+
+	bool cursor_visible;
+	bool active;
+
+#ifdef _DEBUG
+	uint32 tEvent;
+#endif
+
+	OTTD_QuartzWindow *window;
+	NSQuickDrawView *qdview;
+
+#define MAX_DIRTY_RECTS 100
+	OTTDRect dirty_rects[MAX_DIRTY_RECTS];
+	int num_dirty_rects;
+
+	uint16 palette16[256];
+	uint32 palette32[256];
+} _cocoa_video_data;
+
+
+
+
+
+
+/******************************************************************************************
+ *                             Game loop and accessories                                  *
+ ******************************************************************************************/
+
+static uint32 GetTick(void)
+{
+	struct timeval tim;
+
+	gettimeofday(&tim, NULL);
+	return tim.tv_usec / 1000 + tim.tv_sec * 1000;
+}
+
+static void QZ_CheckPaletteAnim(void)
+{
+	if (_pal_last_dirty != -1) {
+		QZ_UpdatePalette(_pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1);
+		_pal_last_dirty = -1;
+	}
+}
+
+
+
+extern void DoExitSave(void);
+
+static void QZ_AskQuit(void)
+{
+	if (_game_mode == GM_MENU) { // do not ask to quit on the main screen
+		_exit_game = true;
+	} else if (_patches.autosave_on_exit) {
+		DoExitSave();
+		_exit_game = true;
+	} else
+		AskExitGame();
+}
+
+
+
+typedef struct VkMapping {
+	unsigned short vk_from;
+	byte map_to;
+} VkMapping;
+
+#define AS(x, z) {x, z}
+
+static const VkMapping _vk_mapping[] = {
+	AS(10,				WKC_BACKQUOTE),		// key left of '1'
+	// Pageup stuff + up/down
+	//AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN),  <==== Does this include HOME/END?
+	AS(QZ_PAGEUP,		WKC_PAGEUP),
+	AS(QZ_PAGEDOWN,		WKC_PAGEDOWN),
+
+	AS(QZ_UP,			WKC_UP),
+	AS(QZ_DOWN,			WKC_DOWN),
+	AS(QZ_LEFT,			WKC_LEFT),
+	AS(QZ_RIGHT,		WKC_RIGHT),
+
+	AS(QZ_HOME,			WKC_HOME),
+	AS(QZ_END,			WKC_END),
+
+	AS(QZ_INSERT,		WKC_INSERT),
+	AS(QZ_DELETE,		WKC_DELETE),
+
+	// Letters. QZ_[a-z] is not in numerical order so we can't use AM(...)
+	AS(QZ_a,			'A'),
+	AS(QZ_b,			'B'),
+	AS(QZ_c,			'C'),
+	AS(QZ_d,			'D'),
+	AS(QZ_e,			'E'),
+	AS(QZ_f,			'F'),
+	AS(QZ_g,			'G'),
+	AS(QZ_h,			'H'),
+	AS(QZ_i,			'I'),
+	AS(QZ_j,			'J'),
+	AS(QZ_k,			'K'),
+	AS(QZ_l,			'L'),
+	AS(QZ_m,			'M'),
+	AS(QZ_n,			'N'),
+	AS(QZ_o,			'O'),
+	AS(QZ_p,			'P'),
+	AS(QZ_q,			'Q'),
+	AS(QZ_r,			'R'),
+	AS(QZ_s,			'S'),
+	AS(QZ_t,			'T'),
+	AS(QZ_u,			'U'),
+	AS(QZ_v,			'V'),
+	AS(QZ_w,			'W'),
+	AS(QZ_x,			'X'),
+	AS(QZ_y,			'Y'),
+	AS(QZ_z,			'Z'),
+	// Same thing for digits
+	AS(QZ_0,			'0'),
+	AS(QZ_1,			'1'),
+	AS(QZ_2,			'2'),
+	AS(QZ_3,			'3'),
+	AS(QZ_4,			'4'),
+	AS(QZ_5,			'5'),
+	AS(QZ_6,			'6'),
+	AS(QZ_7,			'7'),
+	AS(QZ_8,			'8'),
+	AS(QZ_9,			'9'),
+
+	AS(QZ_ESCAPE,		WKC_ESC),
+	AS(QZ_PAUSE,  		WKC_PAUSE),
+	AS(QZ_BACKSPACE,	WKC_BACKSPACE),
+
+	AS(QZ_SPACE,		WKC_SPACE),
+	AS(QZ_RETURN,		WKC_RETURN),
+	AS(QZ_TAB,			WKC_TAB),
+
+	// Function keys
+	AS(QZ_F1,			WKC_F1),
+	AS(QZ_F2,			WKC_F2),
+	AS(QZ_F3,			WKC_F3),
+	AS(QZ_F4,			WKC_F4),
+	AS(QZ_F5,			WKC_F5),
+	AS(QZ_F6,			WKC_F6),
+	AS(QZ_F7,			WKC_F7),
+	AS(QZ_F8,			WKC_F8),
+	AS(QZ_F9,			WKC_F9),
+	AS(QZ_F10,			WKC_F10),
+	AS(QZ_F11,			WKC_F11),
+	AS(QZ_F12,			WKC_F12),
+
+	// Numeric part.
+	AS(QZ_KP0,				WKC_NUM_0),
+	AS(QZ_KP1,				WKC_NUM_1),
+	AS(QZ_KP2,				WKC_NUM_2),
+	AS(QZ_KP3,				WKC_NUM_3),
+	AS(QZ_KP4,				WKC_NUM_4),
+	AS(QZ_KP5,				WKC_NUM_5),
+	AS(QZ_KP6,				WKC_NUM_6),
+	AS(QZ_KP7,				WKC_NUM_7),
+	AS(QZ_KP8,				WKC_NUM_8),
+	AS(QZ_KP9,				WKC_NUM_9),
+	AS(QZ_KP_DIVIDE,		WKC_NUM_DIV),
+	AS(QZ_KP_MULTIPLY,		WKC_NUM_MUL),
+	AS(QZ_KP_MINUS,			WKC_NUM_MINUS),
+	AS(QZ_KP_PLUS,			WKC_NUM_PLUS),
+	AS(QZ_KP_ENTER,			WKC_NUM_ENTER),
+	AS(QZ_KP_PERIOD,		WKC_NUM_DECIMAL)
+};
+
+
+static uint32 QZ_MapKey(unsigned short sym)
+{
+	const VkMapping *map;
+	uint32 key = 0;
+
+	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
+		if(sym == map->vk_from) {
+			key = map->map_to;
+			break;
+		}
+	}
+
+	if(_cocoa_video_data.current_mods & NSShiftKeyMask)
+		key|= WKC_SHIFT;
+	if(_cocoa_video_data.current_mods & NSControlKeyMask)
+		key|= WKC_CTRL;
+	if(_cocoa_video_data.current_mods & NSAlternateKeyMask)
+		key|= WKC_ALT;
+	if(_cocoa_video_data.current_mods & NSCommandKeyMask)
+		key|= WKC_META;
+
+	return key << 16;
+}
+
+static void QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL down)
+{
+	switch(keycode)
+	{
+		case QZ_UP:
+			SB(_dirkeys, 1, 1, down);
+			break;
+		case QZ_DOWN:
+			SB(_dirkeys, 3, 1, down);
+			break;
+		case QZ_LEFT:
+			SB(_dirkeys, 0, 1, down);
+			break;
+		case QZ_RIGHT:
+			SB(_dirkeys, 2, 1, down);
+			break;
+		case QZ_TAB:
+			_cocoa_video_data.tab_is_down = down;
+			break;
+		case QZ_RETURN:
+		case QZ_f:
+			if(down && ((_cocoa_video_data.current_mods & NSControlKeyMask) || (_cocoa_video_data.current_mods & NSCommandKeyMask)))
+				CocoaVideoFullScreen(!_fullscreen);
+
+	}
+
+	if(down) {
+		_pressed_key = QZ_MapKey(keycode) | unicode;
+		DEBUG(driver, 2)("cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, _pressed_key);
+	} else {
+		DEBUG(driver, 2)("cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode);
+	}
+}
+
+static void QZ_DoUnsidedModifiers (unsigned int newMods) {
+
+	const int mapping[] = { QZ_CAPSLOCK, QZ_LSHIFT, QZ_LCTRL, QZ_LALT, QZ_LMETA };
+
+	int i;
+	int bit;
+
+	if (_cocoa_video_data.current_mods == newMods)
+		return;
+
+	/* Iterate through the bits, testing each against the current modifiers */
+	for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
+		unsigned int currentMask, newMask;
+
+		currentMask = _cocoa_video_data.current_mods & bit;
+		newMask     = newMods & bit;
+
+		if ( currentMask && currentMask != newMask ) {     /* modifier up event */
+			/* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */
+			if (bit == NSAlphaShiftKeyMask)
+				QZ_KeyEvent(mapping[i], 0, YES);
+			QZ_KeyEvent(mapping[i], 0, NO);
+		}
+		else if ( newMask && currentMask != newMask ) {     /* modifier down event */
+			QZ_KeyEvent(mapping[i], 0, YES);
+			/* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */
+			if (bit == NSAlphaShiftKeyMask)
+				QZ_KeyEvent(mapping[i], 0, NO);
+		}
+	}
+
+	_cocoa_video_data.current_mods = newMods;
+}
+
+static void QZ_MouseMovedEvent(int x, int y)
+{
+	int dx, dy;
+
+	if (_cursor.fix_at) {
+		dx = x - _cursor.pos.x;
+		dy = y - _cursor.pos.y;
+		if (dx != 0 || dy != 0) {
+			_cursor.delta.x += dx;
+			_cursor.delta.y += dy;
+
+			QZ_WarpCursor(_cursor.pos.x, _cursor.pos.y);
+		}
+	} else {
+		_cursor.delta.x = x - _cursor.pos.x;
+		_cursor.delta.y = y - _cursor.pos.y;
+		_cursor.pos.x = x;
+		_cursor.pos.y = y;
+		_cursor.dirty = true;
+	}
+}
+
+void QZ_MouseButtonEvent( int button, BOOL down )
+{
+	switch(button) {
+		case 0:
+			if(down) {
+				_left_button_down = true;
+			} else {
+				_left_button_down = false;
+				_left_button_clicked = false;
+			}
+			break;
+		case 1:
+			if(down) {
+				_right_button_down = true;
+				_right_button_clicked = true;
+			} else {
+				_right_button_down = false;
+			}
+			break;
+	}
+}
+
+
+static inline NSPoint QZ_GetMouseLocation(NSEvent *event)
+{
+	NSPoint pt;
+
+	if(_cocoa_video_data.fullscreen) {
+		pt = [ NSEvent mouseLocation ];
+		pt.y = _cocoa_video_data.height - pt.y;
+	} else {
+		pt = [event locationInWindow];
+		pt = [_cocoa_video_data.qdview convertPoint:pt fromView:nil];
+	}
+
+	return pt;
+}
+
+static bool QZ_MouseIsInsideView(NSPoint *pt)
+{
+	if(_cocoa_video_data.fullscreen)
+		return pt->x >= 0 && pt->y >= 0 && pt->x < _cocoa_video_data.width && pt->y < _cocoa_video_data.height;
+	else
+		return [ _cocoa_video_data.qdview mouse:*pt inRect:[ _cocoa_video_data.qdview bounds ] ];
+}
+
+
+static bool QZ_PollEvent(void)
+{
+	NSEvent *event;
+	NSPoint pt;
+	NSString *chars;
+#ifdef _DEBUG
+	uint32 et0, et;
+#endif
+
+#ifdef _DEBUG
+	et0 = GetTick();
+#endif
+	event = [ NSApp nextEventMatchingMask:NSAnyEventMask
+			untilDate: [ NSDate distantPast ]
+			inMode: NSDefaultRunLoopMode dequeue:YES ];
+#ifdef _DEBUG
+	et = GetTick();
+	_cocoa_video_data.tEvent+= et - et0;
+#endif
+
+	if(event == nil)
+		return false;
+	if(!_cocoa_video_data.active) {
+		QZ_ShowMouse();
+		[NSApp sendEvent:event];
+		return true;
+	}
+
+	QZ_DoUnsidedModifiers( [ event modifierFlags ] );
+
+	switch ([event type]) {
+		case NSMouseMoved:
+		case NSOtherMouseDragged:
+		case NSRightMouseDragged:
+		case NSLeftMouseDragged:
+			pt = QZ_GetMouseLocation(event);
+			if(!QZ_MouseIsInsideView(&pt) && !_cocoa_video_data.emulating_right_button) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int) pt.x, (int) pt.y);
+			break;
+
+		case NSLeftMouseDown:
+			if(!([ event modifierFlags ] & NSCommandKeyMask) || !QZ_MouseIsInsideView(&pt))
+				[NSApp sendEvent:event];
+
+			pt = QZ_GetMouseLocation(event);
+			if(!QZ_MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int) pt.x, (int) pt.y);
+
+			/* Right mouse button emulation */
+			if([ event modifierFlags ] & NSCommandKeyMask) {
+				_cocoa_video_data.emulating_right_button = true;
+				QZ_MouseButtonEvent(1, YES);
+			} else
+				QZ_MouseButtonEvent(0, YES);
+			break;
+		case NSLeftMouseUp:
+			[NSApp sendEvent:event];
+
+			pt = QZ_GetMouseLocation(event);
+			if(!QZ_MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int) pt.x, (int) pt.y);
+
+			/* Right mouse button emulation */
+			if(_cocoa_video_data.emulating_right_button) {
+				_cocoa_video_data.emulating_right_button = false;
+				QZ_MouseButtonEvent(1, NO);
+			} else
+				QZ_MouseButtonEvent(0, NO);
+			break;
+
+		case NSRightMouseDown:
+			pt = QZ_GetMouseLocation(event);
+			if(!QZ_MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int) pt.x, (int) pt.y);
+			QZ_MouseButtonEvent(1, YES);
+			break;
+		case NSRightMouseUp:
+			pt = QZ_GetMouseLocation(event);
+			if(!QZ_MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int) pt.x, (int) pt.y);
+			QZ_MouseButtonEvent(1, NO);
+			break;
+
+		/* This is not needed since openttd currently only use two buttons */
+		/*
+		case NSOtherMouseDown:
+			pt = QZ_GetMouseLocation(event);
+			if(!QZ_MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int) pt.x, (int) pt.y);
+			QZ_MouseButtonEvent([ event buttonNumber ], YES);
+			break;
+		case NSOtherMouseUp:
+			pt = QZ_GetMouseLocation(event);
+			if(!QZ_MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int) pt.x, (int) pt.y);
+			QZ_MouseButtonEvent([ event buttonNumber ], NO);
+			break;
+		*/
+		case NSKeyDown:
+			/* Quit, hide and minimize */
+			switch([event keyCode]) {
+				case QZ_q:
+				case QZ_h:
+				case QZ_m:
+					if([ event modifierFlags ] & NSCommandKeyMask)
+						[NSApp sendEvent:event];
+					break;
+			}
+
+			chars = [ event characters ];
+			QZ_KeyEvent([event keyCode], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, YES);
+			break;
+
+		case NSKeyUp:
+			/* Quit, hide and minimize */
+			switch([event keyCode]) {
+				case QZ_q:
+				case QZ_h:
+				case QZ_m:
+					if([ event modifierFlags ] & NSCommandKeyMask)
+						[NSApp sendEvent:event];
+					break;
+			}
+
+			chars = [ event characters ];
+			QZ_KeyEvent([event keyCode], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, NO);
+			break;
+
+		case NSScrollWheel:
+			if ( [ event deltaX ] > 0.0 || [ event deltaY ] > 0.0 ) /* Scroll up */
+				_cursor.wheel--;
+			else /* Scroll down */
+				_cursor.wheel++;
+			break;
+
+		default:
+			[NSApp sendEvent:event];
+	}
+
+	return true;
+}
+
+
+
+
+static void QZ_GameLoop(void)
+{
+	uint32 next_tick = GetTick() + 30;
+	uint32 cur_ticks;
+	uint32 pal_tick = 0;
+#ifdef _DEBUG
+	uint32 et0, et, st0, st;
+#endif
+	int i;
+
+	DEBUG(driver, 1)("cocoa_v: QZ_GameLoop");
+
+#ifdef _DEBUG
+	et0 = GetTick();
+	st = 0;
+#endif
+
+	_screen.dst_ptr = _cocoa_video_data.pixels;
+	DisplaySplashImage();
+	QZ_CheckPaletteAnim();
+	QZ_Draw();
+	CSleep(1);
+
+	for(i = 0; i < 2; i++)
+		GameLoop();
+
+	_screen.dst_ptr = _cocoa_video_data.pixels;
+	UpdateWindows();
+	QZ_CheckPaletteAnim();
+	QZ_Draw();
+	CSleep(1);
+
+	while(1) {
+		InteractiveRandom(); // randomness
+
+		while (QZ_PollEvent()) {}
+
+		if (_exit_game)
+			break;
+
+#if defined(_DEBUG)
+		if(_cocoa_video_data.current_mods & NSShiftKeyMask)
+#else
+		if (_cocoa_video_data.tab_is_down)
+#endif
+		{
+			if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
+		} else if (_fast_forward & 2) {
+			_fast_forward = 0;
+		}
+
+		cur_ticks = GetTick();
+		if ((_fast_forward && !_pause) || cur_ticks > next_tick)
+			next_tick = cur_ticks;
+
+		if (cur_ticks == next_tick) {
+			next_tick += 30;
+
+			_ctrl_pressed = !!(_cocoa_video_data.current_mods & NSControlKeyMask);
+			_shift_pressed = !!(_cocoa_video_data.current_mods & NSShiftKeyMask);
+#ifdef _DEBUG
+			_dbg_screen_rect = !!(_cocoa_video_data.current_mods & NSAlphaShiftKeyMask);
+#endif
+
+			GameLoop();
+
+			_screen.dst_ptr = _cocoa_video_data.pixels;
+			UpdateWindows();
+			if (++pal_tick > 4) {
+				QZ_CheckPaletteAnim();
+				pal_tick = 1;
+			}
+			QZ_Draw();
+		} else {
+#ifdef _DEBUG
+			st0 = GetTick();
+#endif
+			CSleep(1);
+#ifdef _DEBUG
+			st+= GetTick() - st0;
+#endif
+			_screen.dst_ptr = _cocoa_video_data.pixels;
+			DrawTextMessage();
+			DrawMouseCursor();
+			QZ_Draw();
+		}
+	}
+
+#ifdef _DEBUG
+	et = GetTick();
+
+	DEBUG(driver, 1)("cocoa_v: nextEventMatchingMask took %i ms total", _cocoa_video_data.tEvent);
+	DEBUG(driver, 1)("cocoa_v: game loop took %i ms total (%i ms without sleep)", et - et0, (et - et0) - st);
+	DEBUG(driver, 1)("cocoa_v: (nextEventMatchingMask total)/(game loop total) is %f%%", (double) _cocoa_video_data.tEvent / (double) (et - et0) * 100);
+	DEBUG(driver, 1)("cocoa_v: (nextEventMatchingMask total)/(game loop without sleep total) is %f%%", (double) _cocoa_video_data.tEvent / (double) ((et - et0) - st) * 100);
+#endif
+}
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************************
+ *                             Windowed mode                                              *
+ ******************************************************************************************/
+
+/*
+    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.
+*/
+static void QZ_SetPortAlphaOpaque (void) {
+	if (_cocoa_video_data.device_bpp == 32) {
+		uint32    *pixels = (uint32*) _cocoa_video_data.realpixels;
+		uint32    rowPixels = _cocoa_video_data.pitch / 4;
+		uint32    i, j;
+
+		for (i = 0; i < _cocoa_video_data.height; i++)
+			for (j = 0; j < _cocoa_video_data.width; j++) {
+			pixels[ (i * rowPixels) + j ] |= 0xFF000000;
+		}
+	}
+}
+
+
+@implementation OTTD_QuartzWindow
+
+/* we override these methods to fix the miniaturize animation/dock icon bug */
+- (void)miniaturize:(id)sender
+{
+	/* make the alpha channel opaque so anim won't have holes in it */
+	QZ_SetPortAlphaOpaque ();
+
+	/* window is hidden now */
+	_cocoa_video_data.active = false;
+
+	QZ_ShowMouse();
+
+	[ super miniaturize:sender ];
+}
+
+- (void)display
+{
+    /*
+	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.
+	*/
+
+	QZ_SetPortAlphaOpaque ();
+
+	/* save current visible surface */
+	[ self cacheImageInRect:[ _cocoa_video_data.qdview frame ] ];
+
+	/* let the window manager redraw controls, border, etc */
+	[ super display ];
+
+	/* restore visible surface */
+	[ self restoreCachedImage ];
+
+	/* window is visible again */
+	_cocoa_video_data.active = true;
+}
+
+- (void)setFrame:(NSRect)frameRect display:(BOOL)flag
+{
+	NSRect newViewFrame;
+	CGrafPtr thePort;
+
+	[ super setFrame:frameRect display:flag ];
+
+	/* Don't do anything if the window is currently beign created */
+    if(_cocoa_video_data.issetting)
+    	return;
+
+	if(_cocoa_video_data.window == nil)
+		return;
+
+	newViewFrame = [ _cocoa_video_data.qdview frame ];
+
+	/* Update the pixels and pitch */
+	thePort = [ _cocoa_video_data.qdview qdPort ];
+	LockPortBits ( thePort );
+
+	_cocoa_video_data.realpixels = GetPixBaseAddr ( GetPortPixMap ( thePort ) );
+	_cocoa_video_data.pitch  = GetPixRowBytes ( GetPortPixMap ( thePort ) );
+
+	/*
+		_cocoa_video_data.realpixels now points to the window's pixels
+		We want it to point to the *view's* pixels
+	*/
+	{
+		int vOffset = [ _cocoa_video_data.window frame ].size.height - newViewFrame.size.height - newViewFrame.origin.y;
+		int hOffset = newViewFrame.origin.x;
+
+		_cocoa_video_data.realpixels = (uint8 *) _cocoa_video_data.realpixels + (vOffset * _cocoa_video_data.pitch) + hOffset * (_cocoa_video_data.device_bpp/8);
+	}
+
+	UnlockPortBits ( thePort );
+
+	/* Allocate new buffer */
+	if(_cocoa_video_data.pixels != NULL)
+		free(_cocoa_video_data.pixels);
+	_cocoa_video_data.pixels = (uint8 *) malloc(newViewFrame.size.width * newViewFrame.size.height);
+	assert(_cocoa_video_data.pixels != NULL);
+
+
+	/* Tell the game that the resolution changed */
+	_cocoa_video_data.width = newViewFrame.size.width;
+	_cocoa_video_data.height = newViewFrame.size.height;
+
+	_screen.width = _cocoa_video_data.width;
+	_screen.height = _cocoa_video_data.height;
+	_screen.pitch = _cocoa_video_data.width;
+
+	GameSizeChanged();
+
+	/* Redraw screen */
+	_cocoa_video_data.num_dirty_rects = MAX_DIRTY_RECTS;
+}
+
+- (void)appDidHide:(NSNotification*)note
+{
+	_cocoa_video_data.active = false;
+//	DEBUG(driver, 1)("cocoa_v: appDidHide");
+}
+
+
+- (void)appWillUnhide:(NSNotification*)note
+{
+	QZ_SetPortAlphaOpaque ();
+
+	/* save current visible surface */
+	[ self cacheImageInRect:[ _cocoa_video_data.qdview frame ] ];
+}
+
+- (void)appDidUnhide:(NSNotification*)note
+{
+	/* restore cached image, since it may not be current, post expose event too */
+	[ self restoreCachedImage ];
+
+	_cocoa_video_data.active = true;
+//	DEBUG(driver, 1)("cocoa_v: appDidUnhide");
+}
+
+
+- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag
+{
+	/* Make our window subclass receive these application notifications */
+	[ [ NSNotificationCenter defaultCenter ] addObserver:self
+	selector:@selector(appDidHide:) name:NSApplicationDidHideNotification object:NSApp ];
+
+	[ [ NSNotificationCenter defaultCenter ] addObserver:self
+	selector:@selector(appDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp ];
+
+	[ [ NSNotificationCenter defaultCenter ] addObserver:self
+	selector:@selector(appWillUnhide:) name:NSApplicationWillUnhideNotification object:NSApp ];
+
+	return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ];
+}
+
+@end
+
+@implementation OTTD_QuartzWindowDelegate
+- (BOOL)windowShouldClose:(id)sender
+{
+	QZ_AskQuit();
+
+	return NO;
+}
+
+- (void)windowDidBecomeKey:(NSNotification *)aNotification
+{
+	_cocoa_video_data.active = true;
+//	DEBUG(driver, 1)("cocoa_v: windowDidBecomeKey");
+}
+
+- (void)windowDidResignKey:(NSNotification *)aNotification
+{
+	_cocoa_video_data.active = false;
+//	DEBUG(driver, 1)("cocoa_v: windowDidResignKey");
+}
+
+- (void)windowDidBecomeMain:(NSNotification *)aNotification
+{
+	_cocoa_video_data.active = true;
+//	DEBUG(driver, 1)("cocoa_v: windowDidBecomeMain");
+}
+
+- (void)windowDidResignMain:(NSNotification *)aNotification
+{
+	_cocoa_video_data.active = false;
+//	DEBUG(driver, 1)("cocoa_v: windowDidResignMain");
+}
+
+@end
+
+
+static void QZ_UpdateWindowPalette(uint start, uint count)
+{
+	uint i;
+	uint32 clr32;
+	uint16 clr16;
+
+	switch(_cocoa_video_data.device_bpp)
+	{
+		case 32:
+			for (i = start; i < start + count; i++) {
+				clr32 = 0xff000000;
+				clr32|= ((uint32) _cur_palette[i].r) << 16;
+				clr32|= ((uint32) _cur_palette[i].g) << 8;
+				clr32|= (uint32) _cur_palette[i].b;
+				_cocoa_video_data.palette32[i] = clr32;
+			}
+			break;
+		case 16:
+			for (i = start; i < start + count; i++) {
+				clr16 = 0x0000;
+				clr16|= ((uint16) ((_cur_palette[i].r >> 3) & 0x1f)) << 10;
+				clr16|= ((uint16) ((_cur_palette[i].g >> 3) & 0x1f)) << 5;
+				clr16|= (uint16) ((_cur_palette[i].b >> 3) & 0x1f);
+				_cocoa_video_data.palette16[i] = clr16;
+			}
+			break;
+	}
+
+	_cocoa_video_data.num_dirty_rects = MAX_DIRTY_RECTS;
+}
+
+static inline void QZ_WindowBlitIndexedPixelsToView32(int left, int top, int right, int bottom)
+{
+	uint32 *trg;
+	uint8 *src;
+	int x, y;
+
+	for(y = top; y < bottom; y++) {
+		trg = ((uint32 *) _cocoa_video_data.realpixels) + y * _cocoa_video_data.pitch / 4 + left;
+		src = _cocoa_video_data.pixels + y * _cocoa_video_data.width + left;
+		for(x = left; x < right; x++, trg++, src++) {
+			*trg = _cocoa_video_data.palette32[*src];
+		}
+	}
+}
+
+static inline void QZ_WindowBlitIndexedPixelsToView16(int left, int top, int right, int bottom)
+{
+	uint16 *trg;
+	uint8 *src;
+	int x, y;
+
+	for(y = top; y < bottom; y++) {
+		trg = ((uint16 *) _cocoa_video_data.realpixels) + y * _cocoa_video_data.pitch / 2 + left;
+		src = _cocoa_video_data.pixels + y * _cocoa_video_data.width + left;
+		for(x = left; x < right; x++, trg++, src++) {
+			*trg = _cocoa_video_data.palette16[*src];
+		}
+	}
+}
+
+static inline void QZ_WindowBlitIndexedPixelsToView(int left, int top, int right, int bottom)
+{
+	switch(_cocoa_video_data.device_bpp)
+	{
+		case 32:
+			QZ_WindowBlitIndexedPixelsToView32(left, top, right, bottom);
+			break;
+		case 16:
+			QZ_WindowBlitIndexedPixelsToView16(left, top, right, bottom);
+			break;
+	}
+}
+
+static bool _resize_icon[] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
+							  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
+							  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
+							  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
+							  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
+							  0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
+							  0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0,
+							  0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
+							  0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
+							  0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+							  0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
+							  0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
+							  0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
+							  0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
+							  0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
+							  1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
+};
+
+static void QZ_DrawResizeIcon(void) {
+	int xoff, yoff, x, y;
+	uint16 *trg16;
+	uint32 *trg32;
+
+	xoff = _cocoa_video_data.width - 16;
+	yoff = _cocoa_video_data.height - 16;
+
+	for(y = 0; y < 16; y++) {
+		trg16 = ((uint16 *) _cocoa_video_data.realpixels) + (yoff + y) * _cocoa_video_data.pitch / 2 + xoff;
+		trg32 = ((uint32 *) _cocoa_video_data.realpixels) + (yoff + y) * _cocoa_video_data.pitch / 4 + xoff;
+
+		for(x = 0; x < 16; x++, trg16++, trg32++) {
+			if(!_resize_icon[y * 16 + x])
+				continue;
+
+			switch(_cocoa_video_data.device_bpp)
+			{
+				case 32:
+					*trg32 = 0xff000000;
+					break;
+				case 16:
+					*trg16 = 0x0000;
+					break;
+			}
+		}
+	}
+}
+
+static void QZ_DrawWindow (void) {
+	int i;
+	RgnHandle dirty, temp;
+
+	/* Check if we need to do anything */
+	if(_cocoa_video_data.num_dirty_rects == 0 || [ _cocoa_video_data.window isMiniaturized ])
+		return;
+
+	if(_cocoa_video_data.num_dirty_rects >= MAX_DIRTY_RECTS) {
+		_cocoa_video_data.num_dirty_rects = 1;
+		_cocoa_video_data.dirty_rects[0].left = 0;
+		_cocoa_video_data.dirty_rects[0].top = 0;
+		_cocoa_video_data.dirty_rects[0].right = _cocoa_video_data.width;
+		_cocoa_video_data.dirty_rects[0].bottom = _cocoa_video_data.height;
+	}
+
+	dirty = NewRgn ();
+	temp  = NewRgn ();
+
+	SetEmptyRgn (dirty);
+
+	/* Build the region of dirty rectangles */
+	for (i = 0; i < _cocoa_video_data.num_dirty_rects; i++) {
+		QZ_WindowBlitIndexedPixelsToView(_cocoa_video_data.dirty_rects[i].left,
+											_cocoa_video_data.dirty_rects[i].top,
+											_cocoa_video_data.dirty_rects[i].right,
+											_cocoa_video_data.dirty_rects[i].bottom);
+
+		MacSetRectRgn (temp, _cocoa_video_data.dirty_rects[i].left, _cocoa_video_data.dirty_rects[i].top,
+								_cocoa_video_data.dirty_rects[i].right, _cocoa_video_data.dirty_rects[i].bottom);
+		MacUnionRgn (dirty, temp, dirty);
+	}
+
+	QZ_DrawResizeIcon();
+
+	/* Flush the dirty region */
+	QDFlushPortBuffer ( [ _cocoa_video_data.qdview qdPort ], dirty );
+	DisposeRgn (dirty);
+	DisposeRgn (temp);
+
+	_cocoa_video_data.num_dirty_rects = 0;
+}
+
+
+extern const char _openttd_revision[];
+
+static const char *QZ_SetVideoWindowed (uint width, uint height) {
+	char caption[50];
+	NSString *nsscaption;
+	unsigned int style;
+	NSRect contentRect;
+	BOOL isCustom = NO;
+
+	if(width > _cocoa_video_data.device_width)
+		width = _cocoa_video_data.device_width;
+	if(height > _cocoa_video_data.device_height)
+		height = _cocoa_video_data.device_height;
+
+	_cocoa_video_data.width = width;
+	_cocoa_video_data.height = height;
+
+	contentRect = NSMakeRect (0, 0, width, height);
+
+	/*
+			Check if we should completely destroy the previous mode
+			- If it is fullscreen
+	*/
+	if (_cocoa_video_data.isset && _cocoa_video_data.fullscreen )
+		QZ_UnsetVideoMode ();
+
+	/* Check if we should recreate the window */
+	if (_cocoa_video_data.window == nil) {
+
+		/* Set the window style */
+		style = NSTitledWindowMask;
+		style|= (NSMiniaturizableWindowMask | NSClosableWindowMask);
+		style|= NSResizableWindowMask;
+
+		/* Manually create a window, avoids having a nib file resource */
+		_cocoa_video_data.window = [ [ OTTD_QuartzWindow alloc ]
+										initWithContentRect:contentRect
+										styleMask:style
+										backing:NSBackingStoreBuffered
+										defer:NO ];
+
+		if (_cocoa_video_data.window == nil)
+			return "Could not create the Cocoa window";
+
+		snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
+		nsscaption = [ [ NSString alloc ] initWithCString:caption encoding:NSASCIIStringEncoding ];
+		[ _cocoa_video_data.window setTitle:nsscaption ];
+		[ _cocoa_video_data.window setMiniwindowTitle:nsscaption ];
+		[ nsscaption release ];
+
+		[ _cocoa_video_data.window setAcceptsMouseMovedEvents:YES ];
+		[ _cocoa_video_data.window setViewsNeedDisplay:NO ];
+
+		[ _cocoa_video_data.window setDelegate: [ [ [ OTTD_QuartzWindowDelegate alloc ] init ] autorelease ] ];
+	}
+	/* We already have a window, just change its size */
+	else {
+
+		if (!isCustom) {
+			[ _cocoa_video_data.window setContentSize:contentRect.size ];
+			[ _cocoa_video_data.qdview setFrameSize:contentRect.size ];
+		}
+	}
+
+	[ _cocoa_video_data.window center ];
+
+	/* Only recreate the view if it doesn't already exist */
+	if (_cocoa_video_data.qdview == nil) {
+
+		_cocoa_video_data.qdview = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
+		[ _cocoa_video_data.qdview setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
+		[ [ _cocoa_video_data.window contentView ] addSubview:_cocoa_video_data.qdview ];
+		[ _cocoa_video_data.qdview release ];
+		[ _cocoa_video_data.window makeKeyAndOrderFront:nil ];
+	}
+
+	LockPortBits ( [ _cocoa_video_data.qdview qdPort ] );
+	_cocoa_video_data.realpixels = GetPixBaseAddr ( GetPortPixMap ( [ _cocoa_video_data.qdview qdPort ] ) );
+	_cocoa_video_data.pitch  = GetPixRowBytes ( GetPortPixMap ( [ _cocoa_video_data.qdview qdPort ] ) );
+	UnlockPortBits ( [ _cocoa_video_data.qdview qdPort ] );
+
+	/*
+	_cocoa_video_data.realpixels now points to the window's pixels
+	We want it to point to the *view's* pixels
+	*/
+	{
+		int vOffset = [ _cocoa_video_data.window frame ].size.height - [ _cocoa_video_data.qdview frame ].size.height - [ _cocoa_video_data.qdview frame ].origin.y;
+
+		int hOffset = [ _cocoa_video_data.qdview frame ].origin.x;
+
+		_cocoa_video_data.realpixels = (uint8 *) _cocoa_video_data.realpixels + (vOffset * _cocoa_video_data.pitch) + hOffset * (_cocoa_video_data.device_bpp / 8);
+	}
+
+	if(_cocoa_video_data.pixels != NULL)
+		free(_cocoa_video_data.pixels);
+	_cocoa_video_data.pixels = (uint8 *) malloc(width * height);
+	if(_cocoa_video_data.pixels == NULL)
+		return "Failed to allocate 8-bit buffer";
+
+	_cocoa_video_data.fullscreen = false;
+
+	return NULL;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************************
+ *                             Fullscreen mode                                            *
+ ******************************************************************************************/
+
+
+/*
+    Gamma functions to try to hide the flash from a rez switch
+    Fade the display from normal to black
+    Save gamma tables for fade back to normal
+*/
+static uint32 QZ_FadeGammaOut (OTTD_QuartzGammaTable *table) {
+
+	CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE],
+		greenTable[QZ_GAMMA_TABLE_SIZE],
+		blueTable[QZ_GAMMA_TABLE_SIZE];
+
+	float percent;
+	int j;
+	unsigned int actual;
+
+	if ( (CGGetDisplayTransferByTable(_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE, table->red, table->green, table->blue, &actual) != CGDisplayNoErr) || actual != QZ_GAMMA_TABLE_SIZE) {
+		return 1;
+	}
+
+	memcpy (redTable, table->red, sizeof(redTable));
+	memcpy (greenTable, table->green, sizeof(greenTable));
+	memcpy (blueTable, table->blue, sizeof(greenTable));
+
+	for (percent = 1.0; percent >= 0.0; percent -= 0.01) {
+		for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
+			redTable[j]   = redTable[j]   * percent;
+			greenTable[j] = greenTable[j] * percent;
+			blueTable[j]  = blueTable[j]  * percent;
+		}
+
+		if (CGSetDisplayTransferByTable(_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE, redTable, greenTable, blueTable) != CGDisplayNoErr) {
+				CGDisplayRestoreColorSyncSettings();
+				return 1;
+		}
+
+		CSleep (10);
+	}
+
+	return 0;
+}
+
+/*
+    Fade the display from black to normal
+    Restore previously saved gamma values
+*/
+static uint32 QZ_FadeGammaIn (OTTD_QuartzGammaTable *table) {
+
+	CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE],
+	greenTable[QZ_GAMMA_TABLE_SIZE],
+	blueTable[QZ_GAMMA_TABLE_SIZE];
+
+	float percent;
+	int j;
+
+	memset (redTable, 0, sizeof(redTable));
+	memset (greenTable, 0, sizeof(greenTable));
+	memset (blueTable, 0, sizeof(greenTable));
+
+	for (percent = 0.0; percent <= 1.0; percent += 0.01) {
+		for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
+			redTable[j]   = table->red[j]   * percent;
+			greenTable[j] = table->green[j] * percent;
+			blueTable[j]  = table->blue[j]  * percent;
+		}
+
+		if (CGSetDisplayTransferByTable(_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE, redTable, greenTable, blueTable) != CGDisplayNoErr) {
+			CGDisplayRestoreColorSyncSettings();
+			return 1;
+		}
+
+		CSleep(10);
+	}
+
+	return 0;
+}
+
+static const char *QZ_SetVideoFullScreen (int width, int height) {
+	const char *errstr = "QZ_SetVideoFullScreen error";
+	int exact_match;
+	CFNumberRef number;
+	int bpp;
+	int gamma_error;
+	OTTD_QuartzGammaTable gamma_table;
+	NSRect screen_rect;
+	CGError error;
+	NSPoint pt;
+
+	/* Destroy any previous mode */
+	if (_cocoa_video_data.isset)
+		QZ_UnsetVideoMode ();
+
+	/* See if requested mode exists */
+	_cocoa_video_data.mode = CGDisplayBestModeForParameters(_cocoa_video_data.display_id, 8, width, height, &exact_match);
+
+	/* If the mode wasn't an exact match, check if it has the right bpp, and update width and height */
+	if ( ! exact_match ) {
+		number = CFDictionaryGetValue (_cocoa_video_data.mode, kCGDisplayBitsPerPixel);
+		CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
+		if(bpp != 8) {
+			errstr = "Failed to find display resolution";
+			goto ERR_NO_MATCH;
+		}
+
+		number = CFDictionaryGetValue (_cocoa_video_data.mode, kCGDisplayWidth);
+		CFNumberGetValue (number, kCFNumberSInt32Type, &width);
+
+		number = CFDictionaryGetValue (_cocoa_video_data.mode, kCGDisplayHeight);
+		CFNumberGetValue (number, kCFNumberSInt32Type, &height);
+	}
+
+	/* Fade display to zero gamma */
+	gamma_error = QZ_FadeGammaOut (&gamma_table);
+
+	/* Put up the blanking window (a window above all other windows) */
+	error = CGDisplayCapture (_cocoa_video_data.display_id);
+
+	if ( CGDisplayNoErr != error ) {
+		errstr = "Failed capturing display";
+		goto ERR_NO_CAPTURE;
+	}
+
+	/* Do the physical switch */
+	if (CGDisplaySwitchToMode (_cocoa_video_data.display_id, _cocoa_video_data.mode) != CGDisplayNoErr) {
+		errstr = "Failed switching display resolution";
+		goto ERR_NO_SWITCH;
+	}
+
+	_cocoa_video_data.realpixels = (uint8 *) CGDisplayBaseAddress (_cocoa_video_data.display_id);
+	_cocoa_video_data.pitch  = CGDisplayBytesPerRow (_cocoa_video_data.display_id);
+
+	_cocoa_video_data.width = CGDisplayPixelsWide (_cocoa_video_data.display_id);
+	_cocoa_video_data.height = CGDisplayPixelsHigh (_cocoa_video_data.display_id);
+	_cocoa_video_data.fullscreen = true;
+
+	/* Setup double-buffer emulation */
+	_cocoa_video_data.pixels = (uint8 *) malloc(width * height);
+	if(_cocoa_video_data.pixels == NULL) {
+		errstr = "Failed to allocate memory for double buffering";
+		goto ERR_DOUBLEBUF;
+	}
+
+	if (!CGDisplayCanSetPalette (_cocoa_video_data.display_id) ) {
+		errstr = "Not an indexed display mode.";
+		goto ERR_NOT_INDEXED;
+	}
+
+	/* If we don't hide menu bar, it will get events and interrupt the program */
+	HideMenuBar ();
+
+	/* Fade the display to original gamma */
+	if (! gamma_error )
+		QZ_FadeGammaIn (&gamma_table);
+
+	/*
+	There is a bug in Cocoa where NSScreen doesn't synchronize
+	with CGDirectDisplay, so the main screen's frame is wrong.
+	As a result, coordinate translation produces incorrect results.
+	We can hack around this bug by setting the screen rect
+	ourselves. This hack should be removed if/when the bug is fixed.
+	*/
+	screen_rect = NSMakeRect(0,0,width,height);
+	[ [ NSScreen mainScreen ] setFrame:screen_rect ];
+
+	/* we're fullscreen, so flag all input states... */
+	_cocoa_video_data.active = true;
+
+
+	pt = [ NSEvent mouseLocation ];
+	pt.y = CGDisplayPixelsHigh (_cocoa_video_data.display_id) - pt.y;
+	if(QZ_MouseIsInsideView(&pt))
+		QZ_HideMouse();
+
+	return NULL;
+
+/* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
+ERR_NOT_INDEXED:
+	free(_cocoa_video_data.pixels);
+	_cocoa_video_data.pixels = NULL;
+ERR_DOUBLEBUF:
+	CGDisplaySwitchToMode (_cocoa_video_data.display_id, _cocoa_video_data.save_mode);
+ERR_NO_SWITCH:
+	CGReleaseAllDisplays ();
+ERR_NO_CAPTURE:
+	if (!gamma_error) {
+		QZ_FadeGammaIn (&gamma_table);
+	}
+ERR_NO_MATCH:
+	return errstr;
+}
+
+
+static void QZ_UpdateFullscreenPalette (uint first_color, uint num_colors) {
+
+	CGTableCount  index;
+	CGDeviceColor color;
+
+	for (index = first_color; index < first_color+num_colors; index++) {
+		/* Clamp colors between 0.0 and 1.0 */
+		color.red   = _cur_palette[index].r / 255.0;
+		color.blue  = _cur_palette[index].b / 255.0;
+		color.green = _cur_palette[index].g / 255.0;
+
+		CGPaletteSetColorAtIndex (_cocoa_video_data.palette, color, index);
+	}
+
+	CGDisplaySetPalette (_cocoa_video_data.display_id, _cocoa_video_data.palette);
+}
+
+/* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
+static void QZ_WaitForVerticalBlank(void)
+{
+	/* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
+	double refreshRate;
+	double linesPerSecond;
+	double target;
+	double position;
+	double adjustment;
+	CFNumberRef refreshRateCFNumber;
+
+	refreshRateCFNumber = CFDictionaryGetValue (_cocoa_video_data.mode, kCGDisplayRefreshRate);
+	if (refreshRateCFNumber == NULL)
+		return;
+
+	if (CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) == 0)
+		return;
+
+	if ( refreshRate == 0 )
+		return;
+
+	linesPerSecond = refreshRate * _cocoa_video_data.height;
+	target = _cocoa_video_data.height;
+
+	/* Figure out the first delay so we start off about right */
+	position = CGDisplayBeamPosition (_cocoa_video_data.display_id);
+	if (position > target)
+		position = 0;
+
+	adjustment = (target - position) / linesPerSecond;
+
+	CSleep((uint32) (adjustment * 1000));
+}
+
+static void QZ_DrawScreen(void)
+{
+	uint y;
+	uint8 *src, *dst;
+
+	QZ_WaitForVerticalBlank();
+
+	for(y = 0; y < _cocoa_video_data.height; y++) {
+		src = _cocoa_video_data.pixels + y * _cocoa_video_data.width;
+		dst = ((uint8 *) _cocoa_video_data.realpixels) + y * _cocoa_video_data.pitch;
+
+		memcpy(dst, src, _cocoa_video_data.width);
+	}
+}
+
+static int QZ_ListFullscreenModes (OTTDPoint *mode_list, int max_modes) {
+	CFIndex num_modes;
+	CFIndex i;
+	int list_size = 0;
+
+	num_modes = CFArrayGetCount (_cocoa_video_data.mode_list);
+
+	/* Build list of modes with the requested bpp */
+	for (i = 0; i < num_modes && list_size < max_modes; i++) {
+		CFDictionaryRef onemode;
+		CFNumberRef     number;
+		int bpp;
+		int intvalue;
+		bool hasMode;
+		uint16 width, height;
+
+		onemode = CFArrayGetValueAtIndex (_cocoa_video_data.mode_list, i);
+		number = CFDictionaryGetValue (onemode, kCGDisplayBitsPerPixel);
+		CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
+
+		if (bpp != 8)
+			continue;
+
+		number = CFDictionaryGetValue (onemode, kCGDisplayWidth);
+		CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
+		width = (uint16) intvalue;
+
+		number = CFDictionaryGetValue (onemode, kCGDisplayHeight);
+		CFNumberGetValue (number, kCFNumberSInt32Type, &intvalue);
+		height = (uint16) intvalue;
+
+		/* Check if mode is already in the list */
+		{
+			int i;
+			hasMode = false;
+			for (i = 0; i < list_size; i++) {
+				if (mode_list[i].x == width &&  mode_list[i].y == height) {
+					hasMode = true;
+					break;
+				}
+			}
+		}
+
+		if ( hasMode )
+			continue;
+
+		/* Add mode to the list */
+		mode_list[list_size].x = width;
+		mode_list[list_size].y = height;
+		list_size++;
+	}
+
+	/* Sort list smallest to largest */
+	{
+		int i, j;
+		for (i = 0; i < list_size; i++) {
+			for (j = 0; j < list_size-1; j++) {
+				if(mode_list[j].x > mode_list[j + 1].x || (mode_list[j].x == mode_list[j + 1].x && mode_list[j].y > mode_list[j + 1].y)) {
+					uint tmpw = mode_list[j].x;
+					uint tmph = mode_list[j].y;
+
+					mode_list[j].x = mode_list[j+1].x;
+					mode_list[j].y = mode_list[j+1].y;
+
+					mode_list[j+1].x = tmpw;
+					mode_list[j+1].y = tmph;
+				}
+			}
+		}
+	}
+
+	return list_size;
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************************
+ *                             Windowed and fullscreen common code                        *
+ ******************************************************************************************/
+
+static void QZ_UpdatePalette(uint start, uint count)
+{
+	if(_cocoa_video_data.fullscreen) {
+		QZ_UpdateFullscreenPalette(start, count);
+	} else {
+		QZ_UpdateWindowPalette(start, count);
+	}
+}
+
+static void QZ_InitPalette(void)
+{
+	QZ_UpdatePalette(0, 256);
+}
+
+static void QZ_Draw(void)
+{
+	if(_cocoa_video_data.fullscreen) {
+		QZ_DrawScreen();
+	} else {
+		QZ_DrawWindow();
+	}
+}
+
+
+static const OTTDPoint _default_resolutions[] = {
+	{ 640,  480},
+	{ 800,  600},
+	{1024,  768},
+	{1152,  864},
+	{1280,  800},
+	{1280,  960},
+	{1280, 1024},
+	{1400, 1050},
+	{1600, 1200},
+	{1680, 1050},
+	{1920, 1200}
+};
+
+static void QZ_UpdateVideoModes(void)
+{
+	uint i, j, count;
+	OTTDPoint modes[32];
+	const OTTDPoint *current_modes;
+
+	if(_cocoa_video_data.fullscreen) {
+		count = QZ_ListFullscreenModes(modes, 32);
+		current_modes = modes;
+	} else {
+		count = lengthof(_default_resolutions);
+		current_modes = _default_resolutions;
+	}
+
+	for(i = 0, j = 0; j < lengthof(_resolutions) && i < count; i++) {
+		if (_cocoa_video_data.fullscreen || ((uint) current_modes[i].x <= _cocoa_video_data.device_width &&
+				  							 (uint) current_modes[i].y <= _cocoa_video_data.device_height)) {
+			_resolutions[j][0] = current_modes[i].x;
+			_resolutions[j][1] = current_modes[i].y;
+			j++;
+		}
+	}
+
+	_num_resolutions = j;
+}
+
+static void QZ_UnsetVideoMode (void) {
+	/* Release fullscreen resources */
+	if ( _cocoa_video_data.fullscreen ) {
+
+		OTTD_QuartzGammaTable gamma_table;
+		int gamma_error;
+		NSRect screen_rect;
+
+		gamma_error = QZ_FadeGammaOut (&gamma_table);
+
+		/* Restore original screen resolution/bpp */
+		CGDisplaySwitchToMode (_cocoa_video_data.display_id, _cocoa_video_data.save_mode);
+		CGReleaseAllDisplays ();
+		ShowMenuBar ();
+        /*
+		Reset the main screen's rectangle
+		See comment in QZ_SetVideoFullscreen for why we do this
+		*/
+		screen_rect = NSMakeRect(0,0,_cocoa_video_data.device_width,_cocoa_video_data.device_height);
+		[ [ NSScreen mainScreen ] setFrame:screen_rect ];
+
+		if (! gamma_error)
+			QZ_FadeGammaIn (&gamma_table);
+	}
+	/* Release window mode resources */
+	else {
+
+		[ _cocoa_video_data.window close ];
+		_cocoa_video_data.window = nil;
+		_cocoa_video_data.qdview = nil;
+	}
+
+	free(_cocoa_video_data.pixels);
+	_cocoa_video_data.pixels = NULL;
+
+	QZ_ShowMouse();
+
+	/* Signal successful teardown */
+	_cocoa_video_data.isset = false;
+}
+
+
+static const char *QZ_SetVideoMode (uint width, uint height, bool fullscreen) {
+	const char *ret;
+
+	/* Setup full screen video */
+	if ( fullscreen ) {
+		_cocoa_video_data.issetting = true;
+		ret = QZ_SetVideoFullScreen (width, height);
+		_cocoa_video_data.issetting = false;
+		if (ret != NULL)
+			return ret;
+	}
+	/* Setup windowed video */
+	else {
+		_cocoa_video_data.issetting = true;
+		ret = QZ_SetVideoWindowed (width, height);
+		_cocoa_video_data.issetting = false;
+		if (ret != NULL)
+			return ret;
+	}
+
+	/* Signal successful completion (used internally) */
+	_cocoa_video_data.isset = true;
+
+	/* Tell the game that the resolution has changed */
+	_screen.width = _cocoa_video_data.width;
+	_screen.height = _cocoa_video_data.height;
+	_screen.pitch = _cocoa_video_data.width;
+
+	QZ_UpdateVideoModes();
+	GameSizeChanged();
+
+	QZ_InitPalette();
+
+	return NULL;
+}
+
+static const char *QZ_SetVideoModeAndRestoreOnFailure (uint width, uint height, bool fullscreen)
+{
+	bool wasset = _cocoa_video_data.isset;
+	uint32 oldwidth = _cocoa_video_data.width;
+	uint32 oldheight = _cocoa_video_data.height;
+	bool oldfullscreen = _cocoa_video_data.fullscreen;
+	const char *ret;
+
+	ret = QZ_SetVideoMode(width, height, fullscreen);
+	if(ret != NULL && wasset)
+		QZ_SetVideoMode(oldwidth, oldheight, oldfullscreen);
+
+	return ret;
+}
+
+static void QZ_VideoInit (void) {
+
+	memset(&_cocoa_video_data, 0, sizeof(_cocoa_video_data));
+
+	/* Initialize the video settings; this data persists between mode switches */
+	_cocoa_video_data.display_id = kCGDirectMainDisplay;
+	_cocoa_video_data.save_mode  = CGDisplayCurrentMode    (_cocoa_video_data.display_id);
+	_cocoa_video_data.mode_list  = CGDisplayAvailableModes (_cocoa_video_data.display_id);
+	_cocoa_video_data.palette    = CGPaletteCreateDefaultColorPalette ();
+
+	/* Gather some information that is useful to know about the display */
+	/* Maybe this should be moved to QZ_SetVideoMode, in case this is changed after startup */
+	CFNumberGetValue (CFDictionaryGetValue (_cocoa_video_data.save_mode, kCGDisplayBitsPerPixel),
+					  kCFNumberSInt32Type, &_cocoa_video_data.device_bpp);
+
+	CFNumberGetValue (CFDictionaryGetValue (_cocoa_video_data.save_mode, kCGDisplayWidth),
+					  kCFNumberSInt32Type, &_cocoa_video_data.device_width);
+
+	CFNumberGetValue (CFDictionaryGetValue (_cocoa_video_data.save_mode, kCGDisplayHeight),
+					  kCFNumberSInt32Type, &_cocoa_video_data.device_height);
+
+	_cocoa_video_data.cursor_visible = true;
+
+	/* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
+//	QZ_RegisterForSleepNotifications ();
+}
+
+
+/* Convert local coordinate to window server (CoreGraphics) coordinate */
+static CGPoint QZ_PrivateLocalToCG (NSPoint *p) {
+	CGPoint cgp;
+
+	if ( ! _cocoa_video_data.fullscreen ) {
+		*p = [ _cocoa_video_data.qdview convertPoint:*p toView: nil ];
+		*p = [ _cocoa_video_data.window convertBaseToScreen:*p ];
+		p->y = _cocoa_video_data.device_height - p->y;
+	}
+
+	cgp.x = p->x;
+	cgp.y = p->y;
+
+	return cgp;
+}
+
+static void QZ_WarpCursor (int x, int y) {
+	NSPoint p;
+	CGPoint cgp;
+
+	/* Only allow warping when in foreground */
+	if ( ! [ NSApp isActive ] )
+		return;
+
+	p = NSMakePoint (x, y);
+	cgp = QZ_PrivateLocalToCG (&p);
+
+	/* this is the magic call that fixes cursor "freezing" after warp */
+	CGSetLocalEventsSuppressionInterval (0.0);
+	/* Do the actual warp */
+	CGWarpMouseCursorPosition (cgp);
+
+	/* Generate the mouse moved event */
+//	SDL_PrivateMouseMotion (0, 0, x, y);
+}
+
+void QZ_ShowMouse (void) {
+	if (!_cocoa_video_data.cursor_visible) {
+		[ NSCursor unhide ];
+		_cocoa_video_data.cursor_visible = true;
+	}
+}
+
+void QZ_HideMouse (void) {
+	if (_cocoa_video_data.cursor_visible) {
+#ifndef _DEBUG
+		[ NSCursor hide ];
+#endif
+		_cocoa_video_data.cursor_visible = false;
+	}
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************************
+ *                             OS X application creation                                  *
+ ******************************************************************************************/
+
+
+
+/* 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
+{
+//	DEBUG(driver, 1)("cocoa_v: applicationShouldTerminate");
+
+	QZ_AskQuit();
+
+	return NSTerminateCancel;		// NSTerminateLater ?
+}
+@end
+
+static void setApplicationMenu(void)
+{
+	/* 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(void)
+{
+	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(void)
+{
+	CPSProcessSerNum PSN;
+
+	/* Ensure the application object is initialised */
+	[NSApplication sharedApplication];
+
+	/* Tell the dock about us */
+	if (!CPSGetCurrentProcess(&PSN))
+		if (!CPSEnableForegroundOperation(&PSN,0x03,0x3C,0x2C,0x1103))
+			if (!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];
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+/******************************************************************************************
+ *                             Video driver interface                                     *
+ ******************************************************************************************/
+
+static void CocoaVideoStop(void)
+{
+	DEBUG(driver, 1)("cocoa_v: CocoaVideoStop");
+
+	if(_cocoa_video_data.isset)
+		QZ_UnsetVideoMode();
+
+	[_ottd_main release];
+}
+
+static const char *CocoaVideoStart(const char * const *parm)
+{
+	const char *ret;
+
+	DEBUG(driver, 1)("cocoa_v: CocoaVideoStart");
+
+	setupApplication();
+
+	QZ_VideoInit();
+
+	ret = QZ_SetVideoMode(_cur_resolution[0], _cur_resolution[1], _fullscreen);
+	if(ret != NULL)
+		CocoaVideoStop();
+
+	return ret;
+}
+
+static void CocoaVideoMakeDirty(int left, int top, int width, int height)
+{
+	if (_cocoa_video_data.num_dirty_rects < MAX_DIRTY_RECTS) {
+		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].left = left;
+		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].top = top;
+		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].right = left + width;
+		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].bottom = top + height;
+	}
+	_cocoa_video_data.num_dirty_rects++;
+}
+
+static void CocoaVideoMainLoop(void)
+{
+	DEBUG(driver, 1)("cocoa_v: CocoaVideoMainLoop");
+
+	/* Start the main event loop */
+	[NSApp run];
+}
+
+static bool CocoaVideoChangeRes(int w, int h)
+{
+	const char *ret;
+	DEBUG(driver, 1)("cocoa_v: CocoaVideoChangeRes");
+
+	ret = QZ_SetVideoModeAndRestoreOnFailure((uint) w, (uint) h, _cocoa_video_data.fullscreen);
+	if(ret != NULL) {
+		DEBUG(driver, 1)("cocoa_v:  failed with message: %s", ret);
+	}
+
+	return ret == NULL;
+}
+
+static void CocoaVideoFullScreen(bool full_screen)
+{
+	const char *ret;
+
+	DEBUG(driver, 1)("cocoa_v: CocoaVideoFullScreen");
+
+	ret = QZ_SetVideoModeAndRestoreOnFailure(_cocoa_video_data.width, _cocoa_video_data.height, full_screen);
+	if(ret != NULL) {
+		DEBUG(driver, 1)("cocoa_v:  failed with message: %s", ret);
+	}
+
+	_fullscreen = _cocoa_video_data.fullscreen;
+}
+
+const HalVideoDriver _cocoa_video_driver = {
+	CocoaVideoStart,
+	CocoaVideoStop,
+	CocoaVideoMakeDirty,
+	CocoaVideoMainLoop,
+	CocoaVideoChangeRes,
+	CocoaVideoFullScreen,
+};
+
+
+/* This is needed since OS X applications are started with the working dir set to / when double-clicked */
+void cocoaSetWorkingDirectory(void)
+{
+	char parentdir[MAXPATHLEN];
+	int chdir_ret;
+	CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle());
+	CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url);
+	if (CFURLGetFileSystemRepresentation(url2, true, (unsigned char *) parentdir, MAXPATHLEN)) {
+		chdir_ret = chdir (parentdir); /* chdir to the binary app's parent */
+		assert(chdir_ret == 0);
+	}
+	CFRelease(url);
+	CFRelease(url2);
+}
+
+/* These are called from main() to prevent a _NSAutoreleaseNoPool error when
+ * exiting before the cocoa video driver has been loaded
+ */
+void cocoaSetupAutoreleasePool(void)
+{
+	_ottd_autorelease_pool = [[NSAutoreleasePool alloc] init];
+}
+
+void cocoaReleaseAutoreleasePool(void)
+{
+	[_ottd_autorelease_pool release];
+}
+
+#endif /* WITH_COCOA */