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