src/video/cocoa_v.mm
branchnoai
changeset 9722 ebf0ece7d8f6
parent 9721 9a27928bcd5e
child 9723 eee46cb39750
equal deleted inserted replaced
9721:9a27928bcd5e 9722:ebf0ece7d8f6
     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 ppc/param.h or i386/param.h included from sys/param.h */
       
    56 #undef ALIGN
       
    57 /* Defined in stdbool.h */
       
    58 #ifndef __cplusplus
       
    59 # ifndef __BEOS__
       
    60 #  undef bool
       
    61 #  undef false
       
    62 #  undef true
       
    63 # endif
       
    64 #endif
       
    65 
       
    66 
       
    67 #include "../stdafx.h"
       
    68 #include "../openttd.h"
       
    69 #include "../debug.h"
       
    70 #include "../macros.h"
       
    71 #include "../os/macosx/splash.h"
       
    72 #include "../variables.h"
       
    73 #include "../gfx.h"
       
    74 #include "cocoa_v.h"
       
    75 #include "cocoa_keys.h"
       
    76 #include "../blitter/factory.hpp"
       
    77 #include "../fileio.h"
       
    78 
       
    79 #undef Point
       
    80 #undef Rect
       
    81 
       
    82 /* Right Mouse Button Emulation enum */
       
    83 enum {
       
    84 	RMBE_COMMAND,
       
    85 	RMBE_CONTROL,
       
    86 	RMBE_OFF,
       
    87 };
       
    88 
       
    89 
       
    90 /* Subclass of NSWindow to fix genie effect and support resize events  */
       
    91 @interface OTTD_QuartzWindow : NSWindow
       
    92 - (void)miniaturize:(id)sender;
       
    93 - (void)display;
       
    94 - (void)setFrame:(NSRect)frameRect display:(BOOL)flag;
       
    95 - (void)appDidHide:(NSNotification*)note;
       
    96 - (void)appWillUnhide:(NSNotification*)note;
       
    97 - (void)appDidUnhide:(NSNotification*)note;
       
    98 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag;
       
    99 @end
       
   100 
       
   101 /* Delegate for our NSWindow to send ask for quit on close */
       
   102 @interface OTTD_QuartzWindowDelegate : NSObject
       
   103 - (BOOL)windowShouldClose:(id)sender;
       
   104 @end
       
   105 
       
   106 @interface OTTDMain : NSObject
       
   107 @end
       
   108 
       
   109 
       
   110 /* Structure for rez switch gamma fades
       
   111  * We can hide the monitor flicker by setting the gamma tables to 0
       
   112  */
       
   113 #define QZ_GAMMA_TABLE_SIZE 256
       
   114 
       
   115 struct OTTD_QuartzGammaTable {
       
   116 	CGGammaValue red[QZ_GAMMA_TABLE_SIZE];
       
   117 	CGGammaValue green[QZ_GAMMA_TABLE_SIZE];
       
   118 	CGGammaValue blue[QZ_GAMMA_TABLE_SIZE];
       
   119 };
       
   120 
       
   121 /* Add methods to get at private members of NSScreen.
       
   122  * Since there is a bug in Apple's screen switching code that does not update
       
   123  * this variable when switching to fullscreen, we'll set it manually (but only
       
   124  * for the main screen).
       
   125  */
       
   126 @interface NSScreen (NSScreenAccess)
       
   127 	- (void) setFrame:(NSRect)frame;
       
   128 @end
       
   129 
       
   130 @implementation NSScreen (NSScreenAccess)
       
   131 - (void) setFrame:(NSRect)frame;
       
   132 {
       
   133 	_frame = frame;
       
   134 }
       
   135 @end
       
   136 
       
   137 
       
   138 static void QZ_Draw();
       
   139 static void QZ_UnsetVideoMode();
       
   140 static void QZ_UpdatePalette(uint start, uint count);
       
   141 static void QZ_WarpCursor(int x, int y);
       
   142 static void QZ_ShowMouse();
       
   143 static void QZ_HideMouse();
       
   144 
       
   145 
       
   146 static NSAutoreleasePool *_ottd_autorelease_pool;
       
   147 static OTTDMain *_ottd_main;
       
   148 
       
   149 
       
   150 static struct CocoaVideoData {
       
   151 	bool isset;
       
   152 	bool issetting;
       
   153 
       
   154 	CGDirectDisplayID  display_id;         /* 0 == main display (only support single display) */
       
   155 	CFDictionaryRef    mode;               /* current mode of the display */
       
   156 	CFDictionaryRef    save_mode;          /* original mode of the display */
       
   157 	CFArrayRef         mode_list;          /* list of available fullscreen modes */
       
   158 	CGDirectPaletteRef palette;            /* palette of an 8-bit display */
       
   159 
       
   160 	uint32 device_width;
       
   161 	uint32 device_height;
       
   162 	uint32 device_bpp;
       
   163 
       
   164 	void *realpixels;
       
   165 	uint8 *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 = (uint8*)malloc(newViewFrame.size.width * newViewFrame.size.height);
       
   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 	const uint32* pal = _cocoa_video_data.palette32;
       
  1023 	const uint8* src = _cocoa_video_data.pixels;
       
  1024 	uint32* dst = (uint32*)_cocoa_video_data.realpixels;
       
  1025 	uint width = _cocoa_video_data.width;
       
  1026 	uint pitch = _cocoa_video_data.pitch / 4;
       
  1027 	uint x;
       
  1028 	uint y;
       
  1029 
       
  1030 	for (y = top; y < bottom; y++) {
       
  1031 		for (x = left; x < right; x++) {
       
  1032 			dst[y * pitch + x] = pal[src[y * width + x]];
       
  1033 		}
       
  1034 	}
       
  1035 }
       
  1036 
       
  1037 static inline void QZ_WindowBlitIndexedPixelsToView16(uint left, uint top, uint right, uint bottom)
       
  1038 {
       
  1039 	const uint16* pal = _cocoa_video_data.palette16;
       
  1040 	const uint8* src = _cocoa_video_data.pixels;
       
  1041 	uint16* dst = (uint16*)_cocoa_video_data.realpixels;
       
  1042 	uint width = _cocoa_video_data.width;
       
  1043 	uint pitch = _cocoa_video_data.pitch / 2;
       
  1044 	uint x;
       
  1045 	uint y;
       
  1046 
       
  1047 	for (y = top; y < bottom; y++) {
       
  1048 		for (x = left; x < right; x++) {
       
  1049 			dst[y * pitch + x] = pal[src[y * width + x]];
       
  1050 		}
       
  1051 	}
       
  1052 }
       
  1053 
       
  1054 static inline void QZ_WindowBlitIndexedPixelsToView(int left, int top, int right, int bottom)
       
  1055 {
       
  1056 	switch (_cocoa_video_data.device_bpp) {
       
  1057 		case 32: QZ_WindowBlitIndexedPixelsToView32(left, top, right, bottom); break;
       
  1058 		case 16: QZ_WindowBlitIndexedPixelsToView16(left, top, right, bottom); break;
       
  1059 	}
       
  1060 }
       
  1061 
       
  1062 static bool _resize_icon[] = {
       
  1063 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
       
  1064 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1,
       
  1065 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0,
       
  1066 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0,
       
  1067 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1,
       
  1068 	0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1,
       
  1069 	0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0,
       
  1070 	0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
       
  1071 	0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
       
  1072 	0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
       
  1073 	0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
       
  1074 	0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0,
       
  1075 	0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1,
       
  1076 	0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1,
       
  1077 	0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0,
       
  1078 	1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0
       
  1079 };
       
  1080 
       
  1081 static void QZ_DrawResizeIcon()
       
  1082 {
       
  1083 	int xoff = _cocoa_video_data.width - 16;
       
  1084 	int yoff = _cocoa_video_data.height - 16;
       
  1085 	int x;
       
  1086 	int y;
       
  1087 
       
  1088 	for (y = 0; y < 16; y++) {
       
  1089 		uint16* trg16 = (uint16*)_cocoa_video_data.realpixels + (yoff + y) * _cocoa_video_data.pitch / 2 + xoff;
       
  1090 		uint32* trg32 = (uint32*)_cocoa_video_data.realpixels + (yoff + y) * _cocoa_video_data.pitch / 4 + xoff;
       
  1091 
       
  1092 		for (x = 0; x < 16; x++, trg16++, trg32++) {
       
  1093 			if (!_resize_icon[y * 16 + x]) continue;
       
  1094 
       
  1095 			switch (_cocoa_video_data.device_bpp) {
       
  1096 				case 32: *trg32 = 0xff000000; break;
       
  1097 				case 16: *trg16 = 0x0000;     break;
       
  1098 			}
       
  1099 		}
       
  1100 	}
       
  1101 }
       
  1102 
       
  1103 static void QZ_DrawWindow()
       
  1104 {
       
  1105 	int i;
       
  1106 	RgnHandle dirty, temp;
       
  1107 
       
  1108 	/* Check if we need to do anything */
       
  1109 	if (_cocoa_video_data.num_dirty_rects == 0 ||
       
  1110 			[ _cocoa_video_data.window isMiniaturized ]) {
       
  1111 		return;
       
  1112 	}
       
  1113 
       
  1114 	if (_cocoa_video_data.num_dirty_rects >= MAX_DIRTY_RECTS) {
       
  1115 		_cocoa_video_data.num_dirty_rects = 1;
       
  1116 		_cocoa_video_data.dirty_rects[0].left = 0;
       
  1117 		_cocoa_video_data.dirty_rects[0].top = 0;
       
  1118 		_cocoa_video_data.dirty_rects[0].right = _cocoa_video_data.width;
       
  1119 		_cocoa_video_data.dirty_rects[0].bottom = _cocoa_video_data.height;
       
  1120 	}
       
  1121 
       
  1122 	dirty = NewRgn();
       
  1123 	temp  = NewRgn();
       
  1124 
       
  1125 	SetEmptyRgn(dirty);
       
  1126 
       
  1127 	/* Build the region of dirty rectangles */
       
  1128 	for (i = 0; i < _cocoa_video_data.num_dirty_rects; i++) {
       
  1129 		QZ_WindowBlitIndexedPixelsToView(
       
  1130 			_cocoa_video_data.dirty_rects[i].left,
       
  1131 			_cocoa_video_data.dirty_rects[i].top,
       
  1132 			_cocoa_video_data.dirty_rects[i].right,
       
  1133 			_cocoa_video_data.dirty_rects[i].bottom
       
  1134 		);
       
  1135 
       
  1136 		MacSetRectRgn(
       
  1137 			temp,
       
  1138 			_cocoa_video_data.dirty_rects[i].left,
       
  1139 			_cocoa_video_data.dirty_rects[i].top,
       
  1140 			_cocoa_video_data.dirty_rects[i].right,
       
  1141 			_cocoa_video_data.dirty_rects[i].bottom
       
  1142 		);
       
  1143 		MacUnionRgn(dirty, temp, dirty);
       
  1144 	}
       
  1145 
       
  1146 	QZ_DrawResizeIcon();
       
  1147 
       
  1148 	/* Flush the dirty region */
       
  1149 	QDFlushPortBuffer( (OpaqueGrafPtr*) [ _cocoa_video_data.qdview qdPort ], dirty);
       
  1150 	DisposeRgn(dirty);
       
  1151 	DisposeRgn(temp);
       
  1152 
       
  1153 	_cocoa_video_data.num_dirty_rects = 0;
       
  1154 }
       
  1155 
       
  1156 
       
  1157 extern const char _openttd_revision[];
       
  1158 
       
  1159 static const char* QZ_SetVideoWindowed(uint width, uint height)
       
  1160 {
       
  1161 	char caption[50];
       
  1162 	NSString *nsscaption;
       
  1163 	unsigned int style;
       
  1164 	NSRect contentRect;
       
  1165 	BOOL isCustom = NO;
       
  1166 
       
  1167 	if (width > _cocoa_video_data.device_width)
       
  1168 		width = _cocoa_video_data.device_width;
       
  1169 	if (height > _cocoa_video_data.device_height)
       
  1170 		height = _cocoa_video_data.device_height;
       
  1171 
       
  1172 	_cocoa_video_data.width = width;
       
  1173 	_cocoa_video_data.height = height;
       
  1174 
       
  1175 	contentRect = NSMakeRect(0, 0, width, height);
       
  1176 
       
  1177 	/* Check if we should completely destroy the previous mode
       
  1178 	 * - If it is fullscreen
       
  1179 	 */
       
  1180 	if (_cocoa_video_data.isset && _cocoa_video_data.fullscreen)
       
  1181 		QZ_UnsetVideoMode();
       
  1182 
       
  1183 	/* Check if we should recreate the window */
       
  1184 	if (_cocoa_video_data.window == nil) {
       
  1185 		/* Set the window style */
       
  1186 		style = NSTitledWindowMask;
       
  1187 		style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
       
  1188 		style |= NSResizableWindowMask;
       
  1189 
       
  1190 		/* Manually create a window, avoids having a nib file resource */
       
  1191 		_cocoa_video_data.window = [ [ OTTD_QuartzWindow alloc ]
       
  1192 										initWithContentRect:contentRect
       
  1193 										styleMask:style
       
  1194 										backing:NSBackingStoreBuffered
       
  1195 										defer:NO ];
       
  1196 
       
  1197 		if (_cocoa_video_data.window == nil)
       
  1198 			return "Could not create the Cocoa window";
       
  1199 
       
  1200 		snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
       
  1201 		nsscaption = [ [ NSString alloc ] initWithCString:caption ];
       
  1202 		[ _cocoa_video_data.window setTitle:nsscaption ];
       
  1203 		[ _cocoa_video_data.window setMiniwindowTitle:nsscaption ];
       
  1204 		[ nsscaption release ];
       
  1205 
       
  1206 		[ _cocoa_video_data.window setAcceptsMouseMovedEvents:YES ];
       
  1207 		[ _cocoa_video_data.window setViewsNeedDisplay:NO ];
       
  1208 
       
  1209 		[ _cocoa_video_data.window setDelegate: [ [ [ OTTD_QuartzWindowDelegate alloc ] init ] autorelease ] ];
       
  1210 	} else {
       
  1211 		/* We already have a window, just change its size */
       
  1212 		if (!isCustom) {
       
  1213 			[ _cocoa_video_data.window setContentSize:contentRect.size ];
       
  1214 			// Ensure frame height - title bar height >= view height
       
  1215 			contentRect.size.height = clamp(height, 0, [ _cocoa_video_data.window frame ].size.height - 22 /* 22 is the height of title bar of window*/);
       
  1216 			height = contentRect.size.height;
       
  1217 			[ _cocoa_video_data.qdview setFrameSize:contentRect.size ];
       
  1218 		}
       
  1219 	}
       
  1220 
       
  1221 	// Update again
       
  1222 	_cocoa_video_data.width = width;
       
  1223 	_cocoa_video_data.height = height;
       
  1224 
       
  1225 	[ _cocoa_video_data.window center ];
       
  1226 
       
  1227 	/* Only recreate the view if it doesn't already exist */
       
  1228 	if (_cocoa_video_data.qdview == nil) {
       
  1229 		_cocoa_video_data.qdview = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ];
       
  1230 		[ _cocoa_video_data.qdview setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
       
  1231 		[ [ _cocoa_video_data.window contentView ] addSubview:_cocoa_video_data.qdview ];
       
  1232 		[ _cocoa_video_data.qdview release ];
       
  1233 		[ _cocoa_video_data.window makeKeyAndOrderFront:nil ];
       
  1234 	}
       
  1235 
       
  1236 	CGrafPtr thePort = (OpaqueGrafPtr*) [ _cocoa_video_data.qdview qdPort ];
       
  1237 
       
  1238 	LockPortBits(thePort);
       
  1239 	_cocoa_video_data.realpixels = GetPixBaseAddr(GetPortPixMap(thePort));
       
  1240 	_cocoa_video_data.pitch = GetPixRowBytes(GetPortPixMap(thePort));
       
  1241 	UnlockPortBits(thePort);
       
  1242 
       
  1243 	/* _cocoa_video_data.realpixels now points to the window's pixels
       
  1244 	 * We want it to point to the *view's* pixels
       
  1245 	 */
       
  1246 	{
       
  1247 		int vOffset = [ _cocoa_video_data.window frame ].size.height - [ _cocoa_video_data.qdview frame ].size.height - [ _cocoa_video_data.qdview frame ].origin.y;
       
  1248 		int hOffset = [ _cocoa_video_data.qdview frame ].origin.x;
       
  1249 
       
  1250 		_cocoa_video_data.realpixels = (uint8*)_cocoa_video_data.realpixels + (vOffset * _cocoa_video_data.pitch) + hOffset * (_cocoa_video_data.device_bpp / 8);
       
  1251 	}
       
  1252 
       
  1253 	free(_cocoa_video_data.pixels);
       
  1254 	_cocoa_video_data.pixels = (uint8*)malloc(width * height);
       
  1255 	if (_cocoa_video_data.pixels == NULL) return "Failed to allocate 8-bit buffer";
       
  1256 
       
  1257 	_cocoa_video_data.fullscreen = false;
       
  1258 
       
  1259 	return NULL;
       
  1260 }
       
  1261 
       
  1262 
       
  1263 /******************************************************************************
       
  1264  *                             Fullscreen mode                                *
       
  1265  ******************************************************************************/
       
  1266 
       
  1267 /* Gamma functions to try to hide the flash from a rez switch
       
  1268  * Fade the display from normal to black
       
  1269  * Save gamma tables for fade back to normal
       
  1270  */
       
  1271 static uint32 QZ_FadeGammaOut(OTTD_QuartzGammaTable* table)
       
  1272 {
       
  1273 	CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE];
       
  1274 	CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE];
       
  1275 	CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE];
       
  1276 	float percent;
       
  1277 	int j;
       
  1278 	unsigned int actual;
       
  1279 
       
  1280 	if (CGGetDisplayTransferByTable(
       
  1281 				_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE,
       
  1282 				table->red, table->green, table->blue, &actual
       
  1283 			) != CGDisplayNoErr ||
       
  1284 			actual != QZ_GAMMA_TABLE_SIZE) {
       
  1285 		return 1;
       
  1286 	}
       
  1287 
       
  1288 	memcpy(redTable,   table->red,   sizeof(redTable));
       
  1289 	memcpy(greenTable, table->green, sizeof(greenTable));
       
  1290 	memcpy(blueTable,  table->blue,  sizeof(greenTable));
       
  1291 
       
  1292 	for (percent = 1.0; percent >= 0.0; percent -= 0.01) {
       
  1293 		for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
       
  1294 			redTable[j]   = redTable[j]   * percent;
       
  1295 			greenTable[j] = greenTable[j] * percent;
       
  1296 			blueTable[j]  = blueTable[j]  * percent;
       
  1297 		}
       
  1298 
       
  1299 		if (CGSetDisplayTransferByTable(
       
  1300 					_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE,
       
  1301 					redTable, greenTable, blueTable
       
  1302 				) != CGDisplayNoErr) {
       
  1303 			CGDisplayRestoreColorSyncSettings();
       
  1304 			return 1;
       
  1305 		}
       
  1306 
       
  1307 		CSleep(10);
       
  1308 	}
       
  1309 
       
  1310 	return 0;
       
  1311 }
       
  1312 
       
  1313 /* Fade the display from black to normal
       
  1314  * Restore previously saved gamma values
       
  1315  */
       
  1316 static uint32 QZ_FadeGammaIn(const OTTD_QuartzGammaTable* table)
       
  1317 {
       
  1318 	CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE];
       
  1319 	CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE];
       
  1320 	CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE];
       
  1321 	float percent;
       
  1322 	int j;
       
  1323 
       
  1324 	memset(redTable, 0, sizeof(redTable));
       
  1325 	memset(greenTable, 0, sizeof(greenTable));
       
  1326 	memset(blueTable, 0, sizeof(greenTable));
       
  1327 
       
  1328 	for (percent = 0.0; percent <= 1.0; percent += 0.01) {
       
  1329 		for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
       
  1330 			redTable[j]   = table->red[j]   * percent;
       
  1331 			greenTable[j] = table->green[j] * percent;
       
  1332 			blueTable[j]  = table->blue[j]  * percent;
       
  1333 		}
       
  1334 
       
  1335 		if (CGSetDisplayTransferByTable(
       
  1336 					_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE,
       
  1337 					redTable, greenTable, blueTable
       
  1338 				) != CGDisplayNoErr) {
       
  1339 			CGDisplayRestoreColorSyncSettings();
       
  1340 			return 1;
       
  1341 		}
       
  1342 
       
  1343 		CSleep(10);
       
  1344 	}
       
  1345 
       
  1346 	return 0;
       
  1347 }
       
  1348 
       
  1349 static const char* QZ_SetVideoFullScreen(int width, int height)
       
  1350 {
       
  1351 	const char* errstr = "QZ_SetVideoFullScreen error";
       
  1352 	int exact_match;
       
  1353 	CFNumberRef number;
       
  1354 	int bpp;
       
  1355 	int gamma_error;
       
  1356 	OTTD_QuartzGammaTable gamma_table;
       
  1357 	NSRect screen_rect;
       
  1358 	CGError error;
       
  1359 	NSPoint pt;
       
  1360 
       
  1361 	/* Destroy any previous mode */
       
  1362 	if (_cocoa_video_data.isset) QZ_UnsetVideoMode();
       
  1363 
       
  1364 	/* See if requested mode exists */
       
  1365 	_cocoa_video_data.mode = CGDisplayBestModeForParameters(_cocoa_video_data.display_id, 8, width, height, &exact_match);
       
  1366 
       
  1367 	/* If the mode wasn't an exact match, check if it has the right bpp, and update width and height */
       
  1368 	if (!exact_match) {
       
  1369 		number = (const __CFNumber*) CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayBitsPerPixel);
       
  1370 		CFNumberGetValue(number, kCFNumberSInt32Type, &bpp);
       
  1371 		if (bpp != 8) {
       
  1372 			errstr = "Failed to find display resolution";
       
  1373 			goto ERR_NO_MATCH;
       
  1374 		}
       
  1375 
       
  1376 		number = (const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayWidth);
       
  1377 		CFNumberGetValue(number, kCFNumberSInt32Type, &width);
       
  1378 
       
  1379 		number = (const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayHeight);
       
  1380 		CFNumberGetValue(number, kCFNumberSInt32Type, &height);
       
  1381 	}
       
  1382 
       
  1383 	/* Fade display to zero gamma */
       
  1384 	gamma_error = QZ_FadeGammaOut(&gamma_table);
       
  1385 
       
  1386 	/* Put up the blanking window (a window above all other windows) */
       
  1387 	error = CGDisplayCapture(_cocoa_video_data.display_id);
       
  1388 
       
  1389 	if (CGDisplayNoErr != error) {
       
  1390 		errstr = "Failed capturing display";
       
  1391 		goto ERR_NO_CAPTURE;
       
  1392 	}
       
  1393 
       
  1394 	/* Do the physical switch */
       
  1395 	if (CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.mode) != CGDisplayNoErr) {
       
  1396 		errstr = "Failed switching display resolution";
       
  1397 		goto ERR_NO_SWITCH;
       
  1398 	}
       
  1399 
       
  1400 	_cocoa_video_data.realpixels = (uint8*)CGDisplayBaseAddress(_cocoa_video_data.display_id);
       
  1401 	_cocoa_video_data.pitch  = CGDisplayBytesPerRow(_cocoa_video_data.display_id);
       
  1402 
       
  1403 	_cocoa_video_data.width = CGDisplayPixelsWide(_cocoa_video_data.display_id);
       
  1404 	_cocoa_video_data.height = CGDisplayPixelsHigh(_cocoa_video_data.display_id);
       
  1405 	_cocoa_video_data.fullscreen = true;
       
  1406 
       
  1407 	/* Setup double-buffer emulation */
       
  1408 	_cocoa_video_data.pixels = (uint8*)malloc(width * height);
       
  1409 	if (_cocoa_video_data.pixels == NULL) {
       
  1410 		errstr = "Failed to allocate memory for double buffering";
       
  1411 		goto ERR_DOUBLEBUF;
       
  1412 	}
       
  1413 
       
  1414 	if (!CGDisplayCanSetPalette(_cocoa_video_data.display_id)) {
       
  1415 		errstr = "Not an indexed display mode.";
       
  1416 		goto ERR_NOT_INDEXED;
       
  1417 	}
       
  1418 
       
  1419 	/* If we don't hide menu bar, it will get events and interrupt the program */
       
  1420 	HideMenuBar();
       
  1421 
       
  1422 	/* Fade the display to original gamma */
       
  1423 	if (!gamma_error) QZ_FadeGammaIn(&gamma_table);
       
  1424 
       
  1425 	/* There is a bug in Cocoa where NSScreen doesn't synchronize
       
  1426 	 * with CGDirectDisplay, so the main screen's frame is wrong.
       
  1427 	 * As a result, coordinate translation produces incorrect results.
       
  1428 	 * We can hack around this bug by setting the screen rect ourselves.
       
  1429 	 * This hack should be removed if/when the bug is fixed.
       
  1430 	 */
       
  1431 	screen_rect = NSMakeRect(0, 0, width, height);
       
  1432 	[ [ NSScreen mainScreen ] setFrame:screen_rect ];
       
  1433 
       
  1434 	/* we're fullscreen, so flag all input states... */
       
  1435 	_cocoa_video_data.active = true;
       
  1436 
       
  1437 
       
  1438 	pt = [ NSEvent mouseLocation ];
       
  1439 	pt.y = CGDisplayPixelsHigh(_cocoa_video_data.display_id) - pt.y;
       
  1440 	if (QZ_MouseIsInsideView(&pt)) QZ_HideMouse();
       
  1441 
       
  1442 	return NULL;
       
  1443 
       
  1444 /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
       
  1445 ERR_NOT_INDEXED:
       
  1446 	free(_cocoa_video_data.pixels);
       
  1447 	_cocoa_video_data.pixels = NULL;
       
  1448 ERR_DOUBLEBUF:
       
  1449 	CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.save_mode);
       
  1450 ERR_NO_SWITCH:
       
  1451 	CGReleaseAllDisplays();
       
  1452 ERR_NO_CAPTURE:
       
  1453 	if (!gamma_error) QZ_FadeGammaIn(&gamma_table);
       
  1454 ERR_NO_MATCH:
       
  1455 	return errstr;
       
  1456 }
       
  1457 
       
  1458 
       
  1459 static void QZ_UpdateFullscreenPalette(uint first_color, uint num_colors)
       
  1460 {
       
  1461 	CGTableCount  index;
       
  1462 	CGDeviceColor color;
       
  1463 
       
  1464 	for (index = first_color; index < first_color+num_colors; index++) {
       
  1465 		/* Clamp colors between 0.0 and 1.0 */
       
  1466 		color.red   = _cur_palette[index].r / 255.0;
       
  1467 		color.blue  = _cur_palette[index].b / 255.0;
       
  1468 		color.green = _cur_palette[index].g / 255.0;
       
  1469 
       
  1470 		CGPaletteSetColorAtIndex(_cocoa_video_data.palette, color, index);
       
  1471 	}
       
  1472 
       
  1473 	CGDisplaySetPalette(_cocoa_video_data.display_id, _cocoa_video_data.palette);
       
  1474 }
       
  1475 
       
  1476 /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
       
  1477 static void QZ_WaitForVerticalBlank()
       
  1478 {
       
  1479 	/* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
       
  1480 	double refreshRate;
       
  1481 	double linesPerSecond;
       
  1482 	double target;
       
  1483 	double position;
       
  1484 	double adjustment;
       
  1485 	CFNumberRef refreshRateCFNumber;
       
  1486 
       
  1487 	refreshRateCFNumber = (const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayRefreshRate);
       
  1488 	if (refreshRateCFNumber == NULL) return;
       
  1489 
       
  1490 	if (CFNumberGetValue(refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) == 0)
       
  1491 		return;
       
  1492 
       
  1493 	if (refreshRate == 0) return;
       
  1494 
       
  1495 	linesPerSecond = refreshRate * _cocoa_video_data.height;
       
  1496 	target = _cocoa_video_data.height;
       
  1497 
       
  1498 	/* Figure out the first delay so we start off about right */
       
  1499 	position = CGDisplayBeamPosition(_cocoa_video_data.display_id);
       
  1500 	if (position > target) position = 0;
       
  1501 
       
  1502 	adjustment = (target - position) / linesPerSecond;
       
  1503 
       
  1504 	CSleep((uint32)(adjustment * 1000));
       
  1505 }
       
  1506 
       
  1507 
       
  1508 static void QZ_DrawScreen()
       
  1509 {
       
  1510 	const uint8* src = _cocoa_video_data.pixels;
       
  1511 	uint8* dst       = (uint8*)_cocoa_video_data.realpixels;
       
  1512 	uint pitch       = _cocoa_video_data.pitch;
       
  1513 	uint width       = _cocoa_video_data.width;
       
  1514 	uint num_dirty   = _cocoa_video_data.num_dirty_rects;
       
  1515 	uint i;
       
  1516 
       
  1517 	/* Check if we need to do anything */
       
  1518 	if (num_dirty == 0) return;
       
  1519 
       
  1520 	if (num_dirty >= MAX_DIRTY_RECTS) {
       
  1521 		num_dirty = 1;
       
  1522 		_cocoa_video_data.dirty_rects[0].left   = 0;
       
  1523 		_cocoa_video_data.dirty_rects[0].top    = 0;
       
  1524 		_cocoa_video_data.dirty_rects[0].right  = _cocoa_video_data.width;
       
  1525 		_cocoa_video_data.dirty_rects[0].bottom = _cocoa_video_data.height;
       
  1526 	}
       
  1527 
       
  1528 	QZ_WaitForVerticalBlank();
       
  1529 	/* Build the region of dirty rectangles */
       
  1530 	for (i = 0; i < num_dirty; i++) {
       
  1531 		uint y      = _cocoa_video_data.dirty_rects[i].top;
       
  1532 		uint left   = _cocoa_video_data.dirty_rects[i].left;
       
  1533 		uint length = _cocoa_video_data.dirty_rects[i].right - left;
       
  1534 		uint bottom = _cocoa_video_data.dirty_rects[i].bottom;
       
  1535 
       
  1536 		for (; y < bottom; y++) {
       
  1537 			memcpy(dst + y * pitch + left, src + y * width + left, length);
       
  1538 		}
       
  1539 	}
       
  1540 
       
  1541 	_cocoa_video_data.num_dirty_rects = 0;
       
  1542 }
       
  1543 
       
  1544 
       
  1545 static int QZ_ListFullscreenModes(OTTDPoint* mode_list, int max_modes)
       
  1546 {
       
  1547 	CFIndex num_modes;
       
  1548 	CFIndex i;
       
  1549 	int list_size = 0;
       
  1550 
       
  1551 	num_modes = CFArrayGetCount(_cocoa_video_data.mode_list);
       
  1552 
       
  1553 	/* Build list of modes with the requested bpp */
       
  1554 	for (i = 0; i < num_modes && list_size < max_modes; i++) {
       
  1555 		CFDictionaryRef onemode;
       
  1556 		CFNumberRef     number;
       
  1557 		int bpp;
       
  1558 		int intvalue;
       
  1559 		bool hasMode;
       
  1560 		uint16 width, height;
       
  1561 
       
  1562 		onemode = (const __CFDictionary*)CFArrayGetValueAtIndex(_cocoa_video_data.mode_list, i);
       
  1563 		number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayBitsPerPixel);
       
  1564 		CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
       
  1565 
       
  1566 		if (bpp != 8) continue;
       
  1567 
       
  1568 		number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayWidth);
       
  1569 		CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue);
       
  1570 		width = (uint16)intvalue;
       
  1571 
       
  1572 		number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayHeight);
       
  1573 		CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue);
       
  1574 		height = (uint16)intvalue;
       
  1575 
       
  1576 		/* Check if mode is already in the list */
       
  1577 		{
       
  1578 			int i;
       
  1579 			hasMode = false;
       
  1580 			for (i = 0; i < list_size; i++) {
       
  1581 				if (mode_list[i].x == width &&  mode_list[i].y == height) {
       
  1582 					hasMode = true;
       
  1583 					break;
       
  1584 				}
       
  1585 			}
       
  1586 		}
       
  1587 
       
  1588 		if (hasMode) continue;
       
  1589 
       
  1590 		/* Add mode to the list */
       
  1591 		mode_list[list_size].x = width;
       
  1592 		mode_list[list_size].y = height;
       
  1593 		list_size++;
       
  1594 	}
       
  1595 
       
  1596 	/* Sort list smallest to largest */
       
  1597 	{
       
  1598 		int i, j;
       
  1599 		for (i = 0; i < list_size; i++) {
       
  1600 			for (j = 0; j < list_size-1; j++) {
       
  1601 				if (mode_list[j].x > mode_list[j + 1].x || (
       
  1602 							mode_list[j].x == mode_list[j + 1].x &&
       
  1603 							mode_list[j].y >  mode_list[j + 1].y
       
  1604 						)) {
       
  1605 					uint tmpw = mode_list[j].x;
       
  1606 					uint tmph = mode_list[j].y;
       
  1607 
       
  1608 					mode_list[j].x = mode_list[j + 1].x;
       
  1609 					mode_list[j].y = mode_list[j + 1].y;
       
  1610 
       
  1611 					mode_list[j + 1].x = tmpw;
       
  1612 					mode_list[j + 1].y = tmph;
       
  1613 				}
       
  1614 			}
       
  1615 		}
       
  1616 	}
       
  1617 
       
  1618 	return list_size;
       
  1619 }
       
  1620 
       
  1621 
       
  1622 /******************************************************************************
       
  1623  *                             Windowed and fullscreen common code            *
       
  1624  ******************************************************************************/
       
  1625 
       
  1626 static void QZ_UpdatePalette(uint start, uint count)
       
  1627 {
       
  1628 	if (_cocoa_video_data.fullscreen) {
       
  1629 		QZ_UpdateFullscreenPalette(start, count);
       
  1630 	} else {
       
  1631 		QZ_UpdateWindowPalette(start, count);
       
  1632 	}
       
  1633 }
       
  1634 
       
  1635 static void QZ_InitPalette()
       
  1636 {
       
  1637 	QZ_UpdatePalette(0, 256);
       
  1638 }
       
  1639 
       
  1640 static void QZ_Draw()
       
  1641 {
       
  1642 	if (_cocoa_video_data.fullscreen) {
       
  1643 		QZ_DrawScreen();
       
  1644 	} else {
       
  1645 		QZ_DrawWindow();
       
  1646 	}
       
  1647 }
       
  1648 
       
  1649 
       
  1650 static const OTTDPoint _default_resolutions[] = {
       
  1651 	{ 640,  480},
       
  1652 	{ 800,  600},
       
  1653 	{1024,  768},
       
  1654 	{1152,  864},
       
  1655 	{1280,  800},
       
  1656 	{1280,  960},
       
  1657 	{1280, 1024},
       
  1658 	{1400, 1050},
       
  1659 	{1600, 1200},
       
  1660 	{1680, 1050},
       
  1661 	{1920, 1200}
       
  1662 };
       
  1663 
       
  1664 static void QZ_UpdateVideoModes()
       
  1665 {
       
  1666 	uint i, j, count;
       
  1667 	OTTDPoint modes[32];
       
  1668 	const OTTDPoint *current_modes;
       
  1669 
       
  1670 	if (_cocoa_video_data.fullscreen) {
       
  1671 		count = QZ_ListFullscreenModes(modes, 32);
       
  1672 		current_modes = modes;
       
  1673 	} else {
       
  1674 		count = lengthof(_default_resolutions);
       
  1675 		current_modes = _default_resolutions;
       
  1676 	}
       
  1677 
       
  1678 	for (i = 0, j = 0; j < lengthof(_resolutions) && i < count; i++) {
       
  1679 		if (_cocoa_video_data.fullscreen || (
       
  1680 					(uint)current_modes[i].x < _cocoa_video_data.device_width &&
       
  1681 					(uint)current_modes[i].y < _cocoa_video_data.device_height)
       
  1682 				) {
       
  1683 			_resolutions[j][0] = current_modes[i].x;
       
  1684 			_resolutions[j][1] = current_modes[i].y;
       
  1685 			j++;
       
  1686 		}
       
  1687 	}
       
  1688 
       
  1689 	_num_resolutions = j;
       
  1690 }
       
  1691 
       
  1692 static void QZ_UnsetVideoMode()
       
  1693 {
       
  1694 	if (_cocoa_video_data.fullscreen) {
       
  1695 		/* Release fullscreen resources */
       
  1696 		OTTD_QuartzGammaTable gamma_table;
       
  1697 		int gamma_error;
       
  1698 		NSRect screen_rect;
       
  1699 
       
  1700 		gamma_error = QZ_FadeGammaOut(&gamma_table);
       
  1701 
       
  1702 		/* Restore original screen resolution/bpp */
       
  1703 		CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.save_mode);
       
  1704 		CGReleaseAllDisplays();
       
  1705 		ShowMenuBar();
       
  1706 		/* Reset the main screen's rectangle
       
  1707 		 * See comment in QZ_SetVideoFullscreen for why we do this
       
  1708 		 */
       
  1709 		screen_rect = NSMakeRect(0,0,_cocoa_video_data.device_width,_cocoa_video_data.device_height);
       
  1710 		[ [ NSScreen mainScreen ] setFrame:screen_rect ];
       
  1711 
       
  1712 		if (!gamma_error) QZ_FadeGammaIn(&gamma_table);
       
  1713 	} else {
       
  1714 		/* Release window mode resources */
       
  1715 		[ _cocoa_video_data.window close ];
       
  1716 		_cocoa_video_data.window = nil;
       
  1717 		_cocoa_video_data.qdview = nil;
       
  1718 	}
       
  1719 
       
  1720 	free(_cocoa_video_data.pixels);
       
  1721 	_cocoa_video_data.pixels = NULL;
       
  1722 
       
  1723 	/* Signal successful teardown */
       
  1724 	_cocoa_video_data.isset = false;
       
  1725 
       
  1726 	QZ_ShowMouse();
       
  1727 }
       
  1728 
       
  1729 
       
  1730 static const char* QZ_SetVideoMode(uint width, uint height, bool fullscreen)
       
  1731 {
       
  1732 	const char *ret;
       
  1733 
       
  1734 	_cocoa_video_data.issetting = true;
       
  1735 	if (fullscreen) {
       
  1736 		/* Setup full screen video */
       
  1737 		ret = QZ_SetVideoFullScreen(width, height);
       
  1738 	} else {
       
  1739 		/* Setup windowed video */
       
  1740 		ret = QZ_SetVideoWindowed(width, height);
       
  1741 	}
       
  1742 	_cocoa_video_data.issetting = false;
       
  1743 	if (ret != NULL) return ret;
       
  1744 
       
  1745 	/* Signal successful completion (used internally) */
       
  1746 	_cocoa_video_data.isset = true;
       
  1747 
       
  1748 	/* Tell the game that the resolution has changed */
       
  1749 	_screen.width = _cocoa_video_data.width;
       
  1750 	_screen.height = _cocoa_video_data.height;
       
  1751 	_screen.pitch = _cocoa_video_data.width;
       
  1752 
       
  1753 	QZ_UpdateVideoModes();
       
  1754 	GameSizeChanged();
       
  1755 
       
  1756 	QZ_InitPalette();
       
  1757 
       
  1758 	return NULL;
       
  1759 }
       
  1760 
       
  1761 static const char* QZ_SetVideoModeAndRestoreOnFailure(uint width, uint height, bool fullscreen)
       
  1762 {
       
  1763 	bool wasset = _cocoa_video_data.isset;
       
  1764 	uint32 oldwidth = _cocoa_video_data.width;
       
  1765 	uint32 oldheight = _cocoa_video_data.height;
       
  1766 	bool oldfullscreen = _cocoa_video_data.fullscreen;
       
  1767 	const char *ret;
       
  1768 
       
  1769 	ret = QZ_SetVideoMode(width, height, fullscreen);
       
  1770 	if (ret != NULL && wasset) QZ_SetVideoMode(oldwidth, oldheight, oldfullscreen);
       
  1771 
       
  1772 	return ret;
       
  1773 }
       
  1774 
       
  1775 static void QZ_VideoInit()
       
  1776 {
       
  1777 	if (BlitterFactoryBase::GetCurrentBlitter()->GetScreenDepth() == 0) error("Can't use a blitter that blits 0 bpp for normal visuals");
       
  1778 
       
  1779 	memset(&_cocoa_video_data, 0, sizeof(_cocoa_video_data));
       
  1780 
       
  1781 	/* Initialize the video settings; this data persists between mode switches */
       
  1782 	_cocoa_video_data.display_id = kCGDirectMainDisplay;
       
  1783 	_cocoa_video_data.save_mode  = CGDisplayCurrentMode(_cocoa_video_data.display_id);
       
  1784 	_cocoa_video_data.mode_list  = CGDisplayAvailableModes(_cocoa_video_data.display_id);
       
  1785 	_cocoa_video_data.palette    = CGPaletteCreateDefaultColorPalette();
       
  1786 
       
  1787 	/* Gather some information that is useful to know about the display */
       
  1788 	/* Maybe this should be moved to QZ_SetVideoMode, in case this is changed after startup */
       
  1789 	CFNumberGetValue(
       
  1790 		(const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayBitsPerPixel),
       
  1791 		kCFNumberSInt32Type, &_cocoa_video_data.device_bpp
       
  1792 	);
       
  1793 
       
  1794 	CFNumberGetValue(
       
  1795 		(const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayWidth),
       
  1796 		kCFNumberSInt32Type, &_cocoa_video_data.device_width
       
  1797 	);
       
  1798 
       
  1799 	CFNumberGetValue(
       
  1800 		(const __CFNumber*)CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayHeight),
       
  1801 		kCFNumberSInt32Type, &_cocoa_video_data.device_height
       
  1802 	);
       
  1803 
       
  1804 	_cocoa_video_data.cursor_visible = true;
       
  1805 
       
  1806 	/* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */
       
  1807 //	QZ_RegisterForSleepNotifications();
       
  1808 }
       
  1809 
       
  1810 
       
  1811 /* Convert local coordinate to window server (CoreGraphics) coordinate */
       
  1812 static CGPoint QZ_PrivateLocalToCG(NSPoint* p)
       
  1813 {
       
  1814 	CGPoint cgp;
       
  1815 
       
  1816 	if (!_cocoa_video_data.fullscreen) {
       
  1817 		*p = [ _cocoa_video_data.qdview convertPoint:*p toView: nil ];
       
  1818 		*p = [ _cocoa_video_data.window convertBaseToScreen:*p ];
       
  1819 		p->y = _cocoa_video_data.device_height - p->y;
       
  1820 	}
       
  1821 
       
  1822 	cgp.x = p->x;
       
  1823 	cgp.y = p->y;
       
  1824 
       
  1825 	return cgp;
       
  1826 }
       
  1827 
       
  1828 static void QZ_WarpCursor(int x, int y)
       
  1829 {
       
  1830 	NSPoint p;
       
  1831 	CGPoint cgp;
       
  1832 
       
  1833 	/* Only allow warping when in foreground */
       
  1834 	if (![ NSApp isActive ]) return;
       
  1835 
       
  1836 	p = NSMakePoint(x, y);
       
  1837 	cgp = QZ_PrivateLocalToCG(&p);
       
  1838 
       
  1839 	/* this is the magic call that fixes cursor "freezing" after warp */
       
  1840 	CGSetLocalEventsSuppressionInterval(0.0);
       
  1841 	/* Do the actual warp */
       
  1842 	CGWarpMouseCursorPosition(cgp);
       
  1843 
       
  1844 	/* Generate the mouse moved event */
       
  1845 }
       
  1846 
       
  1847 static void QZ_ShowMouse()
       
  1848 {
       
  1849 	if (!_cocoa_video_data.cursor_visible) {
       
  1850 		[ NSCursor unhide ];
       
  1851 		_cocoa_video_data.cursor_visible = true;
       
  1852 
       
  1853 		// Hide the openttd cursor when leaving the window
       
  1854 		if (_cocoa_video_data.isset)
       
  1855 			UndrawMouseCursor();
       
  1856 		_cursor.in_window = false;
       
  1857 	}
       
  1858 }
       
  1859 
       
  1860 static void QZ_HideMouse()
       
  1861 {
       
  1862 	if (_cocoa_video_data.cursor_visible) {
       
  1863 #ifndef _DEBUG
       
  1864 		[ NSCursor hide ];
       
  1865 #endif
       
  1866 		_cocoa_video_data.cursor_visible = false;
       
  1867 
       
  1868 		// Show the openttd cursor again
       
  1869 		_cursor.in_window = true;
       
  1870 	}
       
  1871 }
       
  1872 
       
  1873 
       
  1874 /******************************************************************************
       
  1875  *                             OS X application creation                      *
       
  1876  ******************************************************************************/
       
  1877 
       
  1878 /* The main class of the application, the application's delegate */
       
  1879 @implementation OTTDMain
       
  1880 /* Called when the internal event loop has just started running */
       
  1881 - (void) applicationDidFinishLaunching: (NSNotification*) note
       
  1882 {
       
  1883 	/* Hand off to main application code */
       
  1884 	QZ_GameLoop();
       
  1885 
       
  1886 	/* We're done, thank you for playing */
       
  1887 	[ NSApp stop:_ottd_main ];
       
  1888 }
       
  1889 
       
  1890 /* Display the in game quit confirmation dialog */
       
  1891 - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*) sender
       
  1892 {
       
  1893 
       
  1894 	HandleExitGameRequest();
       
  1895 
       
  1896 	return NSTerminateCancel; // NSTerminateLater ?
       
  1897 }
       
  1898 @end
       
  1899 
       
  1900 static void setApplicationMenu()
       
  1901 {
       
  1902 	/* warning: this code is very odd */
       
  1903 	NSMenu *appleMenu;
       
  1904 	NSMenuItem *menuItem;
       
  1905 	NSString *title;
       
  1906 	NSString *appName;
       
  1907 
       
  1908 	appName = @"OTTD";
       
  1909 	appleMenu = [[NSMenu alloc] initWithTitle:appName];
       
  1910 
       
  1911 	/* Add menu items */
       
  1912 	title = [@"About " stringByAppendingString:appName];
       
  1913 	[appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""];
       
  1914 
       
  1915 	[appleMenu addItem:[NSMenuItem separatorItem]];
       
  1916 
       
  1917 	title = [@"Hide " stringByAppendingString:appName];
       
  1918 	[appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"];
       
  1919 
       
  1920 	menuItem = (NSMenuItem*)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"];
       
  1921 	[menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)];
       
  1922 
       
  1923 	[appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""];
       
  1924 
       
  1925 	[appleMenu addItem:[NSMenuItem separatorItem]];
       
  1926 
       
  1927 	title = [@"Quit " stringByAppendingString:appName];
       
  1928 	[appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"];
       
  1929 
       
  1930 
       
  1931 	/* Put menu into the menubar */
       
  1932 	menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""];
       
  1933 	[menuItem setSubmenu:appleMenu];
       
  1934 	[[NSApp mainMenu] addItem:menuItem];
       
  1935 
       
  1936 	/* Tell the application object that this is now the application menu */
       
  1937 	[NSApp setAppleMenu:appleMenu];
       
  1938 
       
  1939 	/* Finally give up our references to the objects */
       
  1940 	[appleMenu release];
       
  1941 	[menuItem release];
       
  1942 }
       
  1943 
       
  1944 /* Create a window menu */
       
  1945 static void setupWindowMenu()
       
  1946 {
       
  1947 	NSMenu* windowMenu;
       
  1948 	NSMenuItem* windowMenuItem;
       
  1949 	NSMenuItem* menuItem;
       
  1950 
       
  1951 	windowMenu = [[NSMenu alloc] initWithTitle:@"Window"];
       
  1952 
       
  1953 	/* "Minimize" item */
       
  1954 	menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"];
       
  1955 	[windowMenu addItem:menuItem];
       
  1956 	[menuItem release];
       
  1957 
       
  1958 	/* Put menu into the menubar */
       
  1959 	windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""];
       
  1960 	[windowMenuItem setSubmenu:windowMenu];
       
  1961 	[[NSApp mainMenu] addItem:windowMenuItem];
       
  1962 
       
  1963 	/* Tell the application object that this is now the window menu */
       
  1964 	[NSApp setWindowsMenu:windowMenu];
       
  1965 
       
  1966 	/* Finally give up our references to the objects */
       
  1967 	[windowMenu release];
       
  1968 	[windowMenuItem release];
       
  1969 }
       
  1970 
       
  1971 static void setupApplication()
       
  1972 {
       
  1973 	CPSProcessSerNum PSN;
       
  1974 
       
  1975 	/* Ensure the application object is initialised */
       
  1976 	[NSApplication sharedApplication];
       
  1977 
       
  1978 	/* Tell the dock about us */
       
  1979 	if (!CPSGetCurrentProcess(&PSN) &&
       
  1980 			!CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103) &&
       
  1981 			!CPSSetFrontProcess(&PSN)) {
       
  1982 		[NSApplication sharedApplication];
       
  1983 	}
       
  1984 
       
  1985 	/* Set up the menubar */
       
  1986 	[NSApp setMainMenu:[[NSMenu alloc] init]];
       
  1987 	setApplicationMenu();
       
  1988 	setupWindowMenu();
       
  1989 
       
  1990 	/* Create OTTDMain and make it the app delegate */
       
  1991 	_ottd_main = [[OTTDMain alloc] init];
       
  1992 	[NSApp setDelegate:_ottd_main];
       
  1993 }
       
  1994 
       
  1995 
       
  1996 /******************************************************************************
       
  1997  *                             Video driver interface                         *
       
  1998  ******************************************************************************/
       
  1999 
       
  2000 static FVideoDriver_Cocoa iFVideoDriver_Cocoa;
       
  2001 
       
  2002 void VideoDriver_Cocoa::Stop()
       
  2003 {
       
  2004 	if (!_cocoa_video_started) return;
       
  2005 
       
  2006 	if (_cocoa_video_data.isset) QZ_UnsetVideoMode();
       
  2007 
       
  2008 	[_ottd_main release];
       
  2009 
       
  2010 	_cocoa_video_started = false;
       
  2011 }
       
  2012 
       
  2013 const char *VideoDriver_Cocoa::Start(const char * const *parm)
       
  2014 {
       
  2015 	const char *ret;
       
  2016 
       
  2017 	if (_cocoa_video_started) return "Already started";
       
  2018 	_cocoa_video_started = true;
       
  2019 
       
  2020 	memset(&_cocoa_video_data, 0, sizeof(_cocoa_video_data));
       
  2021 
       
  2022 	setupApplication();
       
  2023 
       
  2024 	/* Don't create a window or enter fullscreen if we're just going to show a dialog. */
       
  2025 	if (_cocoa_video_dialog) return NULL;
       
  2026 
       
  2027 	QZ_VideoInit();
       
  2028 
       
  2029 	ret = QZ_SetVideoMode(_cur_resolution[0], _cur_resolution[1], _fullscreen);
       
  2030 	if (ret != NULL) _video_driver->Stop();
       
  2031 
       
  2032 	return ret;
       
  2033 }
       
  2034 
       
  2035 void VideoDriver_Cocoa::MakeDirty(int left, int top, int width, int height)
       
  2036 {
       
  2037 	if (_cocoa_video_data.num_dirty_rects < MAX_DIRTY_RECTS) {
       
  2038 		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].left = left;
       
  2039 		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].top = top;
       
  2040 		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].right = left + width;
       
  2041 		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].bottom = top + height;
       
  2042 	}
       
  2043 	_cocoa_video_data.num_dirty_rects++;
       
  2044 }
       
  2045 
       
  2046 void VideoDriver_Cocoa::MainLoop()
       
  2047 {
       
  2048 	/* Start the main event loop */
       
  2049 	[NSApp run];
       
  2050 }
       
  2051 
       
  2052 bool VideoDriver_Cocoa::ChangeResolution(int w, int h)
       
  2053 {
       
  2054 	const char *ret = QZ_SetVideoModeAndRestoreOnFailure((uint)w, (uint)h, _cocoa_video_data.fullscreen);
       
  2055 	if (ret != NULL) {
       
  2056 		DEBUG(driver, 0, "cocoa_v: VideoDriver_Cocoa::ChangeResolution failed with message: %s", ret);
       
  2057 	}
       
  2058 
       
  2059 	return ret == NULL;
       
  2060 }
       
  2061 
       
  2062 void VideoDriver_Cocoa::ToggleFullscreen(bool full_screen)
       
  2063 {
       
  2064 	const char *ret = QZ_SetVideoModeAndRestoreOnFailure(_cocoa_video_data.width, _cocoa_video_data.height, full_screen);
       
  2065 	if (ret != NULL) {
       
  2066 		DEBUG(driver, 0, "cocoa_v: VideoDriver_Cocoa::ToggleFullscreen failed with message: %s", ret);
       
  2067 	}
       
  2068 
       
  2069 	_fullscreen = _cocoa_video_data.fullscreen;
       
  2070 }
       
  2071 
       
  2072 
       
  2073 /* This is needed since sometimes assert is called before the videodriver is initialized */
       
  2074 void CocoaDialog(const char* title, const char* message, const char* buttonLabel)
       
  2075 {
       
  2076 	bool wasstarted;
       
  2077 
       
  2078 	_cocoa_video_dialog = true;
       
  2079 
       
  2080 	wasstarted = _cocoa_video_started;
       
  2081 	if (_video_driver == NULL) {
       
  2082 		setupApplication(); // Setup application before showing dialog
       
  2083 	} else if (!_cocoa_video_started && _video_driver->Start(NULL) != NULL) {
       
  2084 		fprintf(stderr, "%s: %s\n", title, message);
       
  2085 		return;
       
  2086 	}
       
  2087 
       
  2088 	NSRunAlertPanel([NSString stringWithCString: title], [NSString stringWithCString: message], [NSString stringWithCString: buttonLabel], nil, nil);
       
  2089 
       
  2090 	if (!wasstarted && _video_driver != NULL) _video_driver->Stop();
       
  2091 
       
  2092 	_cocoa_video_dialog = false;
       
  2093 }
       
  2094 
       
  2095 /* This is needed since OS X application bundles do not have a
       
  2096  * current directory and the data files are 'somewhere' in the bundle */
       
  2097 void cocoaSetApplicationBundleDir()
       
  2098 {
       
  2099 	char tmp[MAXPATHLEN];
       
  2100 	CFURLRef url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
       
  2101 	if (CFURLGetFileSystemRepresentation(url, true, (unsigned char*)tmp, MAXPATHLEN)) {
       
  2102 		AppendPathSeparator(tmp, lengthof(tmp));
       
  2103 		_searchpaths[SP_APPLICATION_BUNDLE_DIR] = strdup(tmp);
       
  2104 	} else {
       
  2105 		_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
       
  2106 	}
       
  2107 
       
  2108 	CFRelease(url);
       
  2109 }
       
  2110 
       
  2111 /* These are called from main() to prevent a _NSAutoreleaseNoPool error when
       
  2112  * exiting before the cocoa video driver has been loaded
       
  2113  */
       
  2114 void cocoaSetupAutoreleasePool()
       
  2115 {
       
  2116 	_ottd_autorelease_pool = [[NSAutoreleasePool alloc] init];
       
  2117 }
       
  2118 
       
  2119 void cocoaReleaseAutoreleasePool()
       
  2120 {
       
  2121 	[_ottd_autorelease_pool release];
       
  2122 }
       
  2123 
       
  2124 #endif /* WITH_COCOA */