|
@@ -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()
|