src/video/cocoa_v.mm
changeset 8435 38a1ab65d6e4
parent 8434 6cce8c248f24
child 8436 f52eebc3fc20
equal deleted inserted replaced
8434:6cce8c248f24 8435:38a1ab65d6e4
     1 /* $Id$ */
       
     2 
       
     3 /******************************************************************************
       
     4  *                             Cocoa video driver                             *
       
     5  * Known things left to do:                                                   *
       
     6  *  Nothing at the moment.                                                    *
       
     7  ******************************************************************************/
       
     8 
       
     9 #ifdef WITH_COCOA
       
    10 
       
    11 #include <AvailabilityMacros.h>
       
    12 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
       
    13 /* On 10.4, we get tons of warnings that the QuickDraw functions are deprecated.
       
    14  *  We know that. Don't keep bugging us about that. */
       
    15 #	undef AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4
       
    16 #	define AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER_BUT_DEPRECATED_IN_MAC_OS_X_VERSION_10_4 AVAILABLE_MAC_OS_X_VERSION_10_0_AND_LATER
       
    17 #endif
       
    18 
       
    19 #import <Cocoa/Cocoa.h>
       
    20 #import <sys/time.h> /* gettimeofday */
       
    21 #import <sys/param.h> /* for MAXPATHLEN */
       
    22 #import <unistd.h>
       
    23 
       
    24 /**
       
    25  * Important notice regarding all modifications!!!!!!!
       
    26  * There are certain limitations because the file is objective C++.
       
    27  * gdb has limitations.
       
    28  * C++ and objective C code can't be joined in all cases (classes stuff).
       
    29  * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information.
       
    30  */
       
    31 
       
    32 
       
    33 /* Portions of CPS.h */
       
    34 struct CPSProcessSerNum {
       
    35 	UInt32 lo;
       
    36 	UInt32 hi;
       
    37 };
       
    38 
       
    39 extern "C" OSErr CPSGetCurrentProcess(CPSProcessSerNum* psn);
       
    40 extern "C" OSErr CPSEnableForegroundOperation(CPSProcessSerNum* psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5);
       
    41 extern "C" OSErr CPSSetFrontProcess(CPSProcessSerNum* psn);
       
    42 
       
    43 /* From Menus.h (according to Xcode Developer Documentation) */
       
    44 extern "C" void ShowMenuBar();
       
    45 extern "C" void HideMenuBar();
       
    46 
       
    47 /* Disables a warning. This is needed since the method exists but has been dropped from the header, supposedly as of 10.4. */
       
    48 #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
       
    49 @interface NSApplication(NSAppleMenu)
       
    50 - (void)setAppleMenu:(NSMenu *)menu;
       
    51 @end
       
    52 #endif
       
    53 
       
    54 
       
    55 /* Defined in stdbool.h */
       
    56 #ifndef __cplusplus
       
    57 # ifndef __BEOS__
       
    58 #  undef bool
       
    59 #  undef false
       
    60 #  undef true
       
    61 # endif
       
    62 #endif
       
    63 
       
    64 
       
    65 #include "../stdafx.h"
       
    66 #include "../openttd.h"
       
    67 #include "../debug.h"
       
    68 #include "../macros.h"
       
    69 #include "../os/macosx/splash.h"
       
    70 #include "../variables.h"
       
    71 #include "../gfx.h"
       
    72 #include "cocoa_v.h"
       
    73 #include "cocoa_keys.h"
       
    74 #include "../blitter/factory.hpp"
       
    75 #include "../fileio.h"
       
    76 
       
    77 #undef Point
       
    78 #undef Rect
       
    79 
       
    80 /* Right Mouse Button Emulation enum */
       
    81 enum {
       
    82 	RMBE_COMMAND,
       
    83 	RMBE_CONTROL,
       
    84 	RMBE_OFF,
       
    85 };
       
    86 
       
    87 
       
    88 /* Subclass of NSWindow to fix genie effect and support resize events  */
       
    89 @interface OTTD_QuartzWindow : NSWindow
       
    90 - (void)miniaturize:(id)sender;
       
    91 - (void)display;
       
    92 - (void)setFrame:(NSRect)frameRect display:(BOOL)flag;
       
    93 - (void)appDidHide:(NSNotification*)note;
       
    94 - (void)appWillUnhide:(NSNotification*)note;
       
    95 - (void)appDidUnhide:(NSNotification*)note;
       
    96 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag;
       
    97 @end
       
    98 
       
    99 /* Delegate for our NSWindow to send ask for quit on close */
       
   100 @interface OTTD_QuartzWindowDelegate : NSObject
       
   101 - (BOOL)windowShouldClose:(id)sender;
       
   102 @end
       
   103 
       
   104 @interface OTTDMain : NSObject
       
   105 @end
       
   106 
       
   107 
       
   108 /* Structure for rez switch gamma fades
       
   109  * We can hide the monitor flicker by setting the gamma tables to 0
       
   110  */
       
   111 #define QZ_GAMMA_TABLE_SIZE 256
       
   112 
       
   113 struct OTTD_QuartzGammaTable {
       
   114 	CGGammaValue red[QZ_GAMMA_TABLE_SIZE];
       
   115 	CGGammaValue green[QZ_GAMMA_TABLE_SIZE];
       
   116 	CGGammaValue blue[QZ_GAMMA_TABLE_SIZE];
       
   117 };
       
   118 
       
   119 /* Add methods to get at private members of NSScreen.
       
   120  * Since there is a bug in Apple's screen switching code that does not update
       
   121  * this variable when switching to fullscreen, we'll set it manually (but only
       
   122  * for the main screen).
       
   123  */
       
   124 @interface NSScreen (NSScreenAccess)
       
   125 	- (void) setFrame:(NSRect)frame;
       
   126 @end
       
   127 
       
   128 @implementation NSScreen (NSScreenAccess)
       
   129 - (void) setFrame:(NSRect)frame;
       
   130 {
       
   131 	_frame = frame;
       
   132 }
       
   133 @end
       
   134 
       
   135 
       
   136 static void QZ_Draw();
       
   137 static void QZ_UnsetVideoMode();
       
   138 static void QZ_UpdatePalette(uint start, uint count);
       
   139 static void QZ_WarpCursor(int x, int y);
       
   140 static void QZ_ShowMouse();
       
   141 static void QZ_HideMouse();
       
   142 
       
   143 
       
   144 static NSAutoreleasePool *_ottd_autorelease_pool;
       
   145 static OTTDMain *_ottd_main;
       
   146 
       
   147 
       
   148 static struct CocoaVideoData {
       
   149 	bool isset;
       
   150 	bool issetting;
       
   151 
       
   152 	CGDirectDisplayID  display_id;         /* 0 == main display (only support single display) */
       
   153 	CFDictionaryRef    mode;               /* current mode of the display */
       
   154 	CFDictionaryRef    save_mode;          /* original mode of the display */
       
   155 	CFArrayRef         mode_list;          /* list of available fullscreen modes */
       
   156 	CGDirectPaletteRef palette;            /* palette of an 8-bit display */
       
   157 
       
   158 	uint32 blitter_bpp;
       
   159 
       
   160 	uint32 device_width;
       
   161 	uint32 device_height;
       
   162 	uint32 device_bpp;
       
   163 
       
   164 	void *realpixels;
       
   165 	void *pixels;
       
   166 	uint32 width;
       
   167 	uint32 height;
       
   168 	uint32 pitch;
       
   169 	bool fullscreen;
       
   170 
       
   171 	unsigned int current_mods;
       
   172 	bool tab_is_down;
       
   173 	bool emulating_right_button;
       
   174 
       
   175 	bool cursor_visible;
       
   176 	bool active;
       
   177 
       
   178 #ifdef _DEBUG
       
   179 	uint32 tEvent;
       
   180 #endif
       
   181 
       
   182 	OTTD_QuartzWindow *window;
       
   183 	NSQuickDrawView *qdview;
       
   184 
       
   185 #define MAX_DIRTY_RECTS 100
       
   186 	Rect dirty_rects[MAX_DIRTY_RECTS];
       
   187 	int num_dirty_rects;
       
   188 
       
   189 	uint16 palette16[256];
       
   190 	uint32 palette32[256];
       
   191 } _cocoa_video_data;
       
   192 
       
   193 static bool _cocoa_video_started = false;
       
   194 static bool _cocoa_video_dialog = false;
       
   195 
       
   196 
       
   197 
       
   198 
       
   199 /******************************************************************************
       
   200  *                             Game loop and accessories                      *
       
   201  ******************************************************************************/
       
   202 
       
   203 static uint32 GetTick()
       
   204 {
       
   205 	struct timeval tim;
       
   206 
       
   207 	gettimeofday(&tim, NULL);
       
   208 	return tim.tv_usec / 1000 + tim.tv_sec * 1000;
       
   209 }
       
   210 
       
   211 static void QZ_CheckPaletteAnim()
       
   212 {
       
   213 	if (_pal_count_dirty != 0) {
       
   214 		Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
       
   215 
       
   216 		switch (blitter->UsePaletteAnimation()) {
       
   217 			case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
       
   218 				QZ_UpdatePalette(_pal_first_dirty, _pal_count_dirty);
       
   219 				break;
       
   220 
       
   221 			case Blitter::PALETTE_ANIMATION_BLITTER:
       
   222 				blitter->PaletteAnimate(_pal_first_dirty, _pal_count_dirty);
       
   223 				break;
       
   224 
       
   225 			case Blitter::PALETTE_ANIMATION_NONE:
       
   226 				break;
       
   227 
       
   228 			default:
       
   229 				NOT_REACHED();
       
   230 		}
       
   231 		_pal_count_dirty = 0;
       
   232 	}
       
   233 }
       
   234 
       
   235 
       
   236 
       
   237 struct VkMapping {
       
   238 	unsigned short vk_from;
       
   239 	byte map_to;
       
   240 };
       
   241 
       
   242 #define AS(x, z) {x, z}
       
   243 
       
   244 static const VkMapping _vk_mapping[] = {
       
   245 	AS(QZ_BACKQUOTE,  WKC_BACKQUOTE), // key left of '1'
       
   246 	AS(QZ_BACKQUOTE2, WKC_BACKQUOTE), // some keyboards have it on another scancode
       
   247 
       
   248 	/* Pageup stuff + up/down */
       
   249 	AS(QZ_PAGEUP,   WKC_PAGEUP),
       
   250 	AS(QZ_PAGEDOWN, WKC_PAGEDOWN),
       
   251 
       
   252 	AS(QZ_UP,    WKC_UP),
       
   253 	AS(QZ_DOWN,  WKC_DOWN),
       
   254 	AS(QZ_LEFT,  WKC_LEFT),
       
   255 	AS(QZ_RIGHT, WKC_RIGHT),
       
   256 
       
   257 	AS(QZ_HOME, WKC_HOME),
       
   258 	AS(QZ_END,  WKC_END),
       
   259 
       
   260 	AS(QZ_INSERT, WKC_INSERT),
       
   261 	AS(QZ_DELETE, WKC_DELETE),
       
   262 
       
   263 	/* Letters. QZ_[a-z] is not in numerical order so we can't use AM(...) */
       
   264 	AS(QZ_a, 'A'),
       
   265 	AS(QZ_b, 'B'),
       
   266 	AS(QZ_c, 'C'),
       
   267 	AS(QZ_d, 'D'),
       
   268 	AS(QZ_e, 'E'),
       
   269 	AS(QZ_f, 'F'),
       
   270 	AS(QZ_g, 'G'),
       
   271 	AS(QZ_h, 'H'),
       
   272 	AS(QZ_i, 'I'),
       
   273 	AS(QZ_j, 'J'),
       
   274 	AS(QZ_k, 'K'),
       
   275 	AS(QZ_l, 'L'),
       
   276 	AS(QZ_m, 'M'),
       
   277 	AS(QZ_n, 'N'),
       
   278 	AS(QZ_o, 'O'),
       
   279 	AS(QZ_p, 'P'),
       
   280 	AS(QZ_q, 'Q'),
       
   281 	AS(QZ_r, 'R'),
       
   282 	AS(QZ_s, 'S'),
       
   283 	AS(QZ_t, 'T'),
       
   284 	AS(QZ_u, 'U'),
       
   285 	AS(QZ_v, 'V'),
       
   286 	AS(QZ_w, 'W'),
       
   287 	AS(QZ_x, 'X'),
       
   288 	AS(QZ_y, 'Y'),
       
   289 	AS(QZ_z, 'Z'),
       
   290 	/* Same thing for digits */
       
   291 	AS(QZ_0, '0'),
       
   292 	AS(QZ_1, '1'),
       
   293 	AS(QZ_2, '2'),
       
   294 	AS(QZ_3, '3'),
       
   295 	AS(QZ_4, '4'),
       
   296 	AS(QZ_5, '5'),
       
   297 	AS(QZ_6, '6'),
       
   298 	AS(QZ_7, '7'),
       
   299 	AS(QZ_8, '8'),
       
   300 	AS(QZ_9, '9'),
       
   301 
       
   302 	AS(QZ_ESCAPE,    WKC_ESC),
       
   303 	AS(QZ_PAUSE,     WKC_PAUSE),
       
   304 	AS(QZ_BACKSPACE, WKC_BACKSPACE),
       
   305 
       
   306 	AS(QZ_SPACE,  WKC_SPACE),
       
   307 	AS(QZ_RETURN, WKC_RETURN),
       
   308 	AS(QZ_TAB,    WKC_TAB),
       
   309 
       
   310 	/* Function keys */
       
   311 	AS(QZ_F1,  WKC_F1),
       
   312 	AS(QZ_F2,  WKC_F2),
       
   313 	AS(QZ_F3,  WKC_F3),
       
   314 	AS(QZ_F4,  WKC_F4),
       
   315 	AS(QZ_F5,  WKC_F5),
       
   316 	AS(QZ_F6,  WKC_F6),
       
   317 	AS(QZ_F7,  WKC_F7),
       
   318 	AS(QZ_F8,  WKC_F8),
       
   319 	AS(QZ_F9,  WKC_F9),
       
   320 	AS(QZ_F10, WKC_F10),
       
   321 	AS(QZ_F11, WKC_F11),
       
   322 	AS(QZ_F12, WKC_F12),
       
   323 
       
   324 	/* Numeric part */
       
   325 	AS(QZ_KP0,         WKC_NUM_0),
       
   326 	AS(QZ_KP1,         WKC_NUM_1),
       
   327 	AS(QZ_KP2,         WKC_NUM_2),
       
   328 	AS(QZ_KP3,         WKC_NUM_3),
       
   329 	AS(QZ_KP4,         WKC_NUM_4),
       
   330 	AS(QZ_KP5,         WKC_NUM_5),
       
   331 	AS(QZ_KP6,         WKC_NUM_6),
       
   332 	AS(QZ_KP7,         WKC_NUM_7),
       
   333 	AS(QZ_KP8,         WKC_NUM_8),
       
   334 	AS(QZ_KP9,         WKC_NUM_9),
       
   335 	AS(QZ_KP_DIVIDE,   WKC_NUM_DIV),
       
   336 	AS(QZ_KP_MULTIPLY, WKC_NUM_MUL),
       
   337 	AS(QZ_KP_MINUS,    WKC_NUM_MINUS),
       
   338 	AS(QZ_KP_PLUS,     WKC_NUM_PLUS),
       
   339 	AS(QZ_KP_ENTER,    WKC_NUM_ENTER),
       
   340 	AS(QZ_KP_PERIOD,   WKC_NUM_DECIMAL),
       
   341 
       
   342 	/* Other non-letter keys */
       
   343 	AS(QZ_SLASH,        WKC_SLASH),
       
   344 	AS(QZ_SEMICOLON,    WKC_SEMICOLON),
       
   345 	AS(QZ_EQUALS,       WKC_EQUALS),
       
   346 	AS(QZ_LEFTBRACKET,  WKC_L_BRACKET),
       
   347 	AS(QZ_BACKSLASH,    WKC_BACKSLASH),
       
   348 	AS(QZ_RIGHTBRACKET, WKC_R_BRACKET),
       
   349 
       
   350 	AS(QZ_QUOTE,   WKC_SINGLEQUOTE),
       
   351 	AS(QZ_COMMA,   WKC_COMMA),
       
   352 	AS(QZ_MINUS,   WKC_MINUS),
       
   353 	AS(QZ_PERIOD,  WKC_PERIOD)
       
   354 };
       
   355 
       
   356 
       
   357 static uint32 QZ_MapKey(unsigned short sym)
       
   358 {
       
   359 	const VkMapping *map;
       
   360 	uint32 key = 0;
       
   361 
       
   362 	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
       
   363 		if (sym == map->vk_from) {
       
   364 			key = map->map_to;
       
   365 			break;
       
   366 		}
       
   367 	}
       
   368 
       
   369 	if (_cocoa_video_data.current_mods & NSShiftKeyMask)     key |= WKC_SHIFT;
       
   370 	if (_cocoa_video_data.current_mods & NSControlKeyMask)   key |= (_patches.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_CTRL : WKC_META);
       
   371 	if (_cocoa_video_data.current_mods & NSAlternateKeyMask) key |= WKC_ALT;
       
   372 	if (_cocoa_video_data.current_mods & NSCommandKeyMask)   key |= (_patches.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_META : WKC_CTRL);
       
   373 
       
   374 	return key << 16;
       
   375 }
       
   376 
       
   377 static void QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL down)
       
   378 {
       
   379 	switch (keycode) {
       
   380 		case QZ_UP:    SB(_dirkeys, 1, 1, down); break;
       
   381 		case QZ_DOWN:  SB(_dirkeys, 3, 1, down); break;
       
   382 		case QZ_LEFT:  SB(_dirkeys, 0, 1, down); break;
       
   383 		case QZ_RIGHT: SB(_dirkeys, 2, 1, down); break;
       
   384 
       
   385 		case QZ_TAB: _cocoa_video_data.tab_is_down = down; break;
       
   386 
       
   387 		case QZ_RETURN:
       
   388 		case QZ_f:
       
   389 			if (down && (_cocoa_video_data.current_mods & NSCommandKeyMask)) {
       
   390 				_video_driver->ToggleFullscreen(!_fullscreen);
       
   391 			}
       
   392 			break;
       
   393 	}
       
   394 
       
   395 	if (down) {
       
   396 		uint32 pressed_key = QZ_MapKey(keycode) | unicode;
       
   397 		HandleKeypress(pressed_key);
       
   398 		DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, pressed_key);
       
   399 	} else {
       
   400 		DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode);
       
   401 	}
       
   402 }
       
   403 
       
   404 static void QZ_DoUnsidedModifiers(unsigned int newMods)
       
   405 {
       
   406 	const int mapping[] = { QZ_CAPSLOCK, QZ_LSHIFT, QZ_LCTRL, QZ_LALT, QZ_LMETA };
       
   407 
       
   408 	int i;
       
   409 	unsigned int bit;
       
   410 
       
   411 	if (_cocoa_video_data.current_mods == newMods) return;
       
   412 
       
   413 	/* Iterate through the bits, testing each against the current modifiers */
       
   414 	for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
       
   415 		unsigned int currentMask, newMask;
       
   416 
       
   417 		currentMask = _cocoa_video_data.current_mods & bit;
       
   418 		newMask     = newMods & bit;
       
   419 
       
   420 		if (currentMask && currentMask != newMask) { /* modifier up event */
       
   421 			/* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */
       
   422 			if (bit == NSAlphaShiftKeyMask) QZ_KeyEvent(mapping[i], 0, YES);
       
   423 			QZ_KeyEvent(mapping[i], 0, NO);
       
   424 		} else if (newMask && currentMask != newMask) { /* modifier down event */
       
   425 			QZ_KeyEvent(mapping[i], 0, YES);
       
   426 			/* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */
       
   427 			if (bit == NSAlphaShiftKeyMask) QZ_KeyEvent(mapping[i], 0, NO);
       
   428 		}
       
   429 	}
       
   430 
       
   431 	_cocoa_video_data.current_mods = newMods;
       
   432 }
       
   433 
       
   434 static void QZ_MouseMovedEvent(int x, int y)
       
   435 {
       
   436 	if (_cursor.fix_at) {
       
   437 		int dx = x - _cursor.pos.x;
       
   438 		int dy = y - _cursor.pos.y;
       
   439 
       
   440 		if (dx != 0 || dy != 0) {
       
   441 			_cursor.delta.x += dx;
       
   442 			_cursor.delta.y += dy;
       
   443 
       
   444 			QZ_WarpCursor(_cursor.pos.x, _cursor.pos.y);
       
   445 		}
       
   446 	} else {
       
   447 		_cursor.delta.x = x - _cursor.pos.x;
       
   448 		_cursor.delta.y = y - _cursor.pos.y;
       
   449 		_cursor.pos.x = x;
       
   450 		_cursor.pos.y = y;
       
   451 		_cursor.dirty = true;
       
   452 	}
       
   453 	HandleMouseEvents();
       
   454 }
       
   455 
       
   456 
       
   457 static void QZ_MouseButtonEvent(int button, BOOL down)
       
   458 {
       
   459 	switch (button) {
       
   460 		case 0:
       
   461 			if (down) {
       
   462 				_left_button_down = true;
       
   463 			} else {
       
   464 				_left_button_down = false;
       
   465 				_left_button_clicked = false;
       
   466 			}
       
   467 			HandleMouseEvents();
       
   468 			break;
       
   469 
       
   470 		case 1:
       
   471 			if (down) {
       
   472 				_right_button_down = true;
       
   473 				_right_button_clicked = true;
       
   474 			} else {
       
   475 				_right_button_down = false;
       
   476 			}
       
   477 			HandleMouseEvents();
       
   478 			break;
       
   479 	}
       
   480 }
       
   481 
       
   482 
       
   483 static inline NSPoint QZ_GetMouseLocation(NSEvent *event)
       
   484 {
       
   485 	NSPoint pt;
       
   486 
       
   487 	if (_cocoa_video_data.fullscreen) {
       
   488 		pt = [ NSEvent mouseLocation ];
       
   489 		pt.y = _cocoa_video_data.height - pt.y;
       
   490 	} else {
       
   491 		pt = [event locationInWindow];
       
   492 		pt = [_cocoa_video_data.qdview convertPoint:pt fromView:nil];
       
   493 	}
       
   494 
       
   495 	return pt;
       
   496 }
       
   497 
       
   498 static bool QZ_MouseIsInsideView(NSPoint *pt)
       
   499 {
       
   500 	if (_cocoa_video_data.fullscreen) {
       
   501 		return pt->x >= 0 && pt->y >= 0 && pt->x < _cocoa_video_data.width && pt->y < _cocoa_video_data.height;
       
   502 	} else {
       
   503 		return [ _cocoa_video_data.qdview mouse:*pt inRect:[ _cocoa_video_data.qdview bounds ] ];
       
   504 	}
       
   505 }
       
   506 
       
   507 
       
   508 static bool QZ_PollEvent()
       
   509 {
       
   510 	NSEvent *event;
       
   511 	NSPoint pt;
       
   512 	NSString *chars;
       
   513 #ifdef _DEBUG
       
   514 	uint32 et0, et;
       
   515 #endif
       
   516 
       
   517 #ifdef _DEBUG
       
   518 	et0 = GetTick();
       
   519 #endif
       
   520 	event = [ NSApp nextEventMatchingMask:NSAnyEventMask
       
   521 			untilDate: [ NSDate distantPast ]
       
   522 			inMode: NSDefaultRunLoopMode dequeue:YES ];
       
   523 #ifdef _DEBUG
       
   524 	et = GetTick();
       
   525 	_cocoa_video_data.tEvent+= et - et0;
       
   526 #endif
       
   527 
       
   528 	if (event == nil) return false;
       
   529 	if (!_cocoa_video_data.active) {
       
   530 		QZ_ShowMouse();
       
   531 		[NSApp sendEvent:event];
       
   532 		return true;
       
   533 	}
       
   534 
       
   535 	QZ_DoUnsidedModifiers( [ event modifierFlags ] );
       
   536 
       
   537 	switch ([event type]) {
       
   538 		case NSMouseMoved:
       
   539 		case NSOtherMouseDragged:
       
   540 		case NSRightMouseDragged:
       
   541 		case NSLeftMouseDragged:
       
   542 			pt = QZ_GetMouseLocation(event);
       
   543 			if (!QZ_MouseIsInsideView(&pt) &&
       
   544 					!_cocoa_video_data.emulating_right_button) {
       
   545 				QZ_ShowMouse();
       
   546 				[NSApp sendEvent:event];
       
   547 				break;
       
   548 			}
       
   549 
       
   550 			QZ_HideMouse();
       
   551 			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
       
   552 			break;
       
   553 
       
   554 		case NSLeftMouseDown:
       
   555 		{
       
   556 			uint32 keymask = 0;
       
   557 			if (_patches.right_mouse_btn_emulation == RMBE_COMMAND) keymask |= NSCommandKeyMask;
       
   558 			if (_patches.right_mouse_btn_emulation == RMBE_CONTROL) keymask |= NSControlKeyMask;
       
   559 
       
   560 			pt = QZ_GetMouseLocation(event);
       
   561 
       
   562 			if (!([ event modifierFlags ] & keymask) ||
       
   563 					!QZ_MouseIsInsideView(&pt)) {
       
   564 				[NSApp sendEvent:event];
       
   565 			}
       
   566 
       
   567 			if (!QZ_MouseIsInsideView(&pt)) {
       
   568 				QZ_ShowMouse();
       
   569 				break;
       
   570 			}
       
   571 
       
   572 			QZ_HideMouse();
       
   573 			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
       
   574 
       
   575 			/* Right mouse button emulation */
       
   576 			if ([ event modifierFlags ] & keymask) {
       
   577 				_cocoa_video_data.emulating_right_button = true;
       
   578 				QZ_MouseButtonEvent(1, YES);
       
   579 			} else {
       
   580 				QZ_MouseButtonEvent(0, YES);
       
   581 			}
       
   582 			break;
       
   583 		}
       
   584 		case NSLeftMouseUp:
       
   585 			[NSApp sendEvent:event];
       
   586 
       
   587 			pt = QZ_GetMouseLocation(event);
       
   588 			if (!QZ_MouseIsInsideView(&pt)) {
       
   589 				QZ_ShowMouse();
       
   590 				break;
       
   591 			}
       
   592 
       
   593 			QZ_HideMouse();
       
   594 			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
       
   595 
       
   596 			/* Right mouse button emulation */
       
   597 			if (_cocoa_video_data.emulating_right_button) {
       
   598 				_cocoa_video_data.emulating_right_button = false;
       
   599 				QZ_MouseButtonEvent(1, NO);
       
   600 			} else {
       
   601 				QZ_MouseButtonEvent(0, NO);
       
   602 			}
       
   603 			break;
       
   604 
       
   605 		case NSRightMouseDown:
       
   606 			pt = QZ_GetMouseLocation(event);
       
   607 			if (!QZ_MouseIsInsideView(&pt)) {
       
   608 				QZ_ShowMouse();
       
   609 				[NSApp sendEvent:event];
       
   610 				break;
       
   611 			}
       
   612 
       
   613 			QZ_HideMouse();
       
   614 			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
       
   615 			QZ_MouseButtonEvent(1, YES);
       
   616 			break;
       
   617 
       
   618 		case NSRightMouseUp:
       
   619 			pt = QZ_GetMouseLocation(event);
       
   620 			if (!QZ_MouseIsInsideView(&pt)) {
       
   621 				QZ_ShowMouse();
       
   622 				[NSApp sendEvent:event];
       
   623 				break;
       
   624 			}
       
   625 
       
   626 			QZ_HideMouse();
       
   627 			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
       
   628 			QZ_MouseButtonEvent(1, NO);
       
   629 			break;
       
   630 
       
   631 #if 0
       
   632 		/* This is not needed since openttd currently only use two buttons */
       
   633 		case NSOtherMouseDown:
       
   634 			pt = QZ_GetMouseLocation(event);
       
   635 			if (!QZ_MouseIsInsideView(&pt)) {
       
   636 				QZ_ShowMouse();
       
   637 				[NSApp sendEvent:event];
       
   638 				break;
       
   639 			}
       
   640 
       
   641 			QZ_HideMouse();
       
   642 			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
       
   643 			QZ_MouseButtonEvent([ event buttonNumber ], YES);
       
   644 			break;
       
   645 
       
   646 		case NSOtherMouseUp:
       
   647 			pt = QZ_GetMouseLocation(event);
       
   648 			if (!QZ_MouseIsInsideView(&pt)) {
       
   649 				QZ_ShowMouse();
       
   650 				[NSApp sendEvent:event];
       
   651 				break;
       
   652 			}
       
   653 
       
   654 			QZ_HideMouse();
       
   655 			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
       
   656 			QZ_MouseButtonEvent([ event buttonNumber ], NO);
       
   657 			break;
       
   658 #endif
       
   659 
       
   660 		case NSKeyDown:
       
   661 			/* Quit, hide and minimize */
       
   662 			switch ([event keyCode]) {
       
   663 				case QZ_q:
       
   664 				case QZ_h:
       
   665 				case QZ_m:
       
   666 					if ([ event modifierFlags ] & NSCommandKeyMask) {
       
   667 						[NSApp sendEvent:event];
       
   668 					}
       
   669 					break;
       
   670 			}
       
   671 
       
   672 			chars = [ event characters ];
       
   673 			QZ_KeyEvent([event keyCode], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, YES);
       
   674 			break;
       
   675 
       
   676 		case NSKeyUp:
       
   677 			/* Quit, hide and minimize */
       
   678 			switch ([event keyCode]) {
       
   679 				case QZ_q:
       
   680 				case QZ_h:
       
   681 				case QZ_m:
       
   682 					if ([ event modifierFlags ] & NSCommandKeyMask) {
       
   683 						[NSApp sendEvent:event];
       
   684 					}
       
   685 					break;
       
   686 			}
       
   687 
       
   688 			chars = [ event characters ];
       
   689 			QZ_KeyEvent([event keyCode], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, NO);
       
   690 			break;
       
   691 
       
   692 		case NSScrollWheel:
       
   693 			if ([ event deltaY ] > 0.0) { /* Scroll up */
       
   694 				_cursor.wheel--;
       
   695 			} else if ([ event deltaY ] < 0.0) { /* Scroll down */
       
   696 				_cursor.wheel++;
       
   697 			} /* else: deltaY was 0.0 and we don't want to do anything */
       
   698 
       
   699 			/* Set the scroll count for scrollwheel scrolling */
       
   700 			_cursor.h_wheel -= (int)([ event deltaX ]* 5 * _patches.scrollwheel_multiplier);
       
   701 			_cursor.v_wheel -= (int)([ event deltaY ]* 5 * _patches.scrollwheel_multiplier);
       
   702 			break;
       
   703 
       
   704 		default:
       
   705 			[NSApp sendEvent:event];
       
   706 	}
       
   707 
       
   708 	return true;
       
   709 }
       
   710 
       
   711 
       
   712 static void QZ_GameLoop()
       
   713 {
       
   714 	uint32 cur_ticks = GetTick();
       
   715 	uint32 last_cur_ticks = cur_ticks;
       
   716 	uint32 next_tick = cur_ticks + 30;
       
   717 	uint32 pal_tick = 0;
       
   718 #ifdef _DEBUG
       
   719 	uint32 et0, et, st0, st;
       
   720 #endif
       
   721 	int i;
       
   722 
       
   723 #ifdef _DEBUG
       
   724 	et0 = GetTick();
       
   725 	st = 0;
       
   726 #endif
       
   727 
       
   728 	_screen.dst_ptr = _cocoa_video_data.pixels;
       
   729 	DisplaySplashImage();
       
   730 	QZ_CheckPaletteAnim();
       
   731 	QZ_Draw();
       
   732 	CSleep(1);
       
   733 
       
   734 	for (i = 0; i < 2; i++) GameLoop();
       
   735 
       
   736 	_screen.dst_ptr = _cocoa_video_data.pixels;
       
   737 	UpdateWindows();
       
   738 	QZ_CheckPaletteAnim();
       
   739 	QZ_Draw();
       
   740 	CSleep(1);
       
   741 
       
   742 	for (;;) {
       
   743 		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
       
   744 		InteractiveRandom(); // randomness
       
   745 
       
   746 		while (QZ_PollEvent()) {}
       
   747 
       
   748 		if (_exit_game) break;
       
   749 
       
   750 #if defined(_DEBUG)
       
   751 		if (_cocoa_video_data.current_mods & NSShiftKeyMask)
       
   752 #else
       
   753 		if (_cocoa_video_data.tab_is_down)
       
   754 #endif
       
   755 		{
       
   756 			if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
       
   757 		} else if (_fast_forward & 2) {
       
   758 			_fast_forward = 0;
       
   759 		}
       
   760 
       
   761 		cur_ticks = GetTick();
       
   762 		if (cur_ticks >= next_tick || (_fast_forward && !_pause_game) || cur_ticks < prev_cur_ticks) {
       
   763 			_realtime_tick += cur_ticks - last_cur_ticks;
       
   764 			last_cur_ticks = cur_ticks;
       
   765 			next_tick = cur_ticks + 30;
       
   766 
       
   767 			_ctrl_pressed = !!(_cocoa_video_data.current_mods & ( _patches.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask));
       
   768 			_shift_pressed = !!(_cocoa_video_data.current_mods & NSShiftKeyMask);
       
   769 
       
   770 			GameLoop();
       
   771 
       
   772 			_screen.dst_ptr = _cocoa_video_data.pixels;
       
   773 			UpdateWindows();
       
   774 			if (++pal_tick > 4) {
       
   775 				QZ_CheckPaletteAnim();
       
   776 				pal_tick = 1;
       
   777 			}
       
   778 			QZ_Draw();
       
   779 		} else {
       
   780 #ifdef _DEBUG
       
   781 			st0 = GetTick();
       
   782 #endif
       
   783 			CSleep(1);
       
   784 #ifdef _DEBUG
       
   785 			st += GetTick() - st0;
       
   786 #endif
       
   787 			_screen.dst_ptr = _cocoa_video_data.pixels;
       
   788 			DrawChatMessage();
       
   789 			DrawMouseCursor();
       
   790 			QZ_Draw();
       
   791 		}
       
   792 	}
       
   793 
       
   794 #ifdef _DEBUG
       
   795 	et = GetTick();
       
   796 
       
   797 	DEBUG(driver, 1, "cocoa_v: nextEventMatchingMask took %i ms total", _cocoa_video_data.tEvent);
       
   798 	DEBUG(driver, 1, "cocoa_v: game loop took %i ms total (%i ms without sleep)", et - et0, et - et0 - st);
       
   799 	DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop total) is %f%%", (double)_cocoa_video_data.tEvent / (double)(et - et0) * 100);
       
   800 	DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop without sleep total) is %f%%", (double)_cocoa_video_data.tEvent / (double)(et - et0 - st) * 100);
       
   801 #endif
       
   802 }
       
   803 
       
   804 
       
   805 /******************************************************************************
       
   806  *                             Windowed mode                                  *
       
   807  ******************************************************************************/
       
   808 
       
   809 /* This function makes the *game region* of the window 100% opaque.
       
   810  * The genie effect uses the alpha component. Otherwise,
       
   811  * it doesn't seem to matter what value it has.
       
   812  */
       
   813 static void QZ_SetPortAlphaOpaque()
       
   814 {
       
   815 	if (_cocoa_video_data.device_bpp == 32) {
       
   816 		uint32* pixels = (uint32*)_cocoa_video_data.realpixels;
       
   817 		uint32 rowPixels = _cocoa_video_data.pitch / 4;
       
   818 		uint32 i;
       
   819 		uint32 j;
       
   820 
       
   821 		for (i = 0; i < _cocoa_video_data.height; i++)
       
   822 			for (j = 0; j < _cocoa_video_data.width; j++) {
       
   823 			pixels[i * rowPixels + j] |= 0xFF000000;
       
   824 		}
       
   825 	}
       
   826 }
       
   827 
       
   828 
       
   829 @implementation OTTD_QuartzWindow
       
   830 
       
   831 /* we override these methods to fix the miniaturize animation/dock icon bug */
       
   832 - (void)miniaturize:(id)sender
       
   833 {
       
   834 	/* make the alpha channel opaque so anim won't have holes in it */
       
   835 	QZ_SetPortAlphaOpaque ();
       
   836 
       
   837 	/* window is hidden now */
       
   838 	_cocoa_video_data.active = false;
       
   839 
       
   840 	QZ_ShowMouse();
       
   841 
       
   842 	[ super miniaturize:sender ];
       
   843 }
       
   844 
       
   845 - (void)display
       
   846 {
       
   847 	/* This method fires just before the window deminaturizes from the Dock.
       
   848 	 * We'll save the current visible surface, let the window manager redraw any
       
   849 	 * UI elements, and restore the surface. This way, no expose event
       
   850 	 * is required, and the deminiaturize works perfectly.
       
   851 	 */
       
   852 
       
   853 	QZ_SetPortAlphaOpaque();
       
   854 
       
   855 	/* save current visible surface */
       
   856 	[ self cacheImageInRect:[ _cocoa_video_data.qdview frame ] ];
       
   857 
       
   858 	/* let the window manager redraw controls, border, etc */
       
   859 	[ super display ];
       
   860 
       
   861 	/* restore visible surface */
       
   862 	[ self restoreCachedImage ];
       
   863 
       
   864 	/* window is visible again */
       
   865 	_cocoa_video_data.active = true;
       
   866 }
       
   867 
       
   868 - (void)setFrame:(NSRect)frameRect display:(BOOL)flag
       
   869 {
       
   870 	NSRect newViewFrame;
       
   871 	CGrafPtr thePort;
       
   872 
       
   873 	[ super setFrame:frameRect display:flag ];
       
   874 
       
   875 	/* Don't do anything if the window is currently beign created */
       
   876 	if (_cocoa_video_data.issetting) return;
       
   877 
       
   878 	if (_cocoa_video_data.window == nil) return;
       
   879 
       
   880 	newViewFrame = [ _cocoa_video_data.qdview frame ];
       
   881 
       
   882 	/* Update the pixels and pitch */
       
   883 	thePort = (OpaqueGrafPtr*) [ _cocoa_video_data.qdview qdPort ];
       
   884 	LockPortBits(thePort);
       
   885 
       
   886 	_cocoa_video_data.realpixels = GetPixBaseAddr(GetPortPixMap(thePort));
       
   887 	_cocoa_video_data.pitch      = GetPixRowBytes(GetPortPixMap(thePort));
       
   888 
       
   889 	/* _cocoa_video_data.realpixels now points to the window's pixels
       
   890 	 * We want it to point to the *view's* pixels
       
   891 	 */
       
   892 	{
       
   893 		int vOffset = [ _cocoa_video_data.window frame ].size.height - newViewFrame.size.height - newViewFrame.origin.y;
       
   894 		int hOffset = newViewFrame.origin.x;
       
   895 
       
   896 		_cocoa_video_data.realpixels = (uint8*)_cocoa_video_data.realpixels + (vOffset * _cocoa_video_data.pitch) + hOffset * (_cocoa_video_data.device_bpp / 8);
       
   897 	}
       
   898 
       
   899 	UnlockPortBits(thePort);
       
   900 
       
   901 	/* Allocate new buffer */
       
   902 	free(_cocoa_video_data.pixels);
       
   903 	_cocoa_video_data.pixels = malloc(newViewFrame.size.width * newViewFrame.size.height * _cocoa_video_data.blitter_bpp / 8);
       
   904 	assert(_cocoa_video_data.pixels != NULL);
       
   905 
       
   906 
       
   907 	/* Tell the game that the resolution changed */
       
   908 	_cocoa_video_data.width = newViewFrame.size.width;
       
   909 	_cocoa_video_data.height = newViewFrame.size.height;
       
   910 
       
   911 	_screen.width = _cocoa_video_data.width;
       
   912 	_screen.height = _cocoa_video_data.height;
       
   913 	_screen.pitch = _cocoa_video_data.width;
       
   914 
       
   915 	GameSizeChanged();
       
   916 
       
   917 	/* Redraw screen */
       
   918 	_cocoa_video_data.num_dirty_rects = MAX_DIRTY_RECTS;
       
   919 }
       
   920 
       
   921 - (void)appDidHide:(NSNotification*)note
       
   922 {
       
   923 	_cocoa_video_data.active = false;
       
   924 }
       
   925 
       
   926 
       
   927 - (void)appWillUnhide:(NSNotification*)note
       
   928 {
       
   929 	QZ_SetPortAlphaOpaque ();
       
   930 
       
   931 	/* save current visible surface */
       
   932 	[ self cacheImageInRect:[ _cocoa_video_data.qdview frame ] ];
       
   933 }
       
   934 
       
   935 - (void)appDidUnhide:(NSNotification*)note
       
   936 {
       
   937 	/* restore cached image, since it may not be current, post expose event too */
       
   938 	[ self restoreCachedImage ];
       
   939 
       
   940 	_cocoa_video_data.active = true;
       
   941 }
       
   942 
       
   943 
       
   944 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag
       
   945 {
       
   946 	/* Make our window subclass receive these application notifications */
       
   947 	[ [ NSNotificationCenter defaultCenter ] addObserver:self
       
   948 	selector:@selector(appDidHide:) name:NSApplicationDidHideNotification object:NSApp ];
       
   949 
       
   950 	[ [ NSNotificationCenter defaultCenter ] addObserver:self
       
   951 	selector:@selector(appDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp ];
       
   952 
       
   953 	[ [ NSNotificationCenter defaultCenter ] addObserver:self
       
   954 	selector:@selector(appWillUnhide:) name:NSApplicationWillUnhideNotification object:NSApp ];
       
   955 
       
   956 	return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ];
       
   957 }
       
   958 
       
   959 @end
       
   960 
       
   961 @implementation OTTD_QuartzWindowDelegate
       
   962 - (BOOL)windowShouldClose:(id)sender
       
   963 {
       
   964 	HandleExitGameRequest();
       
   965 
       
   966 	return NO;
       
   967 }
       
   968 
       
   969 - (void)windowDidBecomeKey:(NSNotification*)aNotification
       
   970 {
       
   971 	_cocoa_video_data.active = true;
       
   972 }
       
   973 
       
   974 - (void)windowDidResignKey:(NSNotification*)aNotification
       
   975 {
       
   976 	_cocoa_video_data.active = false;
       
   977 }
       
   978 
       
   979 - (void)windowDidBecomeMain:(NSNotification*)aNotification
       
   980 {
       
   981 	_cocoa_video_data.active = true;
       
   982 }
       
   983 
       
   984 - (void)windowDidResignMain:(NSNotification*)aNotification
       
   985 {
       
   986 	_cocoa_video_data.active = false;
       
   987 }
       
   988 
       
   989 @end
       
   990 
       
   991 
       
   992 static void QZ_UpdateWindowPalette(uint start, uint count)
       
   993 {
       
   994 	uint i;
       
   995 
       
   996 	switch (_cocoa_video_data.device_bpp) {
       
   997 		case 32:
       
   998 			for (i = start; i < start + count; i++) {
       
   999 				uint32 clr32 = 0xff000000;
       
  1000 				clr32 |= (uint32)_cur_palette[i].r << 16;
       
  1001 				clr32 |= (uint32)_cur_palette[i].g << 8;
       
  1002 				clr32 |= (uint32)_cur_palette[i].b;
       
  1003 				_cocoa_video_data.palette32[i] = clr32;
       
  1004 			}
       
  1005 			break;
       
  1006 		case 16:
       
  1007 			for (i = start; i < start + count; i++) {
       
  1008 				uint16 clr16 = 0x0000;
       
  1009 				clr16 |= (uint16)((_cur_palette[i].r >> 3) & 0x1f) << 10;
       
  1010 				clr16 |= (uint16)((_cur_palette[i].g >> 3) & 0x1f) << 5;
       
  1011 				clr16 |= (uint16)((_cur_palette[i].b >> 3) & 0x1f);
       
  1012 				_cocoa_video_data.palette16[i] = clr16;
       
  1013 			}
       
  1014 			break;
       
  1015 	}
       
  1016 
       
  1017 	_cocoa_video_data.num_dirty_rects = MAX_DIRTY_RECTS;
       
  1018 }
       
  1019 
       
  1020 static inline void QZ_WindowBlitIndexedPixelsToView32(uint left, uint top, uint right, uint bottom)
       
  1021 {
       
  1022 	uint32* dst = (uint32*)_cocoa_video_data.realpixels;
       
  1023 	uint width = _cocoa_video_data.width;
       
  1024 	uint pitch = _cocoa_video_data.pitch / 4;
       
  1025 	uint x;
       
  1026 	uint y;
       
  1027 
       
  1028 	switch (_cocoa_video_data.blitter_bpp) {
       
  1029 		case 8: {
       
  1030 				const uint32* pal = _cocoa_video_data.palette32;
       
  1031 				const uint8*  src = (uint8*) _cocoa_video_data.pixels;
       
  1032 
       
  1033 				for (y = top; y < bottom; y++) {
       
  1034 					for (x = left; x < right; x++) {
       
  1035 						dst[y * pitch + x] = pal[src[y * width + x]];
       
  1036 					}
       
  1037 				}
       
  1038 			}
       
  1039 			break;
       
  1040 		case 32: {
       
  1041 				const uint32* src = (uint32*) _cocoa_video_data.pixels;
       
  1042 
       
  1043 				dst += top * pitch + left;
       
  1044 				src += top * width + left;
       
  1045 
       
  1046 				for (y = top; y < bottom; y++, dst+= pitch, src+= width)
       
  1047 					memcpy(dst, src, (right - left) * 4);
       
  1048 			}
       
  1049 			break;
       
  1050 	}
       
  1051 }
       
  1052 
       
  1053 /**
       
  1054  * This function copies pixels from the screen buffer in 16bpp windowed mode. It assumes
       
  1055  * that the blitter is 8bpp since the driver only supports 8 and 32 bpp blitters, and we
       
  1056  * don't allow a blitter with a higer bpp than the display in windowed mode.
       
  1057  *
       
  1058  * @param left The x coord for the left edge of the box to blit.
       
  1059  * @param top The y coord for the top edge of the box to blit.
       
  1060  * @param right The x coord for the right edge of the box to blit.
       
  1061  * @param bottom The y coord for the bottom edge of the box to blit.
       
  1062  */
       
  1063 static inline void QZ_WindowBlitIndexedPixelsToView16(uint left, uint top, uint right, uint bottom)
       
  1064 {
       
  1065 	const uint16* pal   = _cocoa_video_data.palette16;
       
  1066 	const uint8*  src   = (uint8*) _cocoa_video_data.pixels;
       
  1067 	uint16*       dst   = (uint16*)_cocoa_video_data.realpixels;
       
  1068 	uint          width = _cocoa_video_data.width;
       
  1069 	uint          pitch = _cocoa_video_data.pitch / 2;
       
  1070 	uint x;
       
  1071 	uint y;
       
  1072 
       
  1073 	for (y = top; y < bottom; y++) {
       
  1074 		for (x = left; x < right; x++) {
       
  1075 			dst[y * pitch + x] = pal[src[y * width + x]];
       
  1076 		}
       
  1077 	}
       
  1078 }
       
  1079 
       
  1080 static inline void QZ_WindowBlitIndexedPixelsToView(int left, int top, int right, int bottom)
       
  1081 {
       
  1082 	switch (_cocoa_video_data.device_bpp) {
       
  1083 		case 32: QZ_WindowBlitIndexedPixelsToView32(left, top, right, bottom); break;
       
  1084 		case 16: QZ_WindowBlitIndexedPixelsToView16(left, top, right, bottom); break;
       
  1085 	}
       
  1086 }
       
  1087 
       
  1088 static bool _resize_icon[] = {
       
  1089 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
       
  1090 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
       
  1091 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
       
  1092 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
       
  1093 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
       
  1094 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
       
  1095 	0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0,
       
  1096 	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
       
  1097 	0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
       
  1098 	0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
       
  1099 	0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
       
  1100 	0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
       
  1101 	0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
       
  1102 	0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
       
  1103 	0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
       
  1104 	1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0
       
  1105 };
       
  1106 
       
  1107 static void QZ_DrawResizeIcon()
       
  1108 {
       
  1109 	int xoff = _cocoa_video_data.width - 16;
       
  1110 	int yoff = _cocoa_video_data.height - 16;
       
  1111 	int x;
       
  1112 	int y;
       
  1113 
       
  1114 	for (y = 0; y < 16; y++) {
       
  1115 		uint16* trg16 = (uint16*)_cocoa_video_data.realpixels + (yoff + y) * _cocoa_video_data.pitch / 2 + xoff;
       
  1116 		uint32* trg32 = (uint32*)_cocoa_video_data.realpixels + (yoff + y) * _cocoa_video_data.pitch / 4 + xoff;
       
  1117 
       
  1118 		for (x = 0; x < 16; x++, trg16++, trg32++) {
       
  1119 			if (!_resize_icon[y * 16 + x]) continue;
       
  1120 
       
  1121 			switch (_cocoa_video_data.device_bpp) {
       
  1122 				case 32: *trg32 = 0xff000000; break;
       
  1123 				case 16: *trg16 = 0x0000;     break;
       
  1124 			}
       
  1125 		}
       
  1126 	}
       
  1127 }
       
  1128 
       
  1129 static void QZ_DrawWindow()
       
  1130 {
       
  1131 	int i;
       
  1132 	RgnHandle dirty, temp;
       
  1133 
       
  1134 	/* Check if we need to do anything */
       
  1135 	if (_cocoa_video_data.num_dirty_rects == 0 ||
       
  1136 			[ _cocoa_video_data.window isMiniaturized ]) {
       
  1137 		return;
       
  1138 	}
       
  1139 
       
  1140 	if (_cocoa_video_data.num_dirty_rects >= MAX_DIRTY_RECTS) {
       
  1141 		_cocoa_video_data.num_dirty_rects = 1;
       
  1142 		_cocoa_video_data.dirty_rects[0].left = 0;
       
  1143 		_cocoa_video_data.dirty_rects[0].top = 0;
       
  1144 		_cocoa_video_data.dirty_rects[0].right = _cocoa_video_data.width;
       
  1145 		_cocoa_video_data.dirty_rects[0].bottom = _cocoa_video_data.height;
       
  1146 	}
       
  1147 
       
  1148 	dirty = NewRgn();
       
  1149 	temp  = NewRgn();
       
  1150 
       
  1151 	SetEmptyRgn(dirty);
       
  1152 
       
  1153 	/* Build the region of dirty rectangles */
       
  1154 	for (i = 0; i < _cocoa_video_data.num_dirty_rects; i++) {
       
  1155 		QZ_WindowBlitIndexedPixelsToView(
       
  1156 			_cocoa_video_data.dirty_rects[i].left,
       
  1157 			_cocoa_video_data.dirty_rects[i].top,
       
  1158 			_cocoa_video_data.dirty_rects[i].right,
       
  1159 			_cocoa_video_data.dirty_rects[i].bottom
       
  1160 		);
       
  1161 
       
  1162 		MacSetRectRgn(
       
  1163 			temp,
       
  1164 			_cocoa_video_data.dirty_rects[i].left,
       
  1165 			_cocoa_video_data.dirty_rects[i].top,
       
  1166 			_cocoa_video_data.dirty_rects[i].right,
       
  1167 			_cocoa_video_data.dirty_rects[i].bottom
       
  1168 		);
       
  1169 		MacUnionRgn(dirty, temp, dirty);
       
  1170 	}
       
  1171 
       
  1172 	QZ_DrawResizeIcon();
       
  1173 
       
  1174 	/* Flush the dirty region */
       
  1175 	QDFlushPortBuffer( (OpaqueGrafPtr*) [ _cocoa_video_data.qdview qdPort ], dirty);
       
  1176 	DisposeRgn(dirty);
       
  1177 	DisposeRgn(temp);
       
  1178 
       
  1179 	_cocoa_video_data.num_dirty_rects = 0;
       
  1180 }
       
  1181 
       
  1182 
       
  1183 extern const char _openttd_revision[];
       
  1184 
       
  1185 static const char* QZ_SetVideoWindowed(uint width, uint height)
       
  1186 {
       
  1187 	char caption[50];
       
  1188 	NSString *nsscaption;
       
  1189 	unsigned int style;
       
  1190 	NSRect contentRect;
       
  1191 	BOOL isCustom = NO;
       
  1192 
       
  1193 	if (_cocoa_video_data.blitter_bpp > _cocoa_video_data.device_bpp) {
       
  1194 		error("Cocoa: Cannot use a blitter with a higer screen depth than the display when running in windowed mode.");
       
  1195 	}
       
  1196 
       
  1197 	if (width > _cocoa_video_data.device_width)
       
  1198 		width = _cocoa_video_data.device_width;
       
  1199 	if (height > _cocoa_video_data.device_height)
       
  1200 		height = _cocoa_video_data.device_height;
       
  1201 
       
  1202 	_cocoa_video_data.width = width;
       
  1203 	_cocoa_video_data.height = height;
       
  1204 
       
  1205 	contentRect = NSMakeRect(0, 0, width, height);
       
  1206 
       
  1207 	/* Check if we should completely destroy the previous mode
       
  1208 	 * - If it is fullscreen
       
  1209 	 */
       
  1210 	if (_cocoa_video_data.isset && _cocoa_video_data.fullscreen)
       
  1211 		QZ_UnsetVideoMode();
       
  1212 
       
  1213 	/* Check if we should recreate the window */
       
  1214 	if (_cocoa_video_data.window == nil) {
       
  1215 		/* Set the window style */
       
  1216 		style = NSTitledWindowMask;
       
  1217 		style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
       
  1218 		style |= NSResizableWindowMask;
       
  1219 
       
  1220 		/* Manually create a window, avoids having a nib file resource */
       
  1221 		_cocoa_video_data.window = [ [ OTTD_QuartzWindow alloc ]
       
  1222 										initWithContentRect:contentRect
       
  1223 										styleMask:style
       
  1224 										backing:NSBackingStoreBuffered
       
  1225 										defer:NO ];
       
  1226 
       
  1227 		if (_cocoa_video_data.window == nil)
       
  1228 			return "Could not create the Cocoa window";
       
  1229 
       
  1230 		snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
       
  1231 		nsscaption = [ [ NSString alloc ] initWithCString:caption ];
       
  1232 		[ _cocoa_video_data.window setTitle:nsscaption ];
       
  1233 		[ _cocoa_video_data.window setMiniwindowTitle:nsscaption ];
       
  1234 		[ nsscaption release ];
       
  1235 
       
  1236 		[ _cocoa_video_data.window setAcceptsMouseMovedEvents:YES ];
       
  1237 		[ _cocoa_video_data.window setViewsNeedDisplay:NO ];
       
  1238 
       
  1239 		[ _cocoa_video_data.window setDelegate: [ [ [ OTTD_QuartzWindowDelegate alloc ] init ] autorelease ] ];
       
  1240 	} else {
       
  1241 		/* We already have a window, just change its size */
       
  1242 		if (!isCustom) {
       
  1243 			[ _cocoa_video_data.window setContentSize:contentRect.size ];
       
  1244 			// Ensure frame height - title bar height >= view height
       
  1245 			contentRect.size.height = Clamp(height, 0, [ _cocoa_video_data.window frame ].size.height - 22 /* 22 is the height of title bar of window*/);
       
  1246 			height = contentRect.size.height;
       
  1247 			[ _cocoa_video_data.qdview setFrameSize:contentRect.size ];
       
  1248 		}
       
  1249 	}
       
  1250 
       
  1251 	// Update again
       
  1252 	_cocoa_video_data.width = width;
       
  1253 	_cocoa_video_data.height = height;
       
  1254 
       
  1255 	[ _cocoa_video_data.window center ];
       
  1256 
       
  1257 	/* Only recreate the view if it doesn't already exist */
       
  1258 	if (_cocoa_video_data.qdview == nil) {
       
  1259 		_cocoa_video_data.qdview = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
       
  1260 		[ _cocoa_video_data.qdview setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
       
  1261 		[ [ _cocoa_video_data.window contentView ] addSubview:_cocoa_video_data.qdview ];
       
  1262 		[ _cocoa_video_data.qdview release ];
       
  1263 		[ _cocoa_video_data.window makeKeyAndOrderFront:nil ];
       
  1264 	}
       
  1265 
       
  1266 	CGrafPtr thePort = (OpaqueGrafPtr*) [ _cocoa_video_data.qdview qdPort ];
       
  1267 
       
  1268 	LockPortBits(thePort);
       
  1269 	_cocoa_video_data.realpixels = GetPixBaseAddr(GetPortPixMap(thePort));
       
  1270 	_cocoa_video_data.pitch = GetPixRowBytes(GetPortPixMap(thePort));
       
  1271 	UnlockPortBits(thePort);
       
  1272 
       
  1273 	/* _cocoa_video_data.realpixels now points to the window's pixels
       
  1274 	 * We want it to point to the *view's* pixels
       
  1275 	 */
       
  1276 	{
       
  1277 		int vOffset = [ _cocoa_video_data.window frame ].size.height - [ _cocoa_video_data.qdview frame ].size.height - [ _cocoa_video_data.qdview frame ].origin.y;
       
  1278 		int hOffset = [ _cocoa_video_data.qdview frame ].origin.x;
       
  1279 
       
  1280 		_cocoa_video_data.realpixels = (uint8*)_cocoa_video_data.realpixels + (vOffset * _cocoa_video_data.pitch) + hOffset * (_cocoa_video_data.device_bpp / 8);
       
  1281 	}
       
  1282 
       
  1283 	free(_cocoa_video_data.pixels);
       
  1284 	_cocoa_video_data.pixels = malloc(width * height * _cocoa_video_data.blitter_bpp / 8);
       
  1285 	if (_cocoa_video_data.pixels == NULL) return "Failed to allocate 8-bit buffer";
       
  1286 
       
  1287 	_cocoa_video_data.fullscreen = false;
       
  1288 
       
  1289 	return NULL;
       
  1290 }
       
  1291 
       
  1292 
       
  1293 /******************************************************************************
       
  1294  *                             Fullscreen mode                                *
       
  1295  ******************************************************************************/
       
  1296 
       
  1297 /* Gamma functions to try to hide the flash from a rez switch
       
  1298  * Fade the display from normal to black
       
  1299  * Save gamma tables for fade back to normal
       
  1300  */
       
  1301 static uint32 QZ_FadeGammaOut(OTTD_QuartzGammaTable* table)
       
  1302 {
       
  1303 	CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE];
       
  1304 	CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE];
       
  1305 	CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE];
       
  1306 	float percent;
       
  1307 	int j;
       
  1308 	unsigned int actual;
       
  1309 
       
  1310 	if (CGGetDisplayTransferByTable(
       
  1311 				_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE,
       
  1312 				table->red, table->green, table->blue, &actual
       
  1313 			) != CGDisplayNoErr ||
       
  1314 			actual != QZ_GAMMA_TABLE_SIZE) {
       
  1315 		return 1;
       
  1316 	}
       
  1317 
       
  1318 	memcpy(redTable,   table->red,   sizeof(redTable));
       
  1319 	memcpy(greenTable, table->green, sizeof(greenTable));
       
  1320 	memcpy(blueTable,  table->blue,  sizeof(greenTable));
       
  1321 
       
  1322 	for (percent = 1.0; percent >= 0.0; percent -= 0.01) {
       
  1323 		for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
       
  1324 			redTable[j]   = redTable[j]   * percent;
       
  1325 			greenTable[j] = greenTable[j] * percent;
       
  1326 			blueTable[j]  = blueTable[j]  * percent;
       
  1327 		}
       
  1328 
       
  1329 		if (CGSetDisplayTransferByTable(
       
  1330 					_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE,
       
  1331 					redTable, greenTable, blueTable
       
  1332 				) != CGDisplayNoErr) {
       
  1333 			CGDisplayRestoreColorSyncSettings();
       
  1334 			return 1;
       
  1335 		}
       
  1336 
       
  1337 		CSleep(10);
       
  1338 	}
       
  1339 
       
  1340 	return 0;
       
  1341 }
       
  1342 
       
  1343 /* Fade the display from black to normal
       
  1344  * Restore previously saved gamma values
       
  1345  */
       
  1346 static uint32 QZ_FadeGammaIn(const OTTD_QuartzGammaTable* table)
       
  1347 {
       
  1348 	CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE];
       
  1349 	CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE];
       
  1350 	CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE];
       
  1351 	float percent;
       
  1352 	int j;
       
  1353 
       
  1354 	memset(redTable, 0, sizeof(redTable));
       
  1355 	memset(greenTable, 0, sizeof(greenTable));
       
  1356 	memset(blueTable, 0, sizeof(greenTable));
       
  1357 
       
  1358 	for (percent = 0.0; percent <= 1.0; percent += 0.01) {
       
  1359 		for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
       
  1360 			redTable[j]   = table->red[j]   * percent;
       
  1361 			greenTable[j] = table->green[j] * percent;
       
  1362 			blueTable[j]  = table->blue[j]  * percent;
       
  1363 		}
       
  1364 
       
  1365 		if (CGSetDisplayTransferByTable(
       
  1366 					_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE,
       
  1367 					redTable, greenTable, blueTable
       
  1368 				) != CGDisplayNoErr) {
       
  1369 			CGDisplayRestoreColorSyncSettings();
       
  1370 			return 1;
       
  1371 		}
       
  1372 
       
  1373 		CSleep(10);
       
  1374 	}
       
  1375 
       
  1376 	return 0;
       
  1377 }
       
  1378 
       
  1379 static const char* QZ_SetVideoFullScreen(int width, int height)
       
  1380 {
       
  1381 	const char* errstr = "QZ_SetVideoFullScreen error";
       
  1382 	int exact_match;
       
  1383 	CFNumberRef number;
       
  1384 	int bpp;
       
  1385 	int gamma_error;
       
  1386 	OTTD_QuartzGammaTable gamma_table;
       
  1387 	NSRect screen_rect;
       
  1388 	CGError error;
       
  1389 	NSPoint pt;
       
  1390 
       
  1391 	/* Destroy any previous mode */
       
  1392 	if (_cocoa_video_data.isset) QZ_UnsetVideoMode();
       
  1393 
       
  1394 	/* See if requested mode exists */
       
  1395 	_cocoa_video_data.mode = CGDisplayBestModeForParameters(_cocoa_video_data.display_id, _cocoa_video_data.blitter_bpp, width, height, &exact_match);
       
  1396 
       
  1397 	/* If the mode wasn't an exact match, check if it has the right bpp, and update width and height */
       
  1398 	if (!exact_match) {
       
  1399 		number = (const __CFNumber*) CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayBitsPerPixel);
       
  1400 		CFNumberGetValue(number, kCFNumberSInt32Type, &bpp);
       
  1401 		if ((uint) bpp != _cocoa_video_data.blitter_bpp) {
       
  1402 			errstr = "Failed to find display resolution";
       
  1403 			goto ERR_NO_MATCH;
       
  1404 		}
       
  1405 
       
  1406 		number = (const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayWidth);
       
  1407 		CFNumberGetValue(number, kCFNumberSInt32Type, &width);
       
  1408 
       
  1409 		number = (const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayHeight);
       
  1410 		CFNumberGetValue(number, kCFNumberSInt32Type, &height);
       
  1411 	}
       
  1412 
       
  1413 	/* Fade display to zero gamma */
       
  1414 	gamma_error = QZ_FadeGammaOut(&gamma_table);
       
  1415 
       
  1416 	/* Put up the blanking window (a window above all other windows) */
       
  1417 	error = CGDisplayCapture(_cocoa_video_data.display_id);
       
  1418 
       
  1419 	if (CGDisplayNoErr != error) {
       
  1420 		errstr = "Failed capturing display";
       
  1421 		goto ERR_NO_CAPTURE;
       
  1422 	}
       
  1423 
       
  1424 	/* Do the physical switch */
       
  1425 	if (CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.mode) != CGDisplayNoErr) {
       
  1426 		errstr = "Failed switching display resolution";
       
  1427 		goto ERR_NO_SWITCH;
       
  1428 	}
       
  1429 
       
  1430 	_cocoa_video_data.realpixels = CGDisplayBaseAddress(_cocoa_video_data.display_id);
       
  1431 	_cocoa_video_data.pitch  = CGDisplayBytesPerRow(_cocoa_video_data.display_id);
       
  1432 
       
  1433 	_cocoa_video_data.width = CGDisplayPixelsWide(_cocoa_video_data.display_id);
       
  1434 	_cocoa_video_data.height = CGDisplayPixelsHigh(_cocoa_video_data.display_id);
       
  1435 	_cocoa_video_data.fullscreen = true;
       
  1436 
       
  1437 	/* Setup double-buffer emulation */
       
  1438 	_cocoa_video_data.pixels = malloc(width * height * _cocoa_video_data.blitter_bpp / 8);
       
  1439 	if (_cocoa_video_data.pixels == NULL) {
       
  1440 		errstr = "Failed to allocate memory for double buffering";
       
  1441 		goto ERR_DOUBLEBUF;
       
  1442 	}
       
  1443 
       
  1444 	if (_cocoa_video_data.blitter_bpp == 8 && !CGDisplayCanSetPalette(_cocoa_video_data.display_id)) {
       
  1445 		errstr = "Not an indexed display mode.";
       
  1446 		goto ERR_NOT_INDEXED;
       
  1447 	}
       
  1448 
       
  1449 	/* If we don't hide menu bar, it will get events and interrupt the program */
       
  1450 	HideMenuBar();
       
  1451 
       
  1452 	/* Fade the display to original gamma */
       
  1453 	if (!gamma_error) QZ_FadeGammaIn(&gamma_table);
       
  1454 
       
  1455 	/* There is a bug in Cocoa where NSScreen doesn't synchronize
       
  1456 	 * with CGDirectDisplay, so the main screen's frame is wrong.
       
  1457 	 * As a result, coordinate translation produces incorrect results.
       
  1458 	 * We can hack around this bug by setting the screen rect ourselves.
       
  1459 	 * This hack should be removed if/when the bug is fixed.
       
  1460 	 */
       
  1461 	screen_rect = NSMakeRect(0, 0, width, height);
       
  1462 	[ [ NSScreen mainScreen ] setFrame:screen_rect ];
       
  1463 
       
  1464 	/* we're fullscreen, so flag all input states... */
       
  1465 	_cocoa_video_data.active = true;
       
  1466 
       
  1467 
       
  1468 	pt = [ NSEvent mouseLocation ];
       
  1469 	pt.y = CGDisplayPixelsHigh(_cocoa_video_data.display_id) - pt.y;
       
  1470 	if (QZ_MouseIsInsideView(&pt)) QZ_HideMouse();
       
  1471 
       
  1472 	return NULL;
       
  1473 
       
  1474 /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
       
  1475 ERR_NOT_INDEXED:
       
  1476 	free(_cocoa_video_data.pixels);
       
  1477 	_cocoa_video_data.pixels = NULL;
       
  1478 ERR_DOUBLEBUF:
       
  1479 	CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.save_mode);
       
  1480 ERR_NO_SWITCH:
       
  1481 	CGReleaseAllDisplays();
       
  1482 ERR_NO_CAPTURE:
       
  1483 	if (!gamma_error) QZ_FadeGammaIn(&gamma_table);
       
  1484 ERR_NO_MATCH:
       
  1485 	return errstr;
       
  1486 }
       
  1487 
       
  1488 
       
  1489 static void QZ_UpdateFullscreenPalette(uint first_color, uint num_colors)
       
  1490 {
       
  1491 	CGTableCount  index;
       
  1492 	CGDeviceColor color;
       
  1493 
       
  1494 	for (index = first_color; index < first_color+num_colors; index++) {
       
  1495 		/* Clamp colors between 0.0 and 1.0 */
       
  1496 		color.red   = _cur_palette[index].r / 255.0;
       
  1497 		color.blue  = _cur_palette[index].b / 255.0;
       
  1498 		color.green = _cur_palette[index].g / 255.0;
       
  1499 
       
  1500 		CGPaletteSetColorAtIndex(_cocoa_video_data.palette, color, index);
       
  1501 	}
       
  1502 
       
  1503 	CGDisplaySetPalette(_cocoa_video_data.display_id, _cocoa_video_data.palette);
       
  1504 }
       
  1505 
       
  1506 /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
       
  1507 static void QZ_WaitForVerticalBlank()
       
  1508 {
       
  1509 	/* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
       
  1510 	double refreshRate;
       
  1511 	double linesPerSecond;
       
  1512 	double target;
       
  1513 	double position;
       
  1514 	double adjustment;
       
  1515 	CFNumberRef refreshRateCFNumber;
       
  1516 
       
  1517 	refreshRateCFNumber = (const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayRefreshRate);
       
  1518 	if (refreshRateCFNumber == NULL) return;
       
  1519 
       
  1520 	if (CFNumberGetValue(refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) == 0)
       
  1521 		return;
       
  1522 
       
  1523 	if (refreshRate == 0) return;
       
  1524 
       
  1525 	linesPerSecond = refreshRate * _cocoa_video_data.height;
       
  1526 	target = _cocoa_video_data.height;
       
  1527 
       
  1528 	/* Figure out the first delay so we start off about right */
       
  1529 	position = CGDisplayBeamPosition(_cocoa_video_data.display_id);
       
  1530 	if (position > target) position = 0;
       
  1531 
       
  1532 	adjustment = (target - position) / linesPerSecond;
       
  1533 
       
  1534 	CSleep((uint32)(adjustment * 1000));
       
  1535 }
       
  1536 
       
  1537 
       
  1538 static void QZ_DrawScreen()
       
  1539 {
       
  1540 	const uint8* src   = (uint8*) _cocoa_video_data.pixels;
       
  1541 	uint8* dst         = (uint8*)_cocoa_video_data.realpixels;
       
  1542 	uint pitch         = _cocoa_video_data.pitch;
       
  1543 	uint width         = _cocoa_video_data.width;
       
  1544 	uint num_dirty     = _cocoa_video_data.num_dirty_rects;
       
  1545 	uint bytesperpixel = _cocoa_video_data.blitter_bpp / 8;
       
  1546 	uint i;
       
  1547 
       
  1548 	/* Check if we need to do anything */
       
  1549 	if (num_dirty == 0) return;
       
  1550 
       
  1551 	if (num_dirty >= MAX_DIRTY_RECTS) {
       
  1552 		num_dirty = 1;
       
  1553 		_cocoa_video_data.dirty_rects[0].left   = 0;
       
  1554 		_cocoa_video_data.dirty_rects[0].top    = 0;
       
  1555 		_cocoa_video_data.dirty_rects[0].right  = _cocoa_video_data.width;
       
  1556 		_cocoa_video_data.dirty_rects[0].bottom = _cocoa_video_data.height;
       
  1557 	}
       
  1558 
       
  1559 	QZ_WaitForVerticalBlank();
       
  1560 	/* Build the region of dirty rectangles */
       
  1561 	for (i = 0; i < num_dirty; i++) {
       
  1562 		uint y      = _cocoa_video_data.dirty_rects[i].top;
       
  1563 		uint left   = _cocoa_video_data.dirty_rects[i].left;
       
  1564 		uint length = _cocoa_video_data.dirty_rects[i].right - left;
       
  1565 		uint bottom = _cocoa_video_data.dirty_rects[i].bottom;
       
  1566 
       
  1567 		for (; y < bottom; y++) {
       
  1568 			memcpy(dst + y * pitch + left * bytesperpixel, src + y * width * bytesperpixel + left * bytesperpixel, length * bytesperpixel);
       
  1569 		}
       
  1570 	}
       
  1571 
       
  1572 	_cocoa_video_data.num_dirty_rects = 0;
       
  1573 }
       
  1574 
       
  1575 
       
  1576 static int QZ_ListFullscreenModes(OTTDPoint* mode_list, int max_modes)
       
  1577 {
       
  1578 	CFIndex num_modes;
       
  1579 	CFIndex i;
       
  1580 	int list_size = 0;
       
  1581 
       
  1582 	num_modes = CFArrayGetCount(_cocoa_video_data.mode_list);
       
  1583 
       
  1584 	/* Build list of modes with the requested bpp */
       
  1585 	for (i = 0; i < num_modes && list_size < max_modes; i++) {
       
  1586 		CFDictionaryRef onemode;
       
  1587 		CFNumberRef     number;
       
  1588 		int bpp;
       
  1589 		int intvalue;
       
  1590 		bool hasMode;
       
  1591 		uint16 width, height;
       
  1592 
       
  1593 		onemode = (const __CFDictionary*)CFArrayGetValueAtIndex(_cocoa_video_data.mode_list, i);
       
  1594 		number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayBitsPerPixel);
       
  1595 		CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
       
  1596 
       
  1597 		if ((uint) bpp != _cocoa_video_data.blitter_bpp) continue;
       
  1598 
       
  1599 		number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayWidth);
       
  1600 		CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue);
       
  1601 		width = (uint16)intvalue;
       
  1602 
       
  1603 		number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayHeight);
       
  1604 		CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue);
       
  1605 		height = (uint16)intvalue;
       
  1606 
       
  1607 		/* Check if mode is already in the list */
       
  1608 		{
       
  1609 			int i;
       
  1610 			hasMode = false;
       
  1611 			for (i = 0; i < list_size; i++) {
       
  1612 				if (mode_list[i].x == width &&  mode_list[i].y == height) {
       
  1613 					hasMode = true;
       
  1614 					break;
       
  1615 				}
       
  1616 			}
       
  1617 		}
       
  1618 
       
  1619 		if (hasMode) continue;
       
  1620 
       
  1621 		/* Add mode to the list */
       
  1622 		mode_list[list_size].x = width;
       
  1623 		mode_list[list_size].y = height;
       
  1624 		list_size++;
       
  1625 	}
       
  1626 
       
  1627 	/* Sort list smallest to largest */
       
  1628 	{
       
  1629 		int i, j;
       
  1630 		for (i = 0; i < list_size; i++) {
       
  1631 			for (j = 0; j < list_size-1; j++) {
       
  1632 				if (mode_list[j].x > mode_list[j + 1].x || (
       
  1633 							mode_list[j].x == mode_list[j + 1].x &&
       
  1634 							mode_list[j].y >  mode_list[j + 1].y
       
  1635 						)) {
       
  1636 					uint tmpw = mode_list[j].x;
       
  1637 					uint tmph = mode_list[j].y;
       
  1638 
       
  1639 					mode_list[j].x = mode_list[j + 1].x;
       
  1640 					mode_list[j].y = mode_list[j + 1].y;
       
  1641 
       
  1642 					mode_list[j + 1].x = tmpw;
       
  1643 					mode_list[j + 1].y = tmph;
       
  1644 				}
       
  1645 			}
       
  1646 		}
       
  1647 	}
       
  1648 
       
  1649 	return list_size;
       
  1650 }
       
  1651 
       
  1652 
       
  1653 /******************************************************************************
       
  1654  *                             Windowed and fullscreen common code            *
       
  1655  ******************************************************************************/
       
  1656 
       
  1657 static void QZ_UpdatePalette(uint start, uint count)
       
  1658 {
       
  1659 	if (_cocoa_video_data.fullscreen) {
       
  1660 		QZ_UpdateFullscreenPalette(start, count);
       
  1661 	} else {
       
  1662 		QZ_UpdateWindowPalette(start, count);
       
  1663 	}
       
  1664 }
       
  1665 
       
  1666 static void QZ_InitPalette()
       
  1667 {
       
  1668 	QZ_UpdatePalette(0, 256);
       
  1669 }
       
  1670 
       
  1671 static void QZ_Draw()
       
  1672 {
       
  1673 	if (_cocoa_video_data.fullscreen) {
       
  1674 		QZ_DrawScreen();
       
  1675 	} else {
       
  1676 		QZ_DrawWindow();
       
  1677 	}
       
  1678 }
       
  1679 
       
  1680 
       
  1681 static const OTTDPoint _default_resolutions[] = {
       
  1682 	{ 640,  480},
       
  1683 	{ 800,  600},
       
  1684 	{1024,  768},
       
  1685 	{1152,  864},
       
  1686 	{1280,  800},
       
  1687 	{1280,  960},
       
  1688 	{1280, 1024},
       
  1689 	{1400, 1050},
       
  1690 	{1600, 1200},
       
  1691 	{1680, 1050},
       
  1692 	{1920, 1200}
       
  1693 };
       
  1694 
       
  1695 static void QZ_UpdateVideoModes()
       
  1696 {
       
  1697 	uint i, j, count;
       
  1698 	OTTDPoint modes[32];
       
  1699 	const OTTDPoint *current_modes;
       
  1700 
       
  1701 	if (_cocoa_video_data.fullscreen) {
       
  1702 		count = QZ_ListFullscreenModes(modes, 32);
       
  1703 		current_modes = modes;
       
  1704 	} else {
       
  1705 		count = lengthof(_default_resolutions);
       
  1706 		current_modes = _default_resolutions;
       
  1707 	}
       
  1708 
       
  1709 	for (i = 0, j = 0; j < lengthof(_resolutions) && i < count; i++) {
       
  1710 		if (_cocoa_video_data.fullscreen || (
       
  1711 					(uint)current_modes[i].x < _cocoa_video_data.device_width &&
       
  1712 					(uint)current_modes[i].y < _cocoa_video_data.device_height)
       
  1713 				) {
       
  1714 			_resolutions[j][0] = current_modes[i].x;
       
  1715 			_resolutions[j][1] = current_modes[i].y;
       
  1716 			j++;
       
  1717 		}
       
  1718 	}
       
  1719 
       
  1720 	_num_resolutions = j;
       
  1721 }
       
  1722 
       
  1723 static void QZ_UnsetVideoMode()
       
  1724 {
       
  1725 	if (_cocoa_video_data.fullscreen) {
       
  1726 		/* Release fullscreen resources */
       
  1727 		OTTD_QuartzGammaTable gamma_table;
       
  1728 		int gamma_error;
       
  1729 		NSRect screen_rect;
       
  1730 
       
  1731 		gamma_error = QZ_FadeGammaOut(&gamma_table);
       
  1732 
       
  1733 		/* Restore original screen resolution/bpp */
       
  1734 		CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.save_mode);
       
  1735 		CGReleaseAllDisplays();
       
  1736 		ShowMenuBar();
       
  1737 		/* Reset the main screen's rectangle
       
  1738 		 * See comment in QZ_SetVideoFullscreen for why we do this
       
  1739 		 */
       
  1740 		screen_rect = NSMakeRect(0,0,_cocoa_video_data.device_width,_cocoa_video_data.device_height);
       
  1741 		[ [ NSScreen mainScreen ] setFrame:screen_rect ];
       
  1742 
       
  1743 		if (!gamma_error) QZ_FadeGammaIn(&gamma_table);
       
  1744 	} else {
       
  1745 		/* Release window mode resources */
       
  1746 		[ _cocoa_video_data.window close ];
       
  1747 		_cocoa_video_data.window = nil;
       
  1748 		_cocoa_video_data.qdview = nil;
       
  1749 	}
       
  1750 
       
  1751 	free(_cocoa_video_data.pixels);
       
  1752 	_cocoa_video_data.pixels = NULL;
       
  1753 
       
  1754 	/* Signal successful teardown */
       
  1755 	_cocoa_video_data.isset = false;
       
  1756 
       
  1757 	QZ_ShowMouse();
       
  1758 }
       
  1759 
       
  1760 
       
  1761 static const char* QZ_SetVideoMode(uint width, uint height, bool fullscreen)
       
  1762 {
       
  1763 	const char *ret;
       
  1764 
       
  1765 	_cocoa_video_data.issetting = true;
       
  1766 	if (fullscreen) {
       
  1767 		/* Setup full screen video */
       
  1768 		ret = QZ_SetVideoFullScreen(width, height);
       
  1769 	} else {
       
  1770 		/* Setup windowed video */
       
  1771 		ret = QZ_SetVideoWindowed(width, height);
       
  1772 	}
       
  1773 	_cocoa_video_data.issetting = false;
       
  1774 	if (ret != NULL) return ret;
       
  1775 
       
  1776 	/* Signal successful completion (used internally) */
       
  1777 	_cocoa_video_data.isset = true;
       
  1778 
       
  1779 	/* Tell the game that the resolution has changed */
       
  1780 	_screen.width = _cocoa_video_data.width;
       
  1781 	_screen.height = _cocoa_video_data.height;
       
  1782 	_screen.pitch = _cocoa_video_data.width;
       
  1783 
       
  1784 	QZ_UpdateVideoModes();
       
  1785 	GameSizeChanged();
       
  1786 
       
  1787 	QZ_InitPalette();
       
  1788 
       
  1789 	return NULL;
       
  1790 }
       
  1791 
       
  1792 static const char* QZ_SetVideoModeAndRestoreOnFailure(uint width, uint height, bool fullscreen)
       
  1793 {
       
  1794 	bool wasset = _cocoa_video_data.isset;
       
  1795 	uint32 oldwidth = _cocoa_video_data.width;
       
  1796 	uint32 oldheight = _cocoa_video_data.height;
       
  1797 	bool oldfullscreen = _cocoa_video_data.fullscreen;
       
  1798 	const char *ret;
       
  1799 
       
  1800 	ret = QZ_SetVideoMode(width, height, fullscreen);
       
  1801 	if (ret != NULL && wasset) QZ_SetVideoMode(oldwidth, oldheight, oldfullscreen);
       
  1802 
       
  1803 	return ret;
       
  1804 }
       
  1805 
       
  1806 static void QZ_VideoInit()
       
  1807 {
       
  1808 
       
  1809 	memset(&_cocoa_video_data, 0, sizeof(_cocoa_video_data));
       
  1810 
       
  1811 	_cocoa_video_data.blitter_bpp = BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth();
       
  1812 	DEBUG(driver, 1, "Cocoa: Blitter bpp %d", _cocoa_video_data.blitter_bpp);
       
  1813 
       
  1814 	if (_cocoa_video_data.blitter_bpp == 0) error("Can't use a blitter that blits 0 bpp for normal visuals");
       
  1815 
       
  1816 
       
  1817 	if (_cocoa_video_data.blitter_bpp != 8 && _cocoa_video_data.blitter_bpp != 32) {
       
  1818 		error("Cocoa: This video driver only supports 8 and 32 bpp blitters.");
       
  1819 	}
       
  1820 
       
  1821 	/* Initialize the video settings; this data persists between mode switches */
       
  1822 	_cocoa_video_data.display_id = kCGDirectMainDisplay;
       
  1823 	_cocoa_video_data.save_mode  = CGDisplayCurrentMode(_cocoa_video_data.display_id);
       
  1824 	_cocoa_video_data.mode_list  = CGDisplayAvailableModes(_cocoa_video_data.display_id);
       
  1825 	_cocoa_video_data.palette    = CGPaletteCreateDefaultColorPalette();
       
  1826 
       
  1827 	/* Gather some information that is useful to know about the display */
       
  1828 	/* Maybe this should be moved to QZ_SetVideoMode, in case this is changed after startup */
       
  1829 	CFNumberGetValue(
       
  1830 		(const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayBitsPerPixel),
       
  1831 		kCFNumberSInt32Type, &_cocoa_video_data.device_bpp
       
  1832 	);
       
  1833 
       
  1834 	CFNumberGetValue(
       
  1835 		(const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayWidth),
       
  1836 		kCFNumberSInt32Type, &_cocoa_video_data.device_width
       
  1837 	);
       
  1838 
       
  1839 	CFNumberGetValue(
       
  1840 		(const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayHeight),
       
  1841 		kCFNumberSInt32Type, &_cocoa_video_data.device_height
       
  1842 	);
       
  1843 
       
  1844 	_cocoa_video_data.cursor_visible = true;
       
  1845 
       
  1846 	/* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
       
  1847 //	QZ_RegisterForSleepNotifications();
       
  1848 }
       
  1849 
       
  1850 
       
  1851 /* Convert local coordinate to window server (CoreGraphics) coordinate */
       
  1852 static CGPoint QZ_PrivateLocalToCG(NSPoint* p)
       
  1853 {
       
  1854 	CGPoint cgp;
       
  1855 
       
  1856 	if (!_cocoa_video_data.fullscreen) {
       
  1857 		*p = [ _cocoa_video_data.qdview convertPoint:*p toView: nil ];
       
  1858 		*p = [ _cocoa_video_data.window convertBaseToScreen:*p ];
       
  1859 		p->y = _cocoa_video_data.device_height - p->y;
       
  1860 	}
       
  1861 
       
  1862 	cgp.x = p->x;
       
  1863 	cgp.y = p->y;
       
  1864 
       
  1865 	return cgp;
       
  1866 }
       
  1867 
       
  1868 static void QZ_WarpCursor(int x, int y)
       
  1869 {
       
  1870 	NSPoint p;
       
  1871 	CGPoint cgp;
       
  1872 
       
  1873 	/* Only allow warping when in foreground */
       
  1874 	if (![ NSApp isActive ]) return;
       
  1875 
       
  1876 	p = NSMakePoint(x, y);
       
  1877 	cgp = QZ_PrivateLocalToCG(&p);
       
  1878 
       
  1879 	/* this is the magic call that fixes cursor "freezing" after warp */
       
  1880 	CGSetLocalEventsSuppressionInterval(0.0);
       
  1881 	/* Do the actual warp */
       
  1882 	CGWarpMouseCursorPosition(cgp);
       
  1883 
       
  1884 	/* Generate the mouse moved event */
       
  1885 }
       
  1886 
       
  1887 static void QZ_ShowMouse()
       
  1888 {
       
  1889 	if (!_cocoa_video_data.cursor_visible) {
       
  1890 		[ NSCursor unhide ];
       
  1891 		_cocoa_video_data.cursor_visible = true;
       
  1892 
       
  1893 		// Hide the openttd cursor when leaving the window
       
  1894 		if (_cocoa_video_data.isset)
       
  1895 			UndrawMouseCursor();
       
  1896 		_cursor.in_window = false;
       
  1897 	}
       
  1898 }
       
  1899 
       
  1900 static void QZ_HideMouse()
       
  1901 {
       
  1902 	if (_cocoa_video_data.cursor_visible) {
       
  1903 #ifndef _DEBUG
       
  1904 		[ NSCursor hide ];
       
  1905 #endif
       
  1906 		_cocoa_video_data.cursor_visible = false;
       
  1907 
       
  1908 		// Show the openttd cursor again
       
  1909 		_cursor.in_window = true;
       
  1910 	}
       
  1911 }
       
  1912 
       
  1913 
       
  1914 /******************************************************************************
       
  1915  *                             OS X application creation                      *
       
  1916  ******************************************************************************/
       
  1917 
       
  1918 /* The main class of the application, the application's delegate */
       
  1919 @implementation OTTDMain
       
  1920 /* Called when the internal event loop has just started running */
       
  1921 - (void) applicationDidFinishLaunching: (NSNotification*) note
       
  1922 {
       
  1923 	/* Hand off to main application code */
       
  1924 	QZ_GameLoop();
       
  1925 
       
  1926 	/* We're done, thank you for playing */
       
  1927 	[ NSApp stop:_ottd_main ];
       
  1928 }
       
  1929 
       
  1930 /* Display the in game quit confirmation dialog */
       
  1931 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*) sender
       
  1932 {
       
  1933 
       
  1934 	HandleExitGameRequest();
       
  1935 
       
  1936 	return NSTerminateCancel; // NSTerminateLater ?
       
  1937 }
       
  1938 @end
       
  1939 
       
  1940 static void setApplicationMenu()
       
  1941 {
       
  1942 	/* warning: this code is very odd */
       
  1943 	NSMenu *appleMenu;
       
  1944 	NSMenuItem *menuItem;
       
  1945 	NSString *title;
       
  1946 	NSString *appName;
       
  1947 
       
  1948 	appName = @"OTTD";
       
  1949 	appleMenu = [[NSMenu alloc] initWithTitle:appName];
       
  1950 
       
  1951 	/* Add menu items */
       
  1952 	title = [@"About " stringByAppendingString:appName];
       
  1953 	[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
       
  1954 
       
  1955 	[appleMenu addItem:[NSMenuItem separatorItem]];
       
  1956 
       
  1957 	title = [@"Hide " stringByAppendingString:appName];
       
  1958 	[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
       
  1959 
       
  1960 	menuItem = (NSMenuItem*)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
       
  1961 	[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
       
  1962 
       
  1963 	[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
       
  1964 
       
  1965 	[appleMenu addItem:[NSMenuItem separatorItem]];
       
  1966 
       
  1967 	title = [@"Quit " stringByAppendingString:appName];
       
  1968 	[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
       
  1969 
       
  1970 
       
  1971 	/* Put menu into the menubar */
       
  1972 	menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
       
  1973 	[menuItem setSubmenu:appleMenu];
       
  1974 	[[NSApp mainMenu] addItem:menuItem];
       
  1975 
       
  1976 	/* Tell the application object that this is now the application menu */
       
  1977 	[NSApp setAppleMenu:appleMenu];
       
  1978 
       
  1979 	/* Finally give up our references to the objects */
       
  1980 	[appleMenu release];
       
  1981 	[menuItem release];
       
  1982 }
       
  1983 
       
  1984 /* Create a window menu */
       
  1985 static void setupWindowMenu()
       
  1986 {
       
  1987 	NSMenu* windowMenu;
       
  1988 	NSMenuItem* windowMenuItem;
       
  1989 	NSMenuItem* menuItem;
       
  1990 
       
  1991 	windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
       
  1992 
       
  1993 	/* "Minimize" item */
       
  1994 	menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
       
  1995 	[windowMenu addItem:menuItem];
       
  1996 	[menuItem release];
       
  1997 
       
  1998 	/* Put menu into the menubar */
       
  1999 	windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
       
  2000 	[windowMenuItem setSubmenu:windowMenu];
       
  2001 	[[NSApp mainMenu] addItem:windowMenuItem];
       
  2002 
       
  2003 	/* Tell the application object that this is now the window menu */
       
  2004 	[NSApp setWindowsMenu:windowMenu];
       
  2005 
       
  2006 	/* Finally give up our references to the objects */
       
  2007 	[windowMenu release];
       
  2008 	[windowMenuItem release];
       
  2009 }
       
  2010 
       
  2011 static void setupApplication()
       
  2012 {
       
  2013 	CPSProcessSerNum PSN;
       
  2014 
       
  2015 	/* Ensure the application object is initialised */
       
  2016 	[NSApplication sharedApplication];
       
  2017 
       
  2018 	/* Tell the dock about us */
       
  2019 	if (!CPSGetCurrentProcess(&PSN) &&
       
  2020 			!CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103) &&
       
  2021 			!CPSSetFrontProcess(&PSN)) {
       
  2022 		[NSApplication sharedApplication];
       
  2023 	}
       
  2024 
       
  2025 	/* Set up the menubar */
       
  2026 	[NSApp setMainMenu:[[NSMenu alloc] init]];
       
  2027 	setApplicationMenu();
       
  2028 	setupWindowMenu();
       
  2029 
       
  2030 	/* Create OTTDMain and make it the app delegate */
       
  2031 	_ottd_main = [[OTTDMain alloc] init];
       
  2032 	[NSApp setDelegate:_ottd_main];
       
  2033 }
       
  2034 
       
  2035 
       
  2036 /******************************************************************************
       
  2037  *                             Video driver interface                         *
       
  2038  ******************************************************************************/
       
  2039 
       
  2040 static FVideoDriver_Cocoa iFVideoDriver_Cocoa;
       
  2041 
       
  2042 void VideoDriver_Cocoa::Stop()
       
  2043 {
       
  2044 	if (!_cocoa_video_started) return;
       
  2045 
       
  2046 	if (_cocoa_video_data.isset) QZ_UnsetVideoMode();
       
  2047 
       
  2048 	[_ottd_main release];
       
  2049 
       
  2050 	_cocoa_video_started = false;
       
  2051 }
       
  2052 
       
  2053 const char *VideoDriver_Cocoa::Start(const char * const *parm)
       
  2054 {
       
  2055 	const char *ret;
       
  2056 
       
  2057 	if (_cocoa_video_started) return "Already started";
       
  2058 	_cocoa_video_started = true;
       
  2059 
       
  2060 	memset(&_cocoa_video_data, 0, sizeof(_cocoa_video_data));
       
  2061 
       
  2062 	setupApplication();
       
  2063 
       
  2064 	/* Don't create a window or enter fullscreen if we're just going to show a dialog. */
       
  2065 	if (_cocoa_video_dialog) return NULL;
       
  2066 
       
  2067 	QZ_VideoInit();
       
  2068 
       
  2069 	ret = QZ_SetVideoMode(_cur_resolution[0], _cur_resolution[1], _fullscreen);
       
  2070 	if (ret != NULL) _video_driver->Stop();
       
  2071 
       
  2072 	return ret;
       
  2073 }
       
  2074 
       
  2075 void VideoDriver_Cocoa::MakeDirty(int left, int top, int width, int height)
       
  2076 {
       
  2077 	if (_cocoa_video_data.num_dirty_rects < MAX_DIRTY_RECTS) {
       
  2078 		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].left = left;
       
  2079 		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].top = top;
       
  2080 		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].right = left + width;
       
  2081 		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].bottom = top + height;
       
  2082 	}
       
  2083 	_cocoa_video_data.num_dirty_rects++;
       
  2084 }
       
  2085 
       
  2086 void VideoDriver_Cocoa::MainLoop()
       
  2087 {
       
  2088 	/* Start the main event loop */
       
  2089 	[NSApp run];
       
  2090 }
       
  2091 
       
  2092 bool VideoDriver_Cocoa::ChangeResolution(int w, int h)
       
  2093 {
       
  2094 	const char *ret = QZ_SetVideoModeAndRestoreOnFailure((uint)w, (uint)h, _cocoa_video_data.fullscreen);
       
  2095 	if (ret != NULL) {
       
  2096 		DEBUG(driver, 0, "cocoa_v: VideoDriver_Cocoa::ChangeResolution failed with message: %s", ret);
       
  2097 	}
       
  2098 
       
  2099 	return ret == NULL;
       
  2100 }
       
  2101 
       
  2102 void VideoDriver_Cocoa::ToggleFullscreen(bool full_screen)
       
  2103 {
       
  2104 	const char *ret = QZ_SetVideoModeAndRestoreOnFailure(_cocoa_video_data.width, _cocoa_video_data.height, full_screen);
       
  2105 	if (ret != NULL) {
       
  2106 		DEBUG(driver, 0, "cocoa_v: VideoDriver_Cocoa::ToggleFullscreen failed with message: %s", ret);
       
  2107 	}
       
  2108 
       
  2109 	_fullscreen = _cocoa_video_data.fullscreen;
       
  2110 }
       
  2111 
       
  2112 
       
  2113 /* This is needed since sometimes assert is called before the videodriver is initialized */
       
  2114 void CocoaDialog(const char* title, const char* message, const char* buttonLabel)
       
  2115 {
       
  2116 	bool wasstarted;
       
  2117 
       
  2118 	_cocoa_video_dialog = true;
       
  2119 
       
  2120 	wasstarted = _cocoa_video_started;
       
  2121 	if (_video_driver == NULL) {
       
  2122 		setupApplication(); // Setup application before showing dialog
       
  2123 	} else if (!_cocoa_video_started && _video_driver->Start(NULL) != NULL) {
       
  2124 		fprintf(stderr, "%s: %s\n", title, message);
       
  2125 		return;
       
  2126 	}
       
  2127 
       
  2128 	NSRunAlertPanel([NSString stringWithCString: title], [NSString stringWithCString: message], [NSString stringWithCString: buttonLabel], nil, nil);
       
  2129 
       
  2130 	if (!wasstarted && _video_driver != NULL) _video_driver->Stop();
       
  2131 
       
  2132 	_cocoa_video_dialog = false;
       
  2133 }
       
  2134 
       
  2135 /* This is needed since OS X application bundles do not have a
       
  2136  * current directory and the data files are 'somewhere' in the bundle */
       
  2137 void cocoaSetApplicationBundleDir()
       
  2138 {
       
  2139 	char tmp[MAXPATHLEN];
       
  2140 	CFURLRef url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
       
  2141 	if (CFURLGetFileSystemRepresentation(url, true, (unsigned char*)tmp, MAXPATHLEN)) {
       
  2142 		AppendPathSeparator(tmp, lengthof(tmp));
       
  2143 		_searchpaths[SP_APPLICATION_BUNDLE_DIR] = strdup(tmp);
       
  2144 	} else {
       
  2145 		_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
       
  2146 	}
       
  2147 
       
  2148 	CFRelease(url);
       
  2149 }
       
  2150 
       
  2151 /* These are called from main() to prevent a _NSAutoreleaseNoPool error when
       
  2152  * exiting before the cocoa video driver has been loaded
       
  2153  */
       
  2154 void cocoaSetupAutoreleasePool()
       
  2155 {
       
  2156 	_ottd_autorelease_pool = [[NSAutoreleasePool alloc] init];
       
  2157 }
       
  2158 
       
  2159 void cocoaReleaseAutoreleasePool()
       
  2160 {
       
  2161 	[_ottd_autorelease_pool release];
       
  2162 }
       
  2163 
       
  2164 #endif /* WITH_COCOA */