src/video/cocoa/wnd_quartz.mm
changeset 7943 8517bbaacb7e
child 7946 c9f6b66454a4
equal deleted inserted replaced
7942:39a55d50eb11 7943:8517bbaacb7e
       
     1 /* $Id$ */
       
     2 
       
     3 /******************************************************************************
       
     4  *                             Cocoa video driver                             *
       
     5  * Known things left to do:                                                   *
       
     6  *  List available resolutions.                                               *
       
     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 /* Defined in stdbool.h */
       
    28 #ifndef __cplusplus
       
    29 # ifndef __BEOS__
       
    30 #  undef bool
       
    31 #  undef false
       
    32 #  undef true
       
    33 # endif
       
    34 #endif
       
    35 
       
    36 
       
    37 #include "../../stdafx.h"
       
    38 #include "../../openttd.h"
       
    39 #include "../../debug.h"
       
    40 #include "../../macros.h"
       
    41 #include "../../os/macosx/splash.h"
       
    42 #include "../../variables.h"
       
    43 #include "../../gfx.h"
       
    44 #include "cocoa_v.h"
       
    45 #include "cocoa_keys.h"
       
    46 #include "../../blitter/factory.hpp"
       
    47 #include "../../fileio.h"
       
    48 
       
    49 #undef Point
       
    50 #undef Rect
       
    51 
       
    52 
       
    53 class WindowQuartzSubdriver;
       
    54 
       
    55 
       
    56 /* Subclass of NSWindow to fix genie effect and support resize events  */
       
    57 @interface OTTD_QuartzWindow : NSWindow {
       
    58 	WindowQuartzSubdriver *driver;
       
    59 }
       
    60 
       
    61 - (void)setDriver:(WindowQuartzSubdriver*)drv;
       
    62 
       
    63 - (void)miniaturize:(id)sender;
       
    64 - (void)display;
       
    65 - (void)setFrame:(NSRect)frameRect display:(BOOL)flag;
       
    66 - (void)appDidHide:(NSNotification*)note;
       
    67 - (void)appWillUnhide:(NSNotification*)note;
       
    68 - (void)appDidUnhide:(NSNotification*)note;
       
    69 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag;
       
    70 @end
       
    71 
       
    72 /* Delegate for our NSWindow to send ask for quit on close */
       
    73 @interface OTTD_QuartzWindowDelegate : NSObject{
       
    74 	WindowQuartzSubdriver *driver;
       
    75 }
       
    76 
       
    77 - (void)setDriver:(WindowQuartzSubdriver*)drv;
       
    78 
       
    79 - (BOOL)windowShouldClose:(id)sender;
       
    80 @end
       
    81 
       
    82 /* Subclass of NSView to fix Quartz rendering */
       
    83 @interface OTTD_QuartzView : NSView {
       
    84 	WindowQuartzSubdriver *driver;
       
    85 }
       
    86 
       
    87 - (void)setDriver:(WindowQuartzSubdriver*)drv;
       
    88 
       
    89 - (void)drawRect:(NSRect)rect;
       
    90 - (BOOL)isOpaque;
       
    91 @end
       
    92 
       
    93 class WindowQuartzSubdriver: public CocoaSubdriver {
       
    94 	int device_width;
       
    95 	int device_height;
       
    96 
       
    97 	int window_width;
       
    98 	int window_height;
       
    99 
       
   100 	int buffer_depth;
       
   101 
       
   102 	void* pixel_buffer;
       
   103 	void* image_buffer;
       
   104 
       
   105 	OTTD_QuartzWindow *window;
       
   106 
       
   107 	#define MAX_DIRTY_RECTS 100
       
   108 	Rect dirty_rects[MAX_DIRTY_RECTS];
       
   109 	int num_dirty_rects;
       
   110 
       
   111 	uint32 palette[256];
       
   112 
       
   113 public:
       
   114 	bool active;
       
   115 	bool setup;
       
   116 
       
   117 	OTTD_QuartzView* qzview;
       
   118 	CGContextRef 	 cgcontext;
       
   119 
       
   120 private:
       
   121 	void GetDeviceInfo();
       
   122 
       
   123 	bool SetVideoMode(int width, int height);
       
   124 
       
   125 	/**
       
   126 	 * This function copies 8bpp pixels from the screen buffer in 32bpp windowed mode.
       
   127 	 *
       
   128 	 * @param left The x coord for the left edge of the box to blit.
       
   129 	 * @param top The y coord for the top edge of the box to blit.
       
   130 	 * @param right The x coord for the right edge of the box to blit.
       
   131 	 * @param bottom The y coord for the bottom edge of the box to blit.
       
   132 	 */
       
   133 	void BlitIndexedToView32(int left, int top, int right, int bottom);
       
   134 
       
   135 public:
       
   136 	WindowQuartzSubdriver(int bpp);
       
   137 	virtual ~WindowQuartzSubdriver();
       
   138 
       
   139 	virtual void Draw();
       
   140 	virtual void MakeDirty(int left, int top, int width, int height);
       
   141 	virtual void UpdatePalette(uint first_color, uint num_colors);
       
   142 
       
   143 	virtual uint ListModes(OTTDPoint* modes, uint max_modes);
       
   144 
       
   145 	virtual bool ChangeResolution(int w, int h);
       
   146 
       
   147 	virtual bool IsFullscreen() { return false; }
       
   148 
       
   149 	virtual int GetWidth() { return window_width; }
       
   150 	virtual int GetHeight() { return window_height; }
       
   151 	virtual void *GetPixelBuffer() { return buffer_depth == 8 ? pixel_buffer : image_buffer; }
       
   152 
       
   153 	/* Convert local coordinate to window server (CoreGraphics) coordinate */
       
   154 	virtual CGPoint PrivateLocalToCG(NSPoint* p);
       
   155 
       
   156 	virtual NSPoint GetMouseLocation(NSEvent *event);
       
   157 	virtual bool MouseIsInsideView(NSPoint *pt);
       
   158 
       
   159 	virtual bool IsActive() { return active; }
       
   160 
       
   161 
       
   162 	void SetPortAlphaOpaque();
       
   163 	bool WindowResized();
       
   164 };
       
   165 
       
   166 
       
   167 static CGColorSpaceRef QZ_GetCorrectColorSpace()
       
   168 {
       
   169 	static CGColorSpaceRef colorSpace = NULL;
       
   170 
       
   171 	if (colorSpace == NULL)
       
   172 	{
       
   173 		CMProfileRef sysProfile;
       
   174 
       
   175 		if (CMGetSystemProfile(&sysProfile) == noErr)
       
   176 		{
       
   177 			colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysProfile);
       
   178 			CMCloseProfile(sysProfile);
       
   179 		}
       
   180 
       
   181 		assert(colorSpace != NULL);
       
   182 	}
       
   183 
       
   184 	return colorSpace;
       
   185 }
       
   186 
       
   187 
       
   188 @implementation OTTD_QuartzWindow
       
   189 
       
   190 - (void)setDriver:(WindowQuartzSubdriver*)drv
       
   191 {
       
   192 	driver = drv;
       
   193 }
       
   194 
       
   195 
       
   196 /* we override these methods to fix the miniaturize animation/dock icon bug */
       
   197 - (void)miniaturize:(id)sender
       
   198 {
       
   199 	/* make the alpha channel opaque so anim won't have holes in it */
       
   200 	driver->SetPortAlphaOpaque ();
       
   201 
       
   202 	/* window is hidden now */
       
   203 	driver->active = false;
       
   204 
       
   205 	QZ_ShowMouse();
       
   206 
       
   207 	[ super miniaturize:sender ];
       
   208 }
       
   209 
       
   210 - (void)display
       
   211 {
       
   212 	/* This method fires just before the window deminaturizes from the Dock.
       
   213 	 * We'll save the current visible surface, let the window manager redraw any
       
   214 	 * UI elements, and restore the surface. This way, no expose event
       
   215 	 * is required, and the deminiaturize works perfectly.
       
   216 	 */
       
   217 
       
   218 	driver->SetPortAlphaOpaque();
       
   219 
       
   220 	/* save current visible surface */
       
   221 	[ self cacheImageInRect:[ driver->qzview frame ] ];
       
   222 
       
   223 	/* let the window manager redraw controls, border, etc */
       
   224 	[ super display ];
       
   225 
       
   226 	/* restore visible surface */
       
   227 	[ self restoreCachedImage ];
       
   228 
       
   229 	/* window is visible again */
       
   230 	driver->active = true;
       
   231 }
       
   232 
       
   233 - (void)setFrame:(NSRect)frameRect display:(BOOL)flag
       
   234 {
       
   235 	[ super setFrame:frameRect display:flag ];
       
   236 
       
   237 	/* Don't do anything if the window is currently being created */
       
   238 	if (driver->setup) return;
       
   239 
       
   240 	if (!driver->WindowResized())
       
   241 		error("Cocoa: Failed to resize window.");
       
   242 }
       
   243 
       
   244 - (void)appDidHide:(NSNotification*)note
       
   245 {
       
   246 	driver->active = false;
       
   247 }
       
   248 
       
   249 
       
   250 - (void)appWillUnhide:(NSNotification*)note
       
   251 {
       
   252 	driver->SetPortAlphaOpaque ();
       
   253 
       
   254 	/* save current visible surface */
       
   255 	[ self cacheImageInRect:[ driver->qzview frame ] ];
       
   256 }
       
   257 
       
   258 - (void)appDidUnhide:(NSNotification*)note
       
   259 {
       
   260 	/* restore cached image, since it may not be current, post expose event too */
       
   261 	[ self restoreCachedImage ];
       
   262 
       
   263 	driver->active = true;
       
   264 }
       
   265 
       
   266 
       
   267 - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag
       
   268 {
       
   269 	/* Make our window subclass receive these application notifications */
       
   270 	[ [ NSNotificationCenter defaultCenter ] addObserver:self
       
   271 	selector:@selector(appDidHide:) name:NSApplicationDidHideNotification object:NSApp ];
       
   272 
       
   273 	[ [ NSNotificationCenter defaultCenter ] addObserver:self
       
   274 	selector:@selector(appDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp ];
       
   275 
       
   276 	[ [ NSNotificationCenter defaultCenter ] addObserver:self
       
   277 	selector:@selector(appWillUnhide:) name:NSApplicationWillUnhideNotification object:NSApp ];
       
   278 
       
   279 	return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ];
       
   280 }
       
   281 
       
   282 @end
       
   283 
       
   284 @implementation OTTD_QuartzWindowDelegate
       
   285 
       
   286 - (void)setDriver:(WindowQuartzSubdriver*)drv
       
   287 {
       
   288 	driver = drv;
       
   289 }
       
   290 
       
   291 - (BOOL)windowShouldClose:(id)sender
       
   292 {
       
   293 	HandleExitGameRequest();
       
   294 
       
   295 	return NO;
       
   296 }
       
   297 
       
   298 - (void)windowDidBecomeKey:(NSNotification*)aNotification
       
   299 {
       
   300 	driver->active = true;
       
   301 }
       
   302 
       
   303 - (void)windowDidResignKey:(NSNotification*)aNotification
       
   304 {
       
   305 	driver->active = false;
       
   306 }
       
   307 
       
   308 - (void)windowDidBecomeMain:(NSNotification*)aNotification
       
   309 {
       
   310 	driver->active = true;
       
   311 }
       
   312 
       
   313 - (void)windowDidResignMain:(NSNotification*)aNotification
       
   314 {
       
   315 	driver->active = false;
       
   316 }
       
   317 
       
   318 @end
       
   319 
       
   320 @implementation OTTD_QuartzView
       
   321 
       
   322 - (void)setDriver:(WindowQuartzSubdriver*)drv
       
   323 {
       
   324 	driver = drv;
       
   325 }
       
   326 
       
   327 
       
   328 - (BOOL)isOpaque
       
   329 {
       
   330 	return YES;
       
   331 }
       
   332 
       
   333 - (void)drawRect:(NSRect)invalidRect
       
   334 {
       
   335 	CGImageRef    fullImage;
       
   336 	CGImageRef    clippedImage;
       
   337 	NSRect        rect;
       
   338 	const NSRect* dirtyRects;
       
   339 	int           dirtyRectCount;
       
   340 	int           n;
       
   341 	CGRect        clipRect;
       
   342 	CGRect        blitRect;
       
   343 	uint32        blitArea       = 0;
       
   344 	NSRect        frameRect      = [ self frame ];
       
   345 	CGContextRef  viewContext    = (CGContextRef)[ [ NSGraphicsContext currentContext ] graphicsPort ];
       
   346 
       
   347 	if (driver->cgcontext == NULL) return;
       
   348 
       
   349 	CGContextSetShouldAntialias(viewContext, FALSE);
       
   350 	CGContextSetInterpolationQuality(viewContext, kCGInterpolationNone);
       
   351 
       
   352 	/* The obtained 'rect' is actually a union of all dirty rects, let's ask for an explicit list of rects instead */
       
   353 	[ self getRectsBeingDrawn:&dirtyRects count:&dirtyRectCount ];
       
   354 
       
   355 	/* We need an Image in order to do blitting, but as we don't touch the context between this call and drawing no copying will actually be done here */
       
   356 	fullImage = CGBitmapContextCreateImage(driver->cgcontext);
       
   357 
       
   358 	/* Calculate total area we are blitting */
       
   359 	for (n = 0; n < dirtyRectCount; n++) {
       
   360 		blitArea += dirtyRects[n].size.width * dirtyRects[n].size.height;
       
   361  	}
       
   362 
       
   363 	/*
       
   364 	 * This might be completely stupid, but in my extremely subjective opinion it feels faster
       
   365 	 * The point is, if we're blitting less than 50% of the dirty rect union then it's still a good idea to blit each dirty
       
   366 	 * rect separately but if we blit more than that, it's just cheaper to blit the entire union in one pass.
       
   367 	 * Feel free to remove or find an even better value than 50% ... / blackis
       
   368 	 */
       
   369 	if (blitArea / (float)(invalidRect.size.width * invalidRect.size.height) > 0.5f) {
       
   370 		rect = invalidRect;
       
   371 
       
   372 		blitRect.origin.x = rect.origin.x;
       
   373 		blitRect.origin.y = rect.origin.y;
       
   374 		blitRect.size.width = rect.size.width;
       
   375 		blitRect.size.height = rect.size.height;
       
   376 
       
   377 		clipRect.origin.x = rect.origin.x;
       
   378 		clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height;
       
   379 
       
   380 		clipRect.size.width = rect.size.width;
       
   381 		clipRect.size.height = rect.size.height;
       
   382 
       
   383 		/* Blit dirty part of image */
       
   384 		clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect);
       
   385 		CGContextDrawImage(viewContext, blitRect, clippedImage);
       
   386 		CGImageRelease(clippedImage);
       
   387 	} else {
       
   388 		for (n = 0; n < dirtyRectCount; n++) {
       
   389 			rect = dirtyRects[n];
       
   390 
       
   391 			blitRect.origin.x = rect.origin.x;
       
   392 			blitRect.origin.y = rect.origin.y;
       
   393 			blitRect.size.width = rect.size.width;
       
   394 			blitRect.size.height = rect.size.height;
       
   395 
       
   396 			clipRect.origin.x = rect.origin.x;
       
   397 			clipRect.origin.y = frameRect.size.height - rect.origin.y - rect.size.height;
       
   398 
       
   399 			clipRect.size.width = rect.size.width;
       
   400 			clipRect.size.height = rect.size.height;
       
   401 
       
   402 			/* Blit dirty part of image */
       
   403 			clippedImage = CGImageCreateWithImageInRect(fullImage, clipRect);
       
   404 			CGContextDrawImage(viewContext, blitRect, clippedImage);
       
   405 			CGImageRelease(clippedImage);
       
   406  		}
       
   407  	}
       
   408 
       
   409 	CGImageRelease(fullImage);
       
   410 }
       
   411 
       
   412 @end
       
   413 
       
   414 
       
   415 extern const char _openttd_revision[];
       
   416 
       
   417 
       
   418 void WindowQuartzSubdriver::GetDeviceInfo()
       
   419 {
       
   420 	CFDictionaryRef    cur_mode;
       
   421 
       
   422 	/* Initialize the video settings; this data persists between mode switches */
       
   423 	cur_mode = CGDisplayCurrentMode(kCGDirectMainDisplay);
       
   424 
       
   425 	/* Gather some information that is useful to know about the display */
       
   426 	CFNumberGetValue(
       
   427 		(const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayWidth),
       
   428 		kCFNumberSInt32Type, &device_width
       
   429 	);
       
   430 
       
   431 	CFNumberGetValue(
       
   432 		(const __CFNumber*)CFDictionaryGetValue(cur_mode, kCGDisplayHeight),
       
   433 		kCFNumberSInt32Type, &device_height
       
   434 	);
       
   435 }
       
   436 
       
   437 bool WindowQuartzSubdriver::SetVideoMode(int width, int height)
       
   438 {
       
   439 	char caption[50];
       
   440 	NSString *nsscaption;
       
   441 	unsigned int style;
       
   442 	NSRect contentRect;
       
   443 	BOOL isCustom = NO;
       
   444 	bool ret;
       
   445 
       
   446 	setup = true;
       
   447 
       
   448 	GetDeviceInfo();
       
   449 
       
   450 	if (width > device_width)
       
   451 		width = device_width;
       
   452 	if (height > device_height)
       
   453 		height = device_height;
       
   454 
       
   455 	contentRect = NSMakeRect(0, 0, width, height);
       
   456 
       
   457 	/* Check if we should recreate the window */
       
   458 	if (window == nil) {
       
   459 		OTTD_QuartzWindowDelegate *delegate;
       
   460 
       
   461 		/* Set the window style */
       
   462 		style = NSTitledWindowMask;
       
   463 		style |= (NSMiniaturizableWindowMask | NSClosableWindowMask);
       
   464 		style |= NSResizableWindowMask;
       
   465 
       
   466 		/* Manually create a window, avoids having a nib file resource */
       
   467 		window = [ [ OTTD_QuartzWindow alloc ]
       
   468 						initWithContentRect: contentRect
       
   469 						styleMask: style
       
   470 						backing: NSBackingStoreBuffered
       
   471 						defer: NO ];
       
   472 
       
   473 		if (window == nil) {
       
   474 			DEBUG(driver, 0, "Could not create the Cocoa window.");
       
   475 			setup = false;
       
   476 			return false;
       
   477 		}
       
   478 
       
   479 		[ window setDriver:this ];
       
   480 
       
   481 		snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision);
       
   482 		nsscaption = [ [ NSString alloc ] initWithCString:caption ];
       
   483 		[ window setTitle: nsscaption ];
       
   484 		[ window setMiniwindowTitle: nsscaption ];
       
   485 		[ nsscaption release ];
       
   486 
       
   487 		[ window setAcceptsMouseMovedEvents: YES ];
       
   488 		[ window setViewsNeedDisplay: NO ];
       
   489 
       
   490 		[ window useOptimizedDrawing: YES ];
       
   491 
       
   492 		delegate = [ [ OTTD_QuartzWindowDelegate alloc ] init ];
       
   493 		[ delegate setDriver: this ];
       
   494 		[ window setDelegate: [ delegate autorelease ] ];
       
   495 	} else {
       
   496 		/* We already have a window, just change its size */
       
   497 		if (!isCustom) {
       
   498 			[ window setContentSize: contentRect.size ];
       
   499 
       
   500 			// Ensure frame height - title bar height >= view height
       
   501 			contentRect.size.height = Clamp(height, 0, [ window frame ].size.height - 22 /* 22 is the height of title bar of window*/);
       
   502 
       
   503 			if (qzview != nil) {
       
   504 				height = contentRect.size.height;
       
   505 				[ qzview setFrameSize: contentRect.size ];
       
   506 			}
       
   507 		}
       
   508 	}
       
   509 
       
   510 	window_width = width;
       
   511 	window_height = height;
       
   512 
       
   513 	[ window center ];
       
   514 
       
   515 	/* Only recreate the view if it doesn't already exist */
       
   516 	if (qzview == nil) {
       
   517 		qzview = [ [ OTTD_QuartzView alloc ] initWithFrame: contentRect ];
       
   518 		if (qzview == nil) {
       
   519 			DEBUG(driver, 0, "Could not create the Quickdraw view.");
       
   520 			setup = false;
       
   521 			return false;
       
   522 		}
       
   523 
       
   524 		[ qzview setDriver: this ];
       
   525 
       
   526 		[ qzview setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ];
       
   527 		[ window setContentView: qzview ];
       
   528 		[ qzview release ];
       
   529 		[ window makeKeyAndOrderFront:nil ];
       
   530 	}
       
   531 
       
   532 	ret = WindowResized();
       
   533 
       
   534 	UpdatePalette(0, 256);
       
   535 
       
   536 	setup = false;
       
   537 
       
   538 	return ret;
       
   539 }
       
   540 
       
   541 void WindowQuartzSubdriver::BlitIndexedToView32(int left, int top, int right, int bottom)
       
   542 {
       
   543 	const uint32* pal   = palette;
       
   544 	const uint8*  src   = (uint8*)pixel_buffer;
       
   545 	uint32*       dst   = (uint32*)image_buffer;
       
   546 	uint          width = window_width;
       
   547 	uint          pitch = window_width;
       
   548 	int x;
       
   549 	int y;
       
   550 
       
   551 	for (y = top; y < bottom; y++) {
       
   552 		for (x = left; x < right; x++) {
       
   553 			dst[y * pitch + x] = pal[src[y * width + x]];
       
   554 		}
       
   555 	}
       
   556 }
       
   557 
       
   558 
       
   559 WindowQuartzSubdriver::WindowQuartzSubdriver(int bpp)
       
   560 {
       
   561 	window_width  = 0;
       
   562 	window_height = 0;
       
   563 	buffer_depth  = bpp;
       
   564 	image_buffer  = NULL;
       
   565 	pixel_buffer  = NULL;
       
   566 	active        = false;
       
   567 	setup         = false;
       
   568 
       
   569 	window = nil;
       
   570 	qzview = nil;
       
   571 
       
   572 	cgcontext = NULL;
       
   573 
       
   574 	num_dirty_rects = MAX_DIRTY_RECTS;
       
   575 }
       
   576 
       
   577 WindowQuartzSubdriver::~WindowQuartzSubdriver()
       
   578 {
       
   579 	QZ_ShowMouse();
       
   580 
       
   581 	/* Release window mode resources */
       
   582 	if (window != nil) [ window close ];
       
   583 
       
   584 	CGContextRelease(cgcontext);
       
   585 
       
   586 	free(image_buffer);
       
   587 	free(pixel_buffer);
       
   588 }
       
   589 
       
   590 void WindowQuartzSubdriver::Draw()
       
   591 {
       
   592 	int i;
       
   593 	NSRect dirtyrect;
       
   594 
       
   595 	/* Check if we need to do anything */
       
   596 	if (num_dirty_rects == 0 ||
       
   597 		[ window isMiniaturized ]) {
       
   598 		return;
       
   599 	}
       
   600 
       
   601 	if (num_dirty_rects >= MAX_DIRTY_RECTS) {
       
   602 		num_dirty_rects = 1;
       
   603 		dirty_rects[0].left = 0;
       
   604 		dirty_rects[0].top = 0;
       
   605 		dirty_rects[0].right = window_width;
       
   606 		dirty_rects[0].bottom = window_height;
       
   607 	}
       
   608 
       
   609 	/* Build the region of dirty rectangles */
       
   610 	for (i = 0; i < num_dirty_rects; i++) {
       
   611 		/* We only need to blit in indexed mode since in 32bpp mode the game draws directly to the image. */
       
   612 		if(buffer_depth == 8) {
       
   613 			BlitIndexedToView32(
       
   614 				dirty_rects[i].left,
       
   615 				dirty_rects[i].top,
       
   616 				dirty_rects[i].right,
       
   617 				dirty_rects[i].bottom
       
   618 			);
       
   619 		}
       
   620 
       
   621 		dirtyrect.origin.x = dirty_rects[i].left;
       
   622 		dirtyrect.origin.y = window_height - dirty_rects[i].bottom;
       
   623 		dirtyrect.size.width = dirty_rects[i].right - dirty_rects[i].left;
       
   624 		dirtyrect.size.height = dirty_rects[i].bottom - dirty_rects[i].top;
       
   625 
       
   626 		/* drawRect will be automatically called by Mac OS X during next update cycle, and then blitting will occur */
       
   627 		[ qzview setNeedsDisplayInRect: dirtyrect ];
       
   628 	}
       
   629 
       
   630 	//DrawResizeIcon();
       
   631 
       
   632 	num_dirty_rects = 0;
       
   633 }
       
   634 
       
   635 void WindowQuartzSubdriver::MakeDirty(int left, int top, int width, int height)
       
   636 {
       
   637 	if (num_dirty_rects < MAX_DIRTY_RECTS) {
       
   638 		dirty_rects[num_dirty_rects].left = left;
       
   639 		dirty_rects[num_dirty_rects].top = top;
       
   640 		dirty_rects[num_dirty_rects].right = left + width;
       
   641 		dirty_rects[num_dirty_rects].bottom = top + height;
       
   642 	}
       
   643 	num_dirty_rects++;
       
   644 }
       
   645 
       
   646 void WindowQuartzSubdriver::UpdatePalette(uint first_color, uint num_colors)
       
   647 {
       
   648 	uint i;
       
   649 
       
   650 	if (buffer_depth != 8)
       
   651 		return;
       
   652 
       
   653 	for (i = first_color; i < first_color + num_colors; i++) {
       
   654 		uint32 clr = 0xff000000;
       
   655 		clr |= (uint32)_cur_palette[i].r << 16;
       
   656 		clr |= (uint32)_cur_palette[i].g << 8;
       
   657 		clr |= (uint32)_cur_palette[i].b;
       
   658 		palette[i] = clr;
       
   659 	}
       
   660 
       
   661 	num_dirty_rects = MAX_DIRTY_RECTS;
       
   662 }
       
   663 
       
   664 uint WindowQuartzSubdriver::ListModes(OTTDPoint* modes, uint max_modes)
       
   665 {
       
   666 	if (max_modes == 0) return 0;
       
   667 
       
   668 	modes[0].x = window_width;
       
   669 	modes[0].y = window_height;
       
   670 
       
   671 	return 1;
       
   672 }
       
   673 
       
   674 bool WindowQuartzSubdriver::ChangeResolution(int w, int h)
       
   675 {
       
   676 	int old_width  = window_width;
       
   677 	int old_height = window_height;
       
   678 
       
   679 	if (SetVideoMode(w, h))
       
   680 		return true;
       
   681 
       
   682 	if (old_width != 0 && old_height != 0)
       
   683 		SetVideoMode(old_width, old_height);
       
   684 
       
   685 	return false;
       
   686 }
       
   687 
       
   688 /* Convert local coordinate to window server (CoreGraphics) coordinate */
       
   689 CGPoint WindowQuartzSubdriver::PrivateLocalToCG(NSPoint* p)
       
   690 {
       
   691 	CGPoint cgp;
       
   692 
       
   693 	*p = [ qzview convertPoint:*p toView: nil ];
       
   694 	*p = [ window convertBaseToScreen:*p ];
       
   695 	p->y = window_height - p->y;
       
   696 
       
   697 	cgp.x = p->x;
       
   698 	cgp.y = p->y;
       
   699 
       
   700 	return cgp;
       
   701 }
       
   702 
       
   703 NSPoint WindowQuartzSubdriver::GetMouseLocation(NSEvent *event)
       
   704 {
       
   705 	NSPoint pt;
       
   706 
       
   707 	pt = [ event locationInWindow ];
       
   708 	pt = [ qzview convertPoint:pt fromView:nil ];
       
   709 
       
   710 	pt.y = window_height - pt.y;
       
   711 
       
   712 	return pt;
       
   713 }
       
   714 
       
   715 bool WindowQuartzSubdriver::MouseIsInsideView(NSPoint *pt)
       
   716 {
       
   717 	return [ qzview mouse:*pt inRect:[ qzview bounds ] ];
       
   718 }
       
   719 
       
   720 
       
   721 /* This function makes the *game region* of the window 100% opaque.
       
   722  * The genie effect uses the alpha component. Otherwise,
       
   723  * it doesn't seem to matter what value it has.
       
   724  */
       
   725 void WindowQuartzSubdriver::SetPortAlphaOpaque()
       
   726 {
       
   727 	uint32* pixels = (uint32*)image_buffer;
       
   728 	uint32  pitch  = window_width;
       
   729 	int x, y;
       
   730 
       
   731 	for (y = 0; y < window_height; y++)
       
   732 		for (x = 0; x < window_width; x++) {
       
   733 		pixels[y * pitch + x] |= 0xFF000000;
       
   734 	}
       
   735 }
       
   736 
       
   737 bool WindowQuartzSubdriver::WindowResized()
       
   738 {
       
   739 	if (window == nil || qzview == nil) return true;
       
   740 
       
   741 	NSRect newframe = [ qzview frame ];
       
   742 
       
   743 	window_width = newframe.size.width;
       
   744 	window_height = newframe.size.height;
       
   745 
       
   746 	/* Create Core Graphics Context */
       
   747 	free(image_buffer);
       
   748 	image_buffer = (uint32*)malloc(window_width * window_height * 4);
       
   749 
       
   750 	CGContextRelease(cgcontext);
       
   751 	cgcontext = CGBitmapContextCreate(
       
   752 		image_buffer,              // data
       
   753 		window_width,              // width
       
   754 		window_height,             // height
       
   755 		8,                         // bits per component
       
   756 		window_width * 4,          // bytes per row
       
   757 		QZ_GetCorrectColorSpace(), // color space
       
   758 		kCGImageAlphaNoneSkipFirst | kCGBitmapByteOrder32Host
       
   759 	);
       
   760 
       
   761 	assert(cgcontext != NULL);
       
   762 	CGContextSetShouldAntialias(cgcontext, FALSE);
       
   763 	CGContextSetAllowsAntialiasing(cgcontext, FALSE);
       
   764 	CGContextSetInterpolationQuality(cgcontext, kCGInterpolationNone);
       
   765 
       
   766 	if (buffer_depth == 8) {
       
   767 		free(pixel_buffer);
       
   768 		pixel_buffer = malloc(window_width * window_height);
       
   769 		if (pixel_buffer == NULL) {
       
   770 			DEBUG(driver, 0, "Failed to allocate pixel buffer");
       
   771 			return false;
       
   772 		}
       
   773 	}
       
   774 
       
   775 	QZ_GameSizeChanged();
       
   776 
       
   777 	/* Redraw screen */
       
   778 	num_dirty_rects = MAX_DIRTY_RECTS;
       
   779 
       
   780 	return true;
       
   781 }
       
   782 
       
   783 
       
   784 CocoaSubdriver *QZ_CreateWindowQuartzSubdriver(int width, int height, int bpp)
       
   785 {
       
   786 	WindowQuartzSubdriver *ret;
       
   787 
       
   788 	if (bpp != 8 && bpp != 32) {
       
   789 		DEBUG(driver, 0, "The cocoa quartz subdriver only supports 8 and 32 bpp.");
       
   790 		return NULL;
       
   791 	}
       
   792 
       
   793 	ret = new WindowQuartzSubdriver(bpp);
       
   794 
       
   795 	if (!ret->ChangeResolution(width, height)) {
       
   796 		delete ret;
       
   797 		return NULL;
       
   798 	}
       
   799 
       
   800 	return ret;
       
   801 }
       
   802 
       
   803 #endif /* WITH_COCOA */