summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Makefile29
-rw-r--r--driver.c8
-rw-r--r--fileio.c31
-rw-r--r--fileio.h1
-rw-r--r--makefiledir/Makefile.config_writer1
-rw-r--r--makefiledir/Makefile.libdetection8
-rw-r--r--os/macosx/Makefile1
-rw-r--r--os/macosx/macos.m4
-rw-r--r--os/macosx/splash.c145
-rw-r--r--os/macosx/splash.h9
-rw-r--r--os/macosx/splash.pngbin0 -> 20053 bytes
-rw-r--r--sound/cocoa_s.c144
-rw-r--r--sound/cocoa_s.h9
-rw-r--r--unix.c26
-rw-r--r--video/cocoa_v.h130
-rw-r--r--video/cocoa_v.m2113
16 files changed, 2651 insertions, 8 deletions
diff --git a/Makefile b/Makefile
index 9d9cf64ca..2680cbd49 100644
--- a/Makefile
+++ b/Makefile
@@ -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
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 @@ $(error You need to have SDL installed in order to run OpenTTD on UNIX. Use DEDI
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 @@ else
STRGEN_FLAGS=
endif
-
# OSX specific setup
ifdef OSX
# set the endian flag for OSX, that can't fail
@@ -519,6 +528,11 @@ ifdef OSX
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 @@ else
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
diff --git a/driver.c b/driver.c
index e6c6999a9..0595e4397 100644
--- a/driver.c
+++ b/driver.c
@@ -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 @@ static const DriverDesc _sound_driver_descs[] = {
#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 @@ static const DriverDesc _video_driver_descs[] = {
#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),
diff --git a/fileio.c b/fileio.c
index d8adac9fb..d0013cf1f 100644
--- a/fileio.c
+++ b/fileio.c
@@ -141,6 +141,37 @@ bool FiosCheckFileExists(const char *filename)
}
}
+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;
diff --git a/fileio.h b/fileio.h
index c0923ce15..926c986be 100644
--- a/fileio.h
+++ b/fileio.h
@@ -10,6 +10,7 @@ byte FioReadByte(void);
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);
diff --git a/makefiledir/Makefile.config_writer b/makefiledir/Makefile.config_writer
index 4b56391b6..dff31a4e8 100644
--- a/makefiledir/Makefile.config_writer
+++ b/makefiledir/Makefile.config_writer
@@ -72,6 +72,7 @@ $(MAKE_CONFIG):
$(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)
diff --git a/makefiledir/Makefile.libdetection b/makefiledir/Makefile.libdetection
index 9177061ee..f648c22bd 100644
--- a/makefiledir/Makefile.libdetection
+++ b/makefiledir/Makefile.libdetection
@@ -93,6 +93,14 @@ endif
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
diff --git a/os/macosx/Makefile b/os/macosx/Makefile
index 577bf420a..3ea875843 100644
--- a/os/macosx/Makefile
+++ b/os/macosx/Makefile
@@ -45,6 +45,7 @@ $(BUILD_OSX_BUNDLE): $(TTD) $(FAT_BINARY)
$(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)
diff --git a/os/macosx/macos.m b/os/macosx/macos.m
index 936aaa7a0..6c5fa8791 100644
--- a/os/macosx/macos.m
+++ b/os/macosx/macos.m
@@ -15,12 +15,12 @@ void ShowMacDialog ( const char *title, const char *message, const char *buttonL
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();
}
diff --git a/os/macosx/splash.c b/os/macosx/splash.c
new file mode 100644
index 000000000..99791cc21
--- /dev/null
+++ b/os/macosx/splash.c
@@ -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
diff --git a/os/macosx/splash.h b/os/macosx/splash.h
new file mode 100644
index 000000000..7454ccaa0
--- /dev/null
+++ b/os/macosx/splash.h
@@ -0,0 +1,9 @@
+
+#ifndef SPLASH_H
+#define SPLASH_H
+
+#define SPLASH_IMAGE_FILE "splash.png"
+
+void DisplaySplashImage(void);
+
+#endif
diff --git a/os/macosx/splash.png b/os/macosx/splash.png
new file mode 100644
index 000000000..e67d10da8
--- /dev/null
+++ b/os/macosx/splash.png
Binary files differ
diff --git a/sound/cocoa_s.c b/sound/cocoa_s.c
new file mode 100644
index 000000000..652944094
--- /dev/null
+++ b/sound/cocoa_s.c
@@ -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 */
diff --git a/sound/cocoa_s.h b/sound/cocoa_s.h
new file mode 100644
index 000000000..3d966165b
--- /dev/null
+++ b/sound/cocoa_s.h
@@ -0,0 +1,9 @@
+
+#ifndef SOUND_COCOA_H
+#define SOUND_COCOA_H
+
+#include "../hal.h"
+
+const HalSoundDriver _cocoa_sound_driver;
+
+#endif
diff --git a/unix.c b/unix.c
index fd20e3715..7e23aca00 100644
--- a/unix.c
+++ b/unix.c
@@ -444,8 +444,26 @@ void ShowOSErrorBox(const char *buf)
#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 @@ int CDECL main(int argc, char* argv[])
signal(SIGPIPE, SIG_IGN);
- return ttd_main(argc, argv);
+ ret = ttd_main(argc, argv);
+
+#ifdef WITH_COCOA
+ cocoaReleaseAutoreleasePool();
+#endif
+
+ return ret;
}
void DeterminePaths(void)
diff --git a/video/cocoa_v.h b/video/cocoa_v.h
new file mode 100644
index 000000000..2d32e0a57
--- /dev/null
+++ b/video/cocoa_v.h
@@ -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
diff --git a/video/cocoa_v.m b/video/cocoa_v.m
new file mode 100644
index 000000000..2dbc2c478
--- /dev/null
+++ b/video/cocoa_v.m
@@ -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 */