File diff r7169:b87d36998a2d → r7170:38b143754b40
src/video/cocoa_v.mm
Show inline comments
 
@@ -44,52 +44,54 @@ extern "C" OSErr CPSSetFrontProcess(CPSP
 
extern "C" void ShowMenuBar();
 
extern "C" void HideMenuBar();
 

	
 
/* Disables a warning. This is needed since the method exists but has been dropped from the header, supposedly as of 10.4. */
 
#if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4)
 
@interface NSApplication(NSAppleMenu)
 
- (void)setAppleMenu:(NSMenu *)menu;
 
@end
 
#endif
 

	
 

	
 
/* Defined in ppc/param.h or i386/param.h included from sys/param.h */
 
#undef ALIGN
 
/* Defined in stdbool.h */
 
#ifndef __cplusplus
 
# ifndef __BEOS__
 
#  undef bool
 
#  undef false
 
#  undef true
 
# endif
 
#endif
 

	
 

	
 
#include "../stdafx.h"
 
#include "../openttd.h"
 
#include "../debug.h"
 
#include "../macros.h"
 
#include "../os/macosx/splash.h"
 
#include "../variables.h"
 
#include "../gfx.h"
 
#include "cocoa_v.h"
 
#include "cocoa_keys.h"
 
#include "../blitter/factory.hpp"
 
#include "../fileio.h"
 

	
 
#undef Point
 
#undef Rect
 

	
 
/* Subclass of NSWindow to fix genie effect and support resize events  */
 
@interface OTTD_QuartzWindow : NSWindow
 
- (void)miniaturize:(id)sender;
 
- (void)display;
 
- (void)setFrame:(NSRect)frameRect display:(BOOL)flag;
 
- (void)appDidHide:(NSNotification*)note;
 
- (void)appWillUnhide:(NSNotification*)note;
 
- (void)appDidUnhide:(NSNotification*)note;
 
- (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag;
 
@end
 

	
 
/* Delegate for our NSWindow to send ask for quit on close */
 
@interface OTTD_QuartzWindowDelegate : NSObject
 
- (BOOL)windowShouldClose:(id)sender;
 
@end
 

	
 
@@ -110,56 +112,55 @@ struct OTTD_QuartzGammaTable {
 

	
 
/* Add methods to get at private members of NSScreen.
 
 * Since there is a bug in Apple's screen switching code that does not update
 
 * this variable when switching to fullscreen, we'll set it manually (but only
 
 * for the main screen).
 
 */
 
@interface NSScreen (NSScreenAccess)
 
	- (void) setFrame:(NSRect)frame;
 
@end
 

	
 
@implementation NSScreen (NSScreenAccess)
 
- (void) setFrame:(NSRect)frame;
 
{
 
	_frame = frame;
 
}
 
@end
 

	
 

	
 
static void QZ_Draw();
 
static void QZ_UnsetVideoMode();
 
static void QZ_UpdatePalette(uint start, uint count);
 
static void QZ_WarpCursor(int x, int y);
 
static void QZ_ShowMouse();
 
static void QZ_HideMouse();
 
static void CocoaVideoFullScreen(bool full_screen);
 

	
 

	
 
static NSAutoreleasePool *_ottd_autorelease_pool;
 
static OTTDMain *_ottd_main;
 

	
 

	
 
static struct CocoaVideoData {
 
static struct VideoDriver_Cocoa::Data {
 
	bool isset;
 
	bool issetting;
 

	
 
	CGDirectDisplayID  display_id;         /* 0 == main display (only support single display) */
 
	CFDictionaryRef    mode;               /* current mode of the display */
 
	CFDictionaryRef    save_mode;          /* original mode of the display */
 
	CFArrayRef         mode_list;          /* list of available fullscreen modes */
 
	CGDirectPaletteRef palette;            /* palette of an 8-bit display */
 

	
 
	uint32 device_width;
 
	uint32 device_height;
 
	uint32 device_bpp;
 

	
 
	void *realpixels;
 
	uint8 *pixels;
 
	uint32 width;
 
	uint32 height;
 
	uint32 pitch;
 
	bool fullscreen;
 

	
 
	unsigned int current_mods;
 
	bool tab_is_down;
 
	bool emulating_right_button;
 

	
 
@@ -345,49 +346,49 @@ static uint32 QZ_MapKey(unsigned short s
 
		}
 
	}
 

	
 
	if (_cocoa_video_data.current_mods & NSShiftKeyMask)     key |= WKC_SHIFT;
 
	if (_cocoa_video_data.current_mods & NSControlKeyMask)   key |= WKC_CTRL;
 
	if (_cocoa_video_data.current_mods & NSAlternateKeyMask) key |= WKC_ALT;
 
	if (_cocoa_video_data.current_mods & NSCommandKeyMask)   key |= WKC_META;
 

	
 
	return key << 16;
 
}
 

	
 
static void QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL down)
 
{
 
	switch (keycode) {
 
		case QZ_UP:    SB(_dirkeys, 1, 1, down); break;
 
		case QZ_DOWN:  SB(_dirkeys, 3, 1, down); break;
 
		case QZ_LEFT:  SB(_dirkeys, 0, 1, down); break;
 
		case QZ_RIGHT: SB(_dirkeys, 2, 1, down); break;
 

	
 
		case QZ_TAB: _cocoa_video_data.tab_is_down = down; break;
 

	
 
		case QZ_RETURN:
 
		case QZ_f:
 
			if (down && (_cocoa_video_data.current_mods & NSCommandKeyMask)) {
 
				CocoaVideoFullScreen(!_fullscreen);
 
				_video_driver->ToggleFullscreen(!_fullscreen);
 
			}
 
			break;
 
	}
 

	
 
	if (down) {
 
		uint32 pressed_key = QZ_MapKey(keycode) | unicode;
 
		HandleKeypress(pressed_key);
 
		DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, pressed_key);
 
	} else {
 
		DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode);
 
	}
 
}
 

	
 
static void QZ_DoUnsidedModifiers(unsigned int newMods)
 
{
 
	const int mapping[] = { QZ_CAPSLOCK, QZ_LSHIFT, QZ_LCTRL, QZ_LALT, QZ_LMETA };
 

	
 
	int i;
 
	unsigned int bit;
 

	
 
	if (_cocoa_video_data.current_mods == newMods) return;
 

	
 
	/* Iterate through the bits, testing each against the current modifiers */
 
	for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
 
@@ -1298,51 +1299,51 @@ static uint32 QZ_FadeGammaIn(const OTTD_
 
	memset(greenTable, 0, sizeof(greenTable));
 
	memset(blueTable, 0, sizeof(greenTable));
 

	
 
	for (percent = 0.0; percent <= 1.0; percent += 0.01) {
 
		for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) {
 
			redTable[j]   = table->red[j]   * percent;
 
			greenTable[j] = table->green[j] * percent;
 
			blueTable[j]  = table->blue[j]  * percent;
 
		}
 

	
 
		if (CGSetDisplayTransferByTable(
 
					_cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE,
 
					redTable, greenTable, blueTable
 
				) != CGDisplayNoErr) {
 
			CGDisplayRestoreColorSyncSettings();
 
			return 1;
 
		}
 

	
 
		CSleep(10);
 
	}
 

	
 
	return 0;
 
}
 

	
 
static const char* QZ_SetVideoFullScreen(int width, int height)
 
static const char* QZ_SetVideoToggleFullscreen(int width, int height)
 
{
 
	const char* errstr = "QZ_SetVideoFullScreen error";
 
	const char* errstr = "QZ_SetVideoToggleFullscreen error";
 
	int exact_match;
 
	CFNumberRef number;
 
	int bpp;
 
	int gamma_error;
 
	OTTD_QuartzGammaTable gamma_table;
 
	NSRect screen_rect;
 
	CGError error;
 
	NSPoint pt;
 

	
 
	/* Destroy any previous mode */
 
	if (_cocoa_video_data.isset) QZ_UnsetVideoMode();
 

	
 
	/* See if requested mode exists */
 
	_cocoa_video_data.mode = CGDisplayBestModeForParameters(_cocoa_video_data.display_id, 8, width, height, &exact_match);
 

	
 
	/* If the mode wasn't an exact match, check if it has the right bpp, and update width and height */
 
	if (!exact_match) {
 
		number = (const __CFNumber*) CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayBitsPerPixel);
 
		CFNumberGetValue(number, kCFNumberSInt32Type, &bpp);
 
		if (bpp != 8) {
 
			errstr = "Failed to find display resolution";
 
			goto ERR_NO_MATCH;
 
		}
 

	
 
@@ -1686,49 +1687,49 @@ static void QZ_UnsetVideoMode()
 
	} else {
 
		/* Release window mode resources */
 
		[ _cocoa_video_data.window close ];
 
		_cocoa_video_data.window = nil;
 
		_cocoa_video_data.qdview = nil;
 
	}
 

	
 
	free(_cocoa_video_data.pixels);
 
	_cocoa_video_data.pixels = NULL;
 

	
 
	/* Signal successful teardown */
 
	_cocoa_video_data.isset = false;
 

	
 
	QZ_ShowMouse();
 
}
 

	
 

	
 
static const char* QZ_SetVideoMode(uint width, uint height, bool fullscreen)
 
{
 
	const char *ret;
 

	
 
	_cocoa_video_data.issetting = true;
 
	if (fullscreen) {
 
		/* Setup full screen video */
 
		ret = QZ_SetVideoFullScreen(width, height);
 
		ret = QZ_SetVideoToggleFullscreen(width, height);
 
	} else {
 
		/* Setup windowed video */
 
		ret = QZ_SetVideoWindowed(width, height);
 
	}
 
	_cocoa_video_data.issetting = false;
 
	if (ret != NULL) return ret;
 

	
 
	/* Signal successful completion (used internally) */
 
	_cocoa_video_data.isset = true;
 

	
 
	/* Tell the game that the resolution has changed */
 
	_screen.width = _cocoa_video_data.width;
 
	_screen.height = _cocoa_video_data.height;
 
	_screen.pitch = _cocoa_video_data.width;
 

	
 
	QZ_UpdateVideoModes();
 
	GameSizeChanged();
 

	
 
	QZ_InitPalette();
 

	
 
	return NULL;
 
}
 

	
 
static const char* QZ_SetVideoModeAndRestoreOnFailure(uint width, uint height, bool fullscreen)
 
@@ -1949,144 +1950,137 @@ static void setupApplication()
 
	[NSApplication sharedApplication];
 

	
 
	/* Tell the dock about us */
 
	if (!CPSGetCurrentProcess(&PSN) &&
 
			!CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103) &&
 
			!CPSSetFrontProcess(&PSN)) {
 
		[NSApplication sharedApplication];
 
	}
 

	
 
	/* Set up the menubar */
 
	[NSApp setMainMenu:[[NSMenu alloc] init]];
 
	setApplicationMenu();
 
	setupWindowMenu();
 

	
 
	/* Create OTTDMain and make it the app delegate */
 
	_ottd_main = [[OTTDMain alloc] init];
 
	[NSApp setDelegate:_ottd_main];
 
}
 

	
 

	
 
/******************************************************************************
 
 *                             Video driver interface                         *
 
 ******************************************************************************/
 

	
 
static void CocoaVideoStop()
 
static FVideoDriver_Cocoa iFVideoDriver_Cocoa;
 

	
 
void VideoDriver_Cocoa::Stop()
 
{
 
	if (!_cocoa_video_started) return;
 

	
 
	if (_cocoa_video_data.isset) QZ_UnsetVideoMode();
 

	
 
	[_ottd_main release];
 

	
 
	_cocoa_video_started = false;
 
}
 

	
 
static const char *CocoaVideoStart(const char * const *parm)
 
const char *VideoDriver_Cocoa::Start(const char * const *parm)
 
{
 
	const char *ret;
 

	
 
	if (_cocoa_video_started) return "Already started";
 
	_cocoa_video_started = true;
 

	
 
	memset(&_cocoa_video_data, 0, sizeof(_cocoa_video_data));
 

	
 
	setupApplication();
 

	
 
	/* Don't create a window or enter fullscreen if we're just going to show a dialog. */
 
	if (_cocoa_video_dialog) return NULL;
 

	
 
	QZ_VideoInit();
 

	
 
	ret = QZ_SetVideoMode(_cur_resolution[0], _cur_resolution[1], _fullscreen);
 
	if (ret != NULL) CocoaVideoStop();
 
	if (ret != NULL) VideoDriver_Cocoa::Stop();
 

	
 
	return ret;
 
}
 

	
 
static void CocoaVideoMakeDirty(int left, int top, int width, int height)
 
void VideoDriver_Cocoa::MakeDirty(int left, int top, int width, int height)
 
{
 
	if (_cocoa_video_data.num_dirty_rects < MAX_DIRTY_RECTS) {
 
		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].left = left;
 
		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].top = top;
 
		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].right = left + width;
 
		_cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].bottom = top + height;
 
	}
 
	_cocoa_video_data.num_dirty_rects++;
 
}
 

	
 
static void CocoaVideoMainLoop()
 
void VideoDriver_Cocoa::MainLoop()
 
{
 
	/* Start the main event loop */
 
	[NSApp run];
 
}
 

	
 
static bool CocoaVideoChangeRes(int w, int h)
 
bool VideoDriver_Cocoa::ChangeResolution(int w, int h)
 
{
 
	const char *ret = QZ_SetVideoModeAndRestoreOnFailure((uint)w, (uint)h, _cocoa_video_data.fullscreen);
 
	if (ret != NULL) {
 
		DEBUG(driver, 0, "cocoa_v: CocoaVideoChangeRes failed with message: %s", ret);
 
		DEBUG(driver, 0, "cocoa_v: VideoDriver_Cocoa::ChangeResolution failed with message: %s", ret);
 
	}
 

	
 
	return ret == NULL;
 
}
 

	
 
static void CocoaVideoFullScreen(bool full_screen)
 
void VideoDriver_Cocoa::ToggleFullscreen(bool full_screen)
 
{
 
	const char *ret = QZ_SetVideoModeAndRestoreOnFailure(_cocoa_video_data.width, _cocoa_video_data.height, full_screen);
 
	if (ret != NULL) {
 
		DEBUG(driver, 0, "cocoa_v: CocoaVideoFullScreen failed with message: %s", ret);
 
		DEBUG(driver, 0, "cocoa_v: VideoDriver_Cocoa::ToggleFullscreen failed with message: %s", ret);
 
	}
 

	
 
	_fullscreen = _cocoa_video_data.fullscreen;
 
}
 

	
 
const HalVideoDriver _cocoa_video_driver = {
 
	CocoaVideoStart,
 
	CocoaVideoStop,
 
	CocoaVideoMakeDirty,
 
	CocoaVideoMainLoop,
 
	CocoaVideoChangeRes,
 
	CocoaVideoFullScreen,
 
};
 

	
 

	
 
/* This is needed since sometimes assert is called before the videodriver is initialized */
 
void CocoaDialog(const char* title, const char* message, const char* buttonLabel)
 
{
 
	bool wasstarted;
 

	
 
	_cocoa_video_dialog = true;
 

	
 
	wasstarted = _cocoa_video_started;
 
	if (!_cocoa_video_started && CocoaVideoStart(NULL) != NULL) {
 
	if (!_cocoa_video_started && VideoDriver_Cocoa::Start(NULL) != NULL) {
 
		fprintf(stderr, "%s: %s\n", title, message);
 
		return;
 
	}
 

	
 
	NSRunAlertPanel([NSString stringWithCString: title], [NSString stringWithCString: message], [NSString stringWithCString: buttonLabel], nil, nil);
 

	
 
	if (!wasstarted) CocoaVideoStop();
 
	if (!wasstarted) VideoDriver_Cocoa::Stop();
 

	
 
	_cocoa_video_dialog = false;
 
}
 

	
 
/* This is needed since OS X application bundles do not have a
 
 * current directory and the data files are 'somewhere' in the bundle */
 
void cocoaSetApplicationBundleDir()
 
{
 
	char tmp[MAXPATHLEN];
 
	CFURLRef url = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
 
	if (CFURLGetFileSystemRepresentation(url, true, (unsigned char*)tmp, MAXPATHLEN)) {
 
		AppendPathSeparator(tmp, lengthof(tmp));
 
		_searchpaths[SP_APPLICATION_BUNDLE_DIR] = strdup(tmp);
 
	} else {
 
		_searchpaths[SP_APPLICATION_BUNDLE_DIR] = NULL;
 
	}
 

	
 
	CFRelease(url);
 
}
 

	
 
/* These are called from main() to prevent a _NSAutoreleaseNoPool error when
 
 * exiting before the cocoa video driver has been loaded
 
 */
 
void cocoaSetupAutoreleasePool()