src/video/cocoa/fullscreen.mm
changeset 8435 38a1ab65d6e4
parent 8423 8453e9a0f0b5
child 8448 a59e637a8eb3
equal deleted inserted replaced
8434:6cce8c248f24 8435:38a1ab65d6e4
       
     1 /* $Id$ */
       
     2 
       
     3 /******************************************************************************
       
     4  *                             Cocoa video driver                             *
       
     5  * Known things left to do:                                                   *
       
     6  *  Scale&copy the old pixel buffer to the new one when switching resolution. *
       
     7  ******************************************************************************/
       
     8 
       
     9 #ifdef WITH_COCOA
       
    10 
       
    11 #include <AvailabilityMacros.h>
       
    12 
       
    13 #import <Cocoa/Cocoa.h>
       
    14 #import <sys/time.h> /* gettimeofday */
       
    15 #import <sys/param.h> /* for MAXPATHLEN */
       
    16 #import <unistd.h>
       
    17 
       
    18 /**
       
    19  * Important notice regarding all modifications!!!!!!!
       
    20  * There are certain limitations because the file is objective C++.
       
    21  * gdb has limitations.
       
    22  * C++ and objective C code can't be joined in all cases (classes stuff).
       
    23  * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information.
       
    24  */
       
    25 
       
    26 
       
    27 /* From Menus.h (according to Xcode Developer Documentation) */
       
    28 extern "C" void ShowMenuBar();
       
    29 extern "C" void HideMenuBar();
       
    30 
       
    31 /* Defined in stdbool.h */
       
    32 #ifndef __cplusplus
       
    33 # ifndef __BEOS__
       
    34 #  undef bool
       
    35 #  undef false
       
    36 #  undef true
       
    37 # endif
       
    38 #endif
       
    39 
       
    40 
       
    41 #include "../../stdafx.h"
       
    42 #include "../../openttd.h"
       
    43 #include "../../debug.h"
       
    44 #include "../../macros.h"
       
    45 #include "../../os/macosx/splash.h"
       
    46 #include "../../variables.h"
       
    47 #include "../../gfx.h"
       
    48 #include "cocoa_v.h"
       
    49 #include "cocoa_keys.h"
       
    50 #include "../../blitter/factory.hpp"
       
    51 #include "../../fileio.h"
       
    52 
       
    53 #undef Point
       
    54 #undef Rect
       
    55 
       
    56 
       
    57 /* Structure for rez switch gamma fades
       
    58  * We can hide the monitor flicker by setting the gamma tables to 0
       
    59  */
       
    60 #define QZ_GAMMA_TABLE_SIZE 256
       
    61 
       
    62 struct OTTD_QuartzGammaTable {
       
    63 	CGGammaValue red[QZ_GAMMA_TABLE_SIZE];
       
    64 	CGGammaValue green[QZ_GAMMA_TABLE_SIZE];
       
    65 	CGGammaValue blue[QZ_GAMMA_TABLE_SIZE];
       
    66 };
       
    67 
       
    68 /* Add methods to get at private members of NSScreen.
       
    69  * Since there is a bug in Apple's screen switching code that does not update
       
    70  * this variable when switching to fullscreen, we'll set it manually (but only
       
    71  * for the main screen).
       
    72  */
       
    73 @interface NSScreen (NSScreenAccess)
       
    74 	- (void) setFrame:(NSRect)frame;
       
    75 @end
       
    76 
       
    77 @implementation NSScreen (NSScreenAccess)
       
    78 - (void) setFrame:(NSRect)frame;
       
    79 {
       
    80 	_frame = frame;
       
    81 }
       
    82 @end
       
    83 
       
    84 
       
    85 class FullscreenSubdriver: public CocoaSubdriver {
       
    86 	int                display_width;
       
    87 	int                display_height;
       
    88 	int                display_depth;
       
    89 	int                screen_pitch;
       
    90 	void*              screen_buffer;
       
    91 	void*              pixel_buffer;
       
    92 
       
    93 	CGDirectDisplayID  display_id;         /* 0 == main display (only support single display) */
       
    94 	CFDictionaryRef    cur_mode;           /* current mode of the display */
       
    95 	CFDictionaryRef    save_mode;          /* original mode of the display */
       
    96 	CGDirectPaletteRef palette;            /* palette of an 8-bit display */
       
    97 
       
    98 	#define MAX_DIRTY_RECTS 100
       
    99 	Rect dirty_rects[MAX_DIRTY_RECTS];
       
   100 	int num_dirty_rects;
       
   101 
       
   102 
       
   103 	/* Gamma functions to try to hide the flash from a rez switch
       
   104 	 * Fade the display from normal to black
       
   105 	 * Save gamma tables for fade back to normal
       
   106 	 */
       
   107 	uint32 FadeGammaOut(OTTD_QuartzGammaTable* table)
       
   108 	{
       
   109 		CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE];
       
   110 		CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE];
       
   111 		CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE];
       
   112 		float percent;
       
   113 		int j;
       
   114 		unsigned int actual;
       
   115 
       
   116 		if (CGGetDisplayTransferByTable(
       
   117 					display_id, QZ_GAMMA_TABLE_SIZE,
       
   118 					table->red, table->green, table->blue, &actual
       
   119 				) != CGDisplayNoErr ||
       
   120 				actual != QZ_GAMMA_TABLE_SIZE) {
       
   121 			return 1;
       
   122 		}
       
   123 
       
   124 		memcpy(redTable,   table->red,   sizeof(redTable));
       
   125 		memcpy(greenTable, table->green, sizeof(greenTable));
       
   126 		memcpy(blueTable,  table->blue,  sizeof(greenTable));
       
   127 
       
   128 		for (percent = 1.0; percent >= 0.0; percent -= 0.01) {
       
   129 			for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
       
   130 				redTable[j]   = redTable[j]   * percent;
       
   131 				greenTable[j] = greenTable[j] * percent;
       
   132 				blueTable[j]  = blueTable[j]  * percent;
       
   133 			}
       
   134 
       
   135 			if (CGSetDisplayTransferByTable(
       
   136 						display_id, QZ_GAMMA_TABLE_SIZE,
       
   137 						redTable, greenTable, blueTable
       
   138 					) != CGDisplayNoErr) {
       
   139 				CGDisplayRestoreColorSyncSettings();
       
   140 				return 1;
       
   141 			}
       
   142 
       
   143 			CSleep(10);
       
   144 		}
       
   145 
       
   146 		return 0;
       
   147 	}
       
   148 
       
   149 	/* Fade the display from black to normal
       
   150 	 * Restore previously saved gamma values
       
   151 	 */
       
   152 	uint32 FadeGammaIn(const OTTD_QuartzGammaTable* table)
       
   153 	{
       
   154 		CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE];
       
   155 		CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE];
       
   156 		CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE];
       
   157 		float percent;
       
   158 		int j;
       
   159 
       
   160 		memset(redTable, 0, sizeof(redTable));
       
   161 		memset(greenTable, 0, sizeof(greenTable));
       
   162 		memset(blueTable, 0, sizeof(greenTable));
       
   163 
       
   164 		for (percent = 0.0; percent <= 1.0; percent += 0.01) {
       
   165 			for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
       
   166 				redTable[j]   = table->red[j]   * percent;
       
   167 				greenTable[j] = table->green[j] * percent;
       
   168 				blueTable[j]  = table->blue[j]  * percent;
       
   169 			}
       
   170 
       
   171 			if (CGSetDisplayTransferByTable(
       
   172 						display_id, QZ_GAMMA_TABLE_SIZE,
       
   173 						redTable, greenTable, blueTable
       
   174 					) != CGDisplayNoErr) {
       
   175 				CGDisplayRestoreColorSyncSettings();
       
   176 				return 1;
       
   177 			}
       
   178 
       
   179 			CSleep(10);
       
   180 		}
       
   181 
       
   182 		return 0;
       
   183 	}
       
   184 
       
   185 	/* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
       
   186 	void WaitForVerticalBlank()
       
   187 	{
       
   188 		/* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
       
   189 		double refreshRate;
       
   190 		double linesPerSecond;
       
   191 		double target;
       
   192 		double position;
       
   193 		double adjustment;
       
   194 		CFNumberRef refreshRateCFNumber;
       
   195 
       
   196 		refreshRateCFNumber = (const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayRefreshRate);
       
   197 		if (refreshRateCFNumber == NULL) return;
       
   198 
       
   199 		if (CFNumberGetValue(refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) == 0)
       
   200 			return;
       
   201 
       
   202 		if (refreshRate == 0) return;
       
   203 
       
   204 		linesPerSecond = refreshRate * display_height;
       
   205 		target = display_height;
       
   206 
       
   207 		/* Figure out the first delay so we start off about right */
       
   208 		position = CGDisplayBeamPosition(display_id);
       
   209 		if (position > target) position = 0;
       
   210 
       
   211 		adjustment = (target - position) / linesPerSecond;
       
   212 
       
   213 		CSleep((uint32)(adjustment * 1000));
       
   214 	}
       
   215 
       
   216 
       
   217 	bool SetVideoMode(int w, int h)
       
   218 	{
       
   219 		int exact_match;
       
   220 		CFNumberRef number;
       
   221 		int bpp;
       
   222 		int gamma_error;
       
   223 		OTTD_QuartzGammaTable gamma_table;
       
   224 		NSRect screen_rect;
       
   225 		CGError error;
       
   226 		NSPoint pt;
       
   227 
       
   228 		/* Destroy any previous mode */
       
   229 		if (pixel_buffer != NULL) {
       
   230 			free(pixel_buffer);
       
   231 			pixel_buffer = NULL;
       
   232 		}
       
   233 
       
   234 		/* See if requested mode exists */
       
   235 		cur_mode = CGDisplayBestModeForParameters(display_id, display_depth, w, h, &exact_match);
       
   236 
       
   237 		/* If the mode wasn't an exact match, check if it has the right bpp, and update width and height */
       
   238 		if (!exact_match) {
       
   239 			number = (const __CFNumber*) CFDictionaryGetValue(cur_mode, kCGDisplayBitsPerPixel);
       
   240 			CFNumberGetValue(number, kCFNumberSInt32Type, &bpp);
       
   241 			if (bpp != display_depth) {
       
   242 				DEBUG(driver, 0, "Failed to find display resolution");
       
   243 				goto ERR_NO_MATCH;
       
   244 			}
       
   245 
       
   246 			number = (const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayWidth);
       
   247 			CFNumberGetValue(number, kCFNumberSInt32Type, &w);
       
   248 
       
   249 			number = (const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayHeight);
       
   250 			CFNumberGetValue(number, kCFNumberSInt32Type, &h);
       
   251 		}
       
   252 
       
   253 		/* Fade display to zero gamma */
       
   254 		gamma_error = FadeGammaOut(&gamma_table);
       
   255 
       
   256 		/* Put up the blanking window (a window above all other windows) */
       
   257 		error = CGDisplayCapture(display_id);
       
   258 
       
   259 		if (CGDisplayNoErr != error) {
       
   260 			DEBUG(driver, 0, "Failed capturing display");
       
   261 			goto ERR_NO_CAPTURE;
       
   262 		}
       
   263 
       
   264 		/* Do the physical switch */
       
   265 		if (CGDisplaySwitchToMode(display_id, cur_mode) != CGDisplayNoErr) {
       
   266 			DEBUG(driver, 0, "Failed switching display resolution");
       
   267 			goto ERR_NO_SWITCH;
       
   268 		}
       
   269 
       
   270 		screen_buffer = CGDisplayBaseAddress(display_id);
       
   271 		screen_pitch  = CGDisplayBytesPerRow(display_id);
       
   272 
       
   273 		display_width = CGDisplayPixelsWide(display_id);
       
   274 		display_height = CGDisplayPixelsHigh(display_id);
       
   275 
       
   276 		/* Setup double-buffer emulation */
       
   277 		pixel_buffer = malloc(display_width * display_height * display_depth / 8);
       
   278 		if (pixel_buffer == NULL) {
       
   279 			DEBUG(driver, 0, "Failed to allocate memory for double buffering");
       
   280 			goto ERR_DOUBLEBUF;
       
   281 		}
       
   282 
       
   283 		if (display_depth == 8 && !CGDisplayCanSetPalette(display_id)) {
       
   284 			DEBUG(driver, 0, "Not an indexed display mode.");
       
   285 			goto ERR_NOT_INDEXED;
       
   286 		}
       
   287 
       
   288 		/* If we don't hide menu bar, it will get events and interrupt the program */
       
   289 		HideMenuBar();
       
   290 
       
   291 		/* Fade the display to original gamma */
       
   292 		if (!gamma_error) FadeGammaIn(&gamma_table);
       
   293 
       
   294 		/* There is a bug in Cocoa where NSScreen doesn't synchronize
       
   295 		 * with CGDirectDisplay, so the main screen's frame is wrong.
       
   296 		 * As a result, coordinate translation produces incorrect results.
       
   297 		 * We can hack around this bug by setting the screen rect ourselves.
       
   298 		 * This hack should be removed if/when the bug is fixed.
       
   299 		*/
       
   300 		screen_rect = NSMakeRect(0, 0, display_width, display_height);
       
   301 		[ [ NSScreen mainScreen ] setFrame:screen_rect ];
       
   302 
       
   303 
       
   304 		pt = [ NSEvent mouseLocation ];
       
   305 		pt.y = display_height - pt.y;
       
   306 		if (MouseIsInsideView(&pt)) QZ_HideMouse();
       
   307 
       
   308 		UpdatePalette(0, 256);
       
   309 
       
   310 		return true;
       
   311 
       
   312 		/* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
       
   313 ERR_NOT_INDEXED:
       
   314 		free(pixel_buffer);
       
   315 		pixel_buffer = NULL;
       
   316 ERR_DOUBLEBUF:
       
   317 		CGDisplaySwitchToMode(display_id, save_mode);
       
   318 ERR_NO_SWITCH:
       
   319 		CGReleaseAllDisplays();
       
   320 ERR_NO_CAPTURE:
       
   321 		if (!gamma_error) FadeGammaIn(&gamma_table);
       
   322 ERR_NO_MATCH:
       
   323 		display_width = 0;
       
   324 		display_height = 0;
       
   325 
       
   326 		return false;
       
   327 	}
       
   328 
       
   329 	void RestoreVideoMode()
       
   330 	{
       
   331 		/* Release fullscreen resources */
       
   332 		OTTD_QuartzGammaTable gamma_table;
       
   333 		int gamma_error;
       
   334 		NSRect screen_rect;
       
   335 
       
   336 		gamma_error = FadeGammaOut(&gamma_table);
       
   337 
       
   338 		/* Restore original screen resolution/bpp */
       
   339 		CGDisplaySwitchToMode(display_id, save_mode);
       
   340 		CGReleaseAllDisplays();
       
   341 		ShowMenuBar();
       
   342 		/* Reset the main screen's rectangle
       
   343 		 * See comment in SetVideoMode for why we do this
       
   344 		 */
       
   345 		screen_rect = NSMakeRect(0, 0, CGDisplayPixelsWide(display_id), CGDisplayPixelsHigh(display_id));
       
   346 		[ [ NSScreen mainScreen ] setFrame:screen_rect ];
       
   347 
       
   348 		QZ_ShowMouse();
       
   349 
       
   350 		/* Destroy the pixel buffer */
       
   351 		if (pixel_buffer != NULL) {
       
   352 			free(pixel_buffer);
       
   353 			pixel_buffer = NULL;
       
   354 		}
       
   355 
       
   356 		if (!gamma_error) FadeGammaIn(&gamma_table);
       
   357 
       
   358 		display_width = 0;
       
   359 		display_height = 0;
       
   360 	}
       
   361 
       
   362 public:
       
   363 	FullscreenSubdriver(int bpp)
       
   364 	{
       
   365 		if (bpp != 8 && bpp != 32) {
       
   366 			error("Cocoa: This video driver only supports 8 and 32 bpp blitters.");
       
   367 		}
       
   368 
       
   369 		/* Initialize the video settings; this data persists between mode switches */
       
   370 		display_id = kCGDirectMainDisplay;
       
   371 		save_mode  = CGDisplayCurrentMode(display_id);
       
   372 
       
   373 		if (bpp == 8) palette = CGPaletteCreateDefaultColorPalette();
       
   374 
       
   375 		display_width  = 0;
       
   376 		display_height = 0;
       
   377 		display_depth  = bpp;
       
   378 		pixel_buffer   = NULL;
       
   379 
       
   380 		num_dirty_rects = MAX_DIRTY_RECTS;
       
   381 	}
       
   382 
       
   383 	virtual ~FullscreenSubdriver()
       
   384 	{
       
   385 		RestoreVideoMode();
       
   386 	}
       
   387 
       
   388 	virtual void Draw()
       
   389 	{
       
   390 		const uint8* src   = (uint8*) pixel_buffer;
       
   391 		uint8* dst         = (uint8*) screen_buffer;
       
   392 		uint pitch         = screen_pitch;
       
   393 		uint width         = display_width;
       
   394 		uint num_dirty     = num_dirty_rects;
       
   395 		uint bytesperpixel = display_depth / 8;
       
   396 		uint i;
       
   397 
       
   398 		/* Check if we need to do anything */
       
   399 		if (num_dirty == 0) return;
       
   400 
       
   401 		if (num_dirty >= MAX_DIRTY_RECTS) {
       
   402 			num_dirty = 1;
       
   403 			dirty_rects[0].left   = 0;
       
   404 			dirty_rects[0].top    = 0;
       
   405 			dirty_rects[0].right  = display_width;
       
   406 			dirty_rects[0].bottom = display_height;
       
   407 		}
       
   408 
       
   409 		WaitForVerticalBlank();
       
   410 		/* Build the region of dirty rectangles */
       
   411 		for (i = 0; i < num_dirty; i++) {
       
   412 			uint y      = dirty_rects[i].top;
       
   413 			uint left   = dirty_rects[i].left;
       
   414 			uint length = dirty_rects[i].right - left;
       
   415 			uint bottom = dirty_rects[i].bottom;
       
   416 
       
   417 			for (; y < bottom; y++) {
       
   418 				memcpy(dst + y * pitch + left * bytesperpixel, src + y * width * bytesperpixel + left * bytesperpixel, length * bytesperpixel);
       
   419 			}
       
   420 		}
       
   421 
       
   422 		num_dirty_rects = 0;
       
   423 	}
       
   424 
       
   425 	virtual void MakeDirty(int left, int top, int width, int height)
       
   426 	{
       
   427 		if (num_dirty_rects < MAX_DIRTY_RECTS) {
       
   428 			dirty_rects[num_dirty_rects].left = left;
       
   429 			dirty_rects[num_dirty_rects].top = top;
       
   430 			dirty_rects[num_dirty_rects].right = left + width;
       
   431 			dirty_rects[num_dirty_rects].bottom = top + height;
       
   432 		}
       
   433 		num_dirty_rects++;
       
   434 	}
       
   435 
       
   436 	virtual void UpdatePalette(uint first_color, uint num_colors)
       
   437 	{
       
   438 		CGTableCount  index;
       
   439 		CGDeviceColor color;
       
   440 
       
   441 		if (display_depth != 8)
       
   442 			return;
       
   443 
       
   444 		for (index = first_color; index < first_color+num_colors; index++) {
       
   445 			/* Clamp colors between 0.0 and 1.0 */
       
   446 			color.red   = _cur_palette[index].r / 255.0;
       
   447 			color.blue  = _cur_palette[index].b / 255.0;
       
   448 			color.green = _cur_palette[index].g / 255.0;
       
   449 
       
   450 			CGPaletteSetColorAtIndex(palette, color, index);
       
   451 		}
       
   452 
       
   453 		CGDisplaySetPalette(display_id, palette);
       
   454 	}
       
   455 
       
   456 	virtual uint ListModes(OTTDPoint* modes, uint max_modes)
       
   457 	{
       
   458 		CFArrayRef mode_list;
       
   459 		CFIndex num_modes;
       
   460 		CFIndex i;
       
   461 		uint count = 0;
       
   462 
       
   463 		mode_list  = CGDisplayAvailableModes(display_id);
       
   464 		num_modes = CFArrayGetCount(mode_list);
       
   465 
       
   466 		/* Build list of modes with the requested bpp */
       
   467 		for (i = 0; i < num_modes && count < max_modes; i++) {
       
   468 			CFDictionaryRef onemode;
       
   469 			CFNumberRef     number;
       
   470 			int bpp;
       
   471 			int intvalue;
       
   472 			bool hasMode;
       
   473 			uint16 width, height;
       
   474 
       
   475 			onemode = (const __CFDictionary*)CFArrayGetValueAtIndex(mode_list, i);
       
   476 			number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayBitsPerPixel);
       
   477 			CFNumberGetValue (number, kCFNumberSInt32Type, &bpp);
       
   478 
       
   479 			if (bpp != display_depth) continue;
       
   480 
       
   481 			number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayWidth);
       
   482 			CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue);
       
   483 			width = (uint16)intvalue;
       
   484 
       
   485 			number = (const __CFNumber*)CFDictionaryGetValue(onemode, kCGDisplayHeight);
       
   486 			CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue);
       
   487 			height = (uint16)intvalue;
       
   488 
       
   489 			/* Check if mode is already in the list */
       
   490 			{
       
   491 				uint i;
       
   492 				hasMode = false;
       
   493 				for (i = 0; i < count; i++) {
       
   494 					if (modes[i].x == width &&  modes[i].y == height) {
       
   495 						hasMode = true;
       
   496 						break;
       
   497 					}
       
   498 				}
       
   499 			}
       
   500 
       
   501 			if (hasMode) continue;
       
   502 
       
   503 			/* Add mode to the list */
       
   504 			modes[count].x = width;
       
   505 			modes[count].y = height;
       
   506 			count++;
       
   507 		}
       
   508 
       
   509 		/* Sort list smallest to largest */
       
   510 		{
       
   511 			uint i, j;
       
   512 			for (i = 0; i < count; i++) {
       
   513 				for (j = 0; j < count-1; j++) {
       
   514 					if (modes[j].x > modes[j + 1].x || (
       
   515 						modes[j].x == modes[j + 1].x &&
       
   516 						modes[j].y >  modes[j + 1].y
       
   517 						)) {
       
   518 						uint tmpw = modes[j].x;
       
   519 						uint tmph = modes[j].y;
       
   520 
       
   521 						modes[j].x = modes[j + 1].x;
       
   522 						modes[j].y = modes[j + 1].y;
       
   523 
       
   524 						modes[j + 1].x = tmpw;
       
   525 						modes[j + 1].y = tmph;
       
   526 					}
       
   527 				}
       
   528 			}
       
   529 		}
       
   530 
       
   531 		return count;
       
   532 	}
       
   533 
       
   534 	virtual bool ChangeResolution(int w, int h)
       
   535 	{
       
   536 		int old_width  = display_width;
       
   537 		int old_height = display_height;
       
   538 
       
   539 		if (SetVideoMode(w, h))
       
   540 			return true;
       
   541 
       
   542 		if (old_width != 0 && old_height != 0)
       
   543 			SetVideoMode(old_width, old_height);
       
   544 
       
   545 		return false;
       
   546 	}
       
   547 
       
   548 	virtual bool IsFullscreen()
       
   549 	{
       
   550 		return true;
       
   551 	}
       
   552 
       
   553 	virtual int GetWidth()
       
   554 	{
       
   555 		return display_width;
       
   556 	}
       
   557 
       
   558 	virtual int GetHeight()
       
   559 	{
       
   560 		return display_height;
       
   561 	}
       
   562 
       
   563 	virtual void *GetPixelBuffer()
       
   564 	{
       
   565 		return pixel_buffer;
       
   566 	}
       
   567 
       
   568 	/*
       
   569 		Convert local coordinate to window server (CoreGraphics) coordinate.
       
   570 		In fullscreen mode this just means copying the coords.
       
   571 	*/
       
   572 	virtual CGPoint PrivateLocalToCG(NSPoint* p)
       
   573 	{
       
   574 		CGPoint cgp;
       
   575 
       
   576 		cgp.x = p->x;
       
   577 		cgp.y = p->y;
       
   578 
       
   579 		return cgp;
       
   580 	}
       
   581 
       
   582 	virtual NSPoint GetMouseLocation(NSEvent *event)
       
   583 	{
       
   584 		NSPoint pt;
       
   585 
       
   586 		pt = [ NSEvent mouseLocation ];
       
   587 		pt.y = display_height - pt.y;
       
   588 
       
   589 		return pt;
       
   590 	}
       
   591 
       
   592 	virtual bool MouseIsInsideView(NSPoint *pt)
       
   593 	{
       
   594 		return pt->x >= 0 && pt->y >= 0 && pt->x < display_width && pt->y < display_height;
       
   595 	}
       
   596 
       
   597 	virtual bool IsActive()
       
   598 	{
       
   599 		return true;
       
   600 	}
       
   601 };
       
   602 
       
   603 CocoaSubdriver *QZ_CreateFullscreenSubdriver(int width, int height, int bpp)
       
   604 {
       
   605 	FullscreenSubdriver *ret;
       
   606 
       
   607 	ret = new FullscreenSubdriver(bpp);
       
   608 
       
   609 	if (!ret->ChangeResolution(width, height)) {
       
   610 		delete ret;
       
   611 		return NULL;
       
   612 	}
       
   613 
       
   614 	return ret;
       
   615 }
       
   616 
       
   617 #endif /* WITH_COCOA */