peter1138@2743: /* $Id$ */ peter1138@2743: tron@3036: /****************************************************************************** tron@3036: * Cocoa video driver * tron@3036: * Known things left to do: * tron@3036: * Nothing at the moment. * tron@3036: ******************************************************************************/ bjarni@2736: bjarni@2736: #ifdef WITH_COCOA bjarni@2736: bjarni@2736: #import bjarni@2736: #import /* gettimeofday */ bjarni@2736: #import /* for MAXPATHLEN */ bjarni@2736: #import bjarni@2736: bjarni@2736: /* Portions of CPS.h */ tron@3036: typedef struct CPSProcessSerNum { tron@3036: UInt32 lo; tron@3036: UInt32 hi; bjarni@2736: } CPSProcessSerNum; bjarni@2736: tron@3036: extern OSErr CPSGetCurrentProcess(CPSProcessSerNum* psn); tron@3036: extern OSErr CPSEnableForegroundOperation(CPSProcessSerNum* psn, UInt32 _arg2, UInt32 _arg3, UInt32 _arg4, UInt32 _arg5); tron@3036: extern OSErr CPSSetFrontProcess(CPSProcessSerNum* psn); bjarni@2736: bjarni@2736: /* From Menus.h (according to Xcode Developer Documentation) */ bjarni@2736: extern void ShowMenuBar(void); bjarni@2736: extern void HideMenuBar(void); bjarni@2736: bjarni@2736: /* Disables a warning. This is needed since the method exists but has been dropped from the header, supposedly as of 10.4. */ bjarni@2741: #if (MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_X_VERSION_10_4) bjarni@2736: @interface NSApplication(NSAppleMenu) bjarni@2736: - (void)setAppleMenu:(NSMenu *)menu; bjarni@2736: @end bjarni@2741: #endif bjarni@2736: bjarni@2736: bjarni@2736: /* Defined in ppc/param.h or i386/param.h included from sys/param.h */ bjarni@2736: #undef ALIGN bjarni@2736: /* Defined in stdbool.h */ bjarni@2736: #ifndef __cplusplus bjarni@2736: # ifndef __BEOS__ bjarni@2736: # undef bool bjarni@2736: # undef false bjarni@2736: # undef true bjarni@2736: # endif bjarni@2736: #endif bjarni@2736: bjarni@2736: #include "../stdafx.h" bjarni@2736: #include "../openttd.h" bjarni@2736: #include "../debug.h" bjarni@2736: #include "../functions.h" bjarni@2736: #include "../gfx.h" bjarni@2736: #include "../macros.h" bjarni@2736: #include "../sdl.h" bjarni@2736: #include "../window.h" celestar@5642: #include "../network/network.h" bjarni@2736: #include "../variables.h" bjarni@2736: #include "../os/macosx/splash.h" bjarni@2736: bjarni@2736: #include "cocoa_v.h" tron@3035: #include "cocoa_keys.h" bjarni@2736: bjarni@2736: #undef Point bjarni@2736: #undef Rect bjarni@2736: bjarni@2736: bjarni@2736: /* Subclass of NSWindow to fix genie effect and support resize events */ bjarni@2736: @interface OTTD_QuartzWindow : NSWindow bjarni@2736: - (void)miniaturize:(id)sender; bjarni@2736: - (void)display; bjarni@2736: - (void)setFrame:(NSRect)frameRect display:(BOOL)flag; bjarni@2736: - (void)appDidHide:(NSNotification*)note; bjarni@2736: - (void)appWillUnhide:(NSNotification*)note; bjarni@2736: - (void)appDidUnhide:(NSNotification*)note; bjarni@2736: - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag; bjarni@2736: @end bjarni@2736: bjarni@2736: /* Delegate for our NSWindow to send ask for quit on close */ bjarni@2736: @interface OTTD_QuartzWindowDelegate : NSObject bjarni@2736: - (BOOL)windowShouldClose:(id)sender; bjarni@2736: @end bjarni@2736: bjarni@2736: @interface OTTDMain : NSObject bjarni@2736: @end bjarni@2736: bjarni@2736: tron@3036: /* Structure for rez switch gamma fades tron@3036: * We can hide the monitor flicker by setting the gamma tables to 0 tron@3036: */ bjarni@2736: #define QZ_GAMMA_TABLE_SIZE 256 bjarni@2736: bjarni@2736: typedef struct { bjarni@2736: CGGammaValue red[QZ_GAMMA_TABLE_SIZE]; bjarni@2736: CGGammaValue green[QZ_GAMMA_TABLE_SIZE]; bjarni@2736: CGGammaValue blue[QZ_GAMMA_TABLE_SIZE]; bjarni@2736: } OTTD_QuartzGammaTable; bjarni@2736: tron@3036: /* Add methods to get at private members of NSScreen. tron@3036: * Since there is a bug in Apple's screen switching code that does not update tron@3036: * this variable when switching to fullscreen, we'll set it manually (but only tron@3036: * for the main screen). tron@3036: */ bjarni@2736: @interface NSScreen (NSScreenAccess) tron@3036: - (void) setFrame:(NSRect)frame; bjarni@2736: @end bjarni@2736: bjarni@2736: @implementation NSScreen (NSScreenAccess) bjarni@2736: - (void) setFrame:(NSRect)frame; bjarni@2736: { bjarni@2736: _frame = frame; bjarni@2736: } bjarni@2736: @end bjarni@2736: bjarni@2736: bjarni@2736: static void QZ_Draw(void); tron@3036: static void QZ_UnsetVideoMode(void); bjarni@2736: static void QZ_UpdatePalette(uint start, uint count); tron@3036: static void QZ_WarpCursor(int x, int y); tron@3036: static void QZ_ShowMouse(void); tron@3036: static void QZ_HideMouse(void); bjarni@2736: static void CocoaVideoFullScreen(bool full_screen); bjarni@2736: bjarni@2736: bjarni@2736: static NSAutoreleasePool *_ottd_autorelease_pool; bjarni@2736: static OTTDMain *_ottd_main; bjarni@2736: bjarni@2736: bjarni@2736: static struct CocoaVideoData { bjarni@2736: bool isset; bjarni@2736: bool issetting; bjarni@2736: bjarni@2736: CGDirectDisplayID display_id; /* 0 == main display (only support single display) */ bjarni@2736: CFDictionaryRef mode; /* current mode of the display */ bjarni@2736: CFDictionaryRef save_mode; /* original mode of the display */ bjarni@2736: CFArrayRef mode_list; /* list of available fullscreen modes */ bjarni@2736: CGDirectPaletteRef palette; /* palette of an 8-bit display */ bjarni@2736: bjarni@2736: uint32 device_width; bjarni@2736: uint32 device_height; bjarni@2736: uint32 device_bpp; bjarni@2736: bjarni@2736: void *realpixels; bjarni@2736: uint8 *pixels; bjarni@2736: uint32 width; bjarni@2736: uint32 height; bjarni@2736: uint32 pitch; bjarni@2736: bool fullscreen; bjarni@2736: bjarni@2736: unsigned int current_mods; bjarni@2736: bool tab_is_down; bjarni@2736: bool emulating_right_button; bjarni@2736: bjarni@2736: bool cursor_visible; bjarni@2736: bool active; bjarni@2736: bjarni@2736: #ifdef _DEBUG bjarni@2736: uint32 tEvent; bjarni@2736: #endif bjarni@2736: bjarni@2736: OTTD_QuartzWindow *window; bjarni@2736: NSQuickDrawView *qdview; bjarni@2736: bjarni@2736: #define MAX_DIRTY_RECTS 100 bjarni@2736: OTTDRect dirty_rects[MAX_DIRTY_RECTS]; bjarni@2736: int num_dirty_rects; bjarni@2736: bjarni@2736: uint16 palette16[256]; bjarni@2736: uint32 palette32[256]; bjarni@2736: } _cocoa_video_data; bjarni@2736: truelight@2827: static bool _cocoa_video_started = false; truelight@2827: static bool _cocoa_video_dialog = false; bjarni@2736: bjarni@2736: bjarni@2736: bjarni@2736: tron@3036: /****************************************************************************** tron@3036: * Game loop and accessories * tron@3036: ******************************************************************************/ bjarni@2736: bjarni@2736: static uint32 GetTick(void) bjarni@2736: { bjarni@2736: struct timeval tim; bjarni@2736: bjarni@2736: gettimeofday(&tim, NULL); bjarni@2736: return tim.tv_usec / 1000 + tim.tv_sec * 1000; bjarni@2736: } bjarni@2736: bjarni@2736: static void QZ_CheckPaletteAnim(void) bjarni@2736: { bjarni@2736: if (_pal_last_dirty != -1) { bjarni@2736: QZ_UpdatePalette(_pal_first_dirty, _pal_last_dirty - _pal_first_dirty + 1); bjarni@2736: _pal_last_dirty = -1; bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: bjarni@2736: typedef struct VkMapping { bjarni@2736: unsigned short vk_from; bjarni@2736: byte map_to; bjarni@2736: } VkMapping; bjarni@2736: bjarni@2736: #define AS(x, z) {x, z} bjarni@2736: bjarni@2736: static const VkMapping _vk_mapping[] = { tron@3036: AS(QZ_BACKQUOTE, WKC_BACKQUOTE), // key left of '1' tron@3036: AS(QZ_BACKQUOTE2, WKC_BACKQUOTE), // some keyboards have it on another scancode bjarni@2845: bjarni@2736: // Pageup stuff + up/down bjarni@2736: //AM(SDLK_PAGEUP, SDLK_PAGEDOWN, WKC_PAGEUP, WKC_PAGEDOWN), <==== Does this include HOME/END? tron@3036: AS(QZ_PAGEUP, WKC_PAGEUP), tron@3036: AS(QZ_PAGEDOWN, WKC_PAGEDOWN), bjarni@2736: tron@3036: AS(QZ_UP, WKC_UP), tron@3036: AS(QZ_DOWN, WKC_DOWN), tron@3036: AS(QZ_LEFT, WKC_LEFT), tron@3036: AS(QZ_RIGHT, WKC_RIGHT), bjarni@2736: tron@3036: AS(QZ_HOME, WKC_HOME), tron@3036: AS(QZ_END, WKC_END), bjarni@2736: tron@3036: AS(QZ_INSERT, WKC_INSERT), tron@3036: AS(QZ_DELETE, WKC_DELETE), bjarni@2736: bjarni@2736: // Letters. QZ_[a-z] is not in numerical order so we can't use AM(...) tron@3036: AS(QZ_a, 'A'), tron@3036: AS(QZ_b, 'B'), tron@3036: AS(QZ_c, 'C'), tron@3036: AS(QZ_d, 'D'), tron@3036: AS(QZ_e, 'E'), tron@3036: AS(QZ_f, 'F'), tron@3036: AS(QZ_g, 'G'), tron@3036: AS(QZ_h, 'H'), tron@3036: AS(QZ_i, 'I'), tron@3036: AS(QZ_j, 'J'), tron@3036: AS(QZ_k, 'K'), tron@3036: AS(QZ_l, 'L'), tron@3036: AS(QZ_m, 'M'), tron@3036: AS(QZ_n, 'N'), tron@3036: AS(QZ_o, 'O'), tron@3036: AS(QZ_p, 'P'), tron@3036: AS(QZ_q, 'Q'), tron@3036: AS(QZ_r, 'R'), tron@3036: AS(QZ_s, 'S'), tron@3036: AS(QZ_t, 'T'), tron@3036: AS(QZ_u, 'U'), tron@3036: AS(QZ_v, 'V'), tron@3036: AS(QZ_w, 'W'), tron@3036: AS(QZ_x, 'X'), tron@3036: AS(QZ_y, 'Y'), tron@3036: AS(QZ_z, 'Z'), bjarni@2736: // Same thing for digits tron@3036: AS(QZ_0, '0'), tron@3036: AS(QZ_1, '1'), tron@3036: AS(QZ_2, '2'), tron@3036: AS(QZ_3, '3'), tron@3036: AS(QZ_4, '4'), tron@3036: AS(QZ_5, '5'), tron@3036: AS(QZ_6, '6'), tron@3036: AS(QZ_7, '7'), tron@3036: AS(QZ_8, '8'), tron@3036: AS(QZ_9, '9'), bjarni@2736: tron@3036: AS(QZ_ESCAPE, WKC_ESC), tron@3036: AS(QZ_PAUSE, WKC_PAUSE), tron@3036: AS(QZ_BACKSPACE, WKC_BACKSPACE), bjarni@2736: tron@3036: AS(QZ_SPACE, WKC_SPACE), tron@3036: AS(QZ_RETURN, WKC_RETURN), tron@3036: AS(QZ_TAB, WKC_TAB), bjarni@2736: bjarni@2736: // Function keys tron@3036: AS(QZ_F1, WKC_F1), tron@3036: AS(QZ_F2, WKC_F2), tron@3036: AS(QZ_F3, WKC_F3), tron@3036: AS(QZ_F4, WKC_F4), tron@3036: AS(QZ_F5, WKC_F5), tron@3036: AS(QZ_F6, WKC_F6), tron@3036: AS(QZ_F7, WKC_F7), tron@3036: AS(QZ_F8, WKC_F8), tron@3036: AS(QZ_F9, WKC_F9), tron@3036: AS(QZ_F10, WKC_F10), tron@3036: AS(QZ_F11, WKC_F11), tron@3036: AS(QZ_F12, WKC_F12), bjarni@2736: bjarni@2736: // Numeric part. tron@3036: AS(QZ_KP0, WKC_NUM_0), tron@3036: AS(QZ_KP1, WKC_NUM_1), tron@3036: AS(QZ_KP2, WKC_NUM_2), tron@3036: AS(QZ_KP3, WKC_NUM_3), tron@3036: AS(QZ_KP4, WKC_NUM_4), tron@3036: AS(QZ_KP5, WKC_NUM_5), tron@3036: AS(QZ_KP6, WKC_NUM_6), tron@3036: AS(QZ_KP7, WKC_NUM_7), tron@3036: AS(QZ_KP8, WKC_NUM_8), tron@3036: AS(QZ_KP9, WKC_NUM_9), tron@3036: AS(QZ_KP_DIVIDE, WKC_NUM_DIV), tron@3036: AS(QZ_KP_MULTIPLY, WKC_NUM_MUL), tron@3036: AS(QZ_KP_MINUS, WKC_NUM_MINUS), tron@3036: AS(QZ_KP_PLUS, WKC_NUM_PLUS), tron@3036: AS(QZ_KP_ENTER, WKC_NUM_ENTER), tron@3036: AS(QZ_KP_PERIOD, WKC_NUM_DECIMAL) bjarni@2736: }; bjarni@2736: bjarni@2736: bjarni@2736: static uint32 QZ_MapKey(unsigned short sym) bjarni@2736: { bjarni@2736: const VkMapping *map; bjarni@2736: uint32 key = 0; bjarni@2736: bjarni@2736: for (map = _vk_mapping; map != endof(_vk_mapping); ++map) { tron@3036: if (sym == map->vk_from) { bjarni@2736: key = map->map_to; bjarni@2736: break; bjarni@2736: } bjarni@2736: } bjarni@2736: tron@3036: if (_cocoa_video_data.current_mods & NSShiftKeyMask) key |= WKC_SHIFT; tron@3036: if (_cocoa_video_data.current_mods & NSControlKeyMask) key |= WKC_CTRL; tron@3036: if (_cocoa_video_data.current_mods & NSAlternateKeyMask) key |= WKC_ALT; tron@3036: if (_cocoa_video_data.current_mods & NSCommandKeyMask) key |= WKC_META; bjarni@2736: bjarni@2736: return key << 16; bjarni@2736: } bjarni@2736: bjarni@2736: static void QZ_KeyEvent(unsigned short keycode, unsigned short unicode, BOOL down) bjarni@2736: { tron@3036: switch (keycode) { tron@3036: case QZ_UP: SB(_dirkeys, 1, 1, down); break; tron@3036: case QZ_DOWN: SB(_dirkeys, 3, 1, down); break; tron@3036: case QZ_LEFT: SB(_dirkeys, 0, 1, down); break; tron@3036: case QZ_RIGHT: SB(_dirkeys, 2, 1, down); break; tron@3036: tron@3036: case QZ_TAB: _cocoa_video_data.tab_is_down = down; break; tron@3036: bjarni@2736: case QZ_RETURN: bjarni@2736: case QZ_f: tron@3036: if (down && ( tron@3036: (_cocoa_video_data.current_mods & NSControlKeyMask) || tron@3036: (_cocoa_video_data.current_mods & NSCommandKeyMask) tron@3036: )) { bjarni@2736: CocoaVideoFullScreen(!_fullscreen); tron@3036: } tron@3036: break; bjarni@2736: } bjarni@2736: tron@3036: if (down) { Darkvater@5086: uint32 pressed_key = QZ_MapKey(keycode) | unicode; Darkvater@5086: HandleKeypress(pressed_key); rubidium@5571: DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), down, mapping: %x", keycode, unicode, pressed_key); bjarni@2736: } else { rubidium@5571: DEBUG(driver, 2, "cocoa_v: QZ_KeyEvent: %x (%x), up", keycode, unicode); bjarni@2736: } bjarni@2736: } bjarni@2736: tron@3036: static void QZ_DoUnsidedModifiers(unsigned int newMods) tron@3036: { bjarni@2736: const int mapping[] = { QZ_CAPSLOCK, QZ_LSHIFT, QZ_LCTRL, QZ_LALT, QZ_LMETA }; bjarni@2736: bjarni@2736: int i; bjarni@2736: int bit; bjarni@2736: tron@3036: if (_cocoa_video_data.current_mods == newMods) return; bjarni@2736: bjarni@2736: /* Iterate through the bits, testing each against the current modifiers */ bjarni@2736: for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) { bjarni@2736: unsigned int currentMask, newMask; bjarni@2736: bjarni@2736: currentMask = _cocoa_video_data.current_mods & bit; bjarni@2736: newMask = newMods & bit; bjarni@2736: tron@3036: if (currentMask && currentMask != newMask) { /* modifier up event */ bjarni@2736: /* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */ tron@3036: if (bit == NSAlphaShiftKeyMask) QZ_KeyEvent(mapping[i], 0, YES); bjarni@2736: QZ_KeyEvent(mapping[i], 0, NO); tron@3036: } else if (newMask && currentMask != newMask) { /* modifier down event */ bjarni@2736: QZ_KeyEvent(mapping[i], 0, YES); bjarni@2736: /* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */ tron@3036: if (bit == NSAlphaShiftKeyMask) QZ_KeyEvent(mapping[i], 0, NO); bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: _cocoa_video_data.current_mods = newMods; bjarni@2736: } bjarni@2736: bjarni@2736: static void QZ_MouseMovedEvent(int x, int y) bjarni@2736: { tron@3036: if (_cursor.fix_at) { tron@3036: int dx = x - _cursor.pos.x; tron@3036: int dy = y - _cursor.pos.y; bjarni@2736: bjarni@2736: if (dx != 0 || dy != 0) { bjarni@2736: _cursor.delta.x += dx; bjarni@2736: _cursor.delta.y += dy; bjarni@2736: bjarni@2736: QZ_WarpCursor(_cursor.pos.x, _cursor.pos.y); bjarni@2736: } bjarni@2736: } else { bjarni@2736: _cursor.delta.x = x - _cursor.pos.x; bjarni@2736: _cursor.delta.y = y - _cursor.pos.y; bjarni@2736: _cursor.pos.x = x; bjarni@2736: _cursor.pos.y = y; bjarni@2736: _cursor.dirty = true; bjarni@2736: } Darkvater@5090: HandleMouseEvents(); bjarni@2736: } bjarni@2736: tron@4509: tron@4509: static void QZ_MouseButtonEvent(int button, BOOL down) bjarni@2736: { tron@3036: switch (button) { bjarni@2736: case 0: tron@3036: if (down) { bjarni@2736: _left_button_down = true; bjarni@2736: } else { bjarni@2736: _left_button_down = false; bjarni@2736: _left_button_clicked = false; bjarni@2736: } Darkvater@5090: HandleMouseEvents(); bjarni@2736: break; tron@3036: bjarni@2736: case 1: tron@3036: if (down) { bjarni@2736: _right_button_down = true; bjarni@2736: _right_button_clicked = true; bjarni@2736: } else { bjarni@2736: _right_button_down = false; bjarni@2736: } Darkvater@5090: HandleMouseEvents(); bjarni@2736: break; bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: static inline NSPoint QZ_GetMouseLocation(NSEvent *event) bjarni@2736: { bjarni@2736: NSPoint pt; bjarni@2736: tron@3036: if (_cocoa_video_data.fullscreen) { bjarni@2736: pt = [ NSEvent mouseLocation ]; bjarni@2736: pt.y = _cocoa_video_data.height - pt.y; bjarni@2736: } else { bjarni@2736: pt = [event locationInWindow]; bjarni@2736: pt = [_cocoa_video_data.qdview convertPoint:pt fromView:nil]; bjarni@2736: } bjarni@2736: bjarni@2736: return pt; bjarni@2736: } bjarni@2736: bjarni@2736: static bool QZ_MouseIsInsideView(NSPoint *pt) bjarni@2736: { tron@3036: if (_cocoa_video_data.fullscreen) { bjarni@2736: return pt->x >= 0 && pt->y >= 0 && pt->x < _cocoa_video_data.width && pt->y < _cocoa_video_data.height; tron@3036: } else { bjarni@2736: return [ _cocoa_video_data.qdview mouse:*pt inRect:[ _cocoa_video_data.qdview bounds ] ]; tron@3036: } bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: static bool QZ_PollEvent(void) bjarni@2736: { bjarni@2736: NSEvent *event; bjarni@2736: NSPoint pt; bjarni@2736: NSString *chars; bjarni@2736: #ifdef _DEBUG bjarni@2736: uint32 et0, et; bjarni@2736: #endif bjarni@2736: bjarni@2736: #ifdef _DEBUG bjarni@2736: et0 = GetTick(); bjarni@2736: #endif bjarni@2736: event = [ NSApp nextEventMatchingMask:NSAnyEventMask bjarni@2736: untilDate: [ NSDate distantPast ] bjarni@2736: inMode: NSDefaultRunLoopMode dequeue:YES ]; bjarni@2736: #ifdef _DEBUG bjarni@2736: et = GetTick(); bjarni@2736: _cocoa_video_data.tEvent+= et - et0; bjarni@2736: #endif bjarni@2736: tron@3036: if (event == nil) return false; tron@3036: if (!_cocoa_video_data.active) { bjarni@2736: QZ_ShowMouse(); bjarni@2736: [NSApp sendEvent:event]; bjarni@2736: return true; bjarni@2736: } bjarni@2736: bjarni@2736: QZ_DoUnsidedModifiers( [ event modifierFlags ] ); bjarni@2736: bjarni@2736: switch ([event type]) { bjarni@2736: case NSMouseMoved: bjarni@2736: case NSOtherMouseDragged: bjarni@2736: case NSRightMouseDragged: bjarni@2736: case NSLeftMouseDragged: bjarni@2736: pt = QZ_GetMouseLocation(event); tron@3036: if (!QZ_MouseIsInsideView(&pt) && tron@3036: !_cocoa_video_data.emulating_right_button) { bjarni@2736: QZ_ShowMouse(); bjarni@2736: [NSApp sendEvent:event]; bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: QZ_HideMouse(); tron@3036: QZ_MouseMovedEvent((int)pt.x, (int)pt.y); bjarni@2736: break; bjarni@2736: bjarni@2736: case NSLeftMouseDown: tron@3036: if (!([ event modifierFlags ] & NSCommandKeyMask) || tron@3036: !QZ_MouseIsInsideView(&pt)) { bjarni@2736: [NSApp sendEvent:event]; tron@3036: } bjarni@2736: bjarni@2736: pt = QZ_GetMouseLocation(event); tron@3036: if (!QZ_MouseIsInsideView(&pt)) { bjarni@2736: QZ_ShowMouse(); bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: QZ_HideMouse(); tron@3036: QZ_MouseMovedEvent((int)pt.x, (int)pt.y); bjarni@2736: bjarni@2736: /* Right mouse button emulation */ tron@3036: if ([ event modifierFlags ] & NSCommandKeyMask) { bjarni@2736: _cocoa_video_data.emulating_right_button = true; bjarni@2736: QZ_MouseButtonEvent(1, YES); tron@3036: } else { bjarni@2736: QZ_MouseButtonEvent(0, YES); tron@3036: } bjarni@2736: break; tron@3036: bjarni@2736: case NSLeftMouseUp: bjarni@2736: [NSApp sendEvent:event]; bjarni@2736: bjarni@2736: pt = QZ_GetMouseLocation(event); tron@3036: if (!QZ_MouseIsInsideView(&pt)) { bjarni@2736: QZ_ShowMouse(); bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: QZ_HideMouse(); tron@3036: QZ_MouseMovedEvent((int)pt.x, (int)pt.y); bjarni@2736: bjarni@2736: /* Right mouse button emulation */ tron@3036: if (_cocoa_video_data.emulating_right_button) { bjarni@2736: _cocoa_video_data.emulating_right_button = false; bjarni@2736: QZ_MouseButtonEvent(1, NO); tron@3036: } else { bjarni@2736: QZ_MouseButtonEvent(0, NO); tron@3036: } bjarni@2736: break; bjarni@2736: bjarni@2736: case NSRightMouseDown: bjarni@2736: pt = QZ_GetMouseLocation(event); tron@3036: if (!QZ_MouseIsInsideView(&pt)) { bjarni@2736: QZ_ShowMouse(); bjarni@2736: [NSApp sendEvent:event]; bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: QZ_HideMouse(); tron@3036: QZ_MouseMovedEvent((int)pt.x, (int)pt.y); bjarni@2736: QZ_MouseButtonEvent(1, YES); bjarni@2736: break; tron@3036: bjarni@2736: case NSRightMouseUp: bjarni@2736: pt = QZ_GetMouseLocation(event); tron@3036: if (!QZ_MouseIsInsideView(&pt)) { bjarni@2736: QZ_ShowMouse(); bjarni@2736: [NSApp sendEvent:event]; bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: QZ_HideMouse(); tron@3036: QZ_MouseMovedEvent((int)pt.x, (int)pt.y); bjarni@2736: QZ_MouseButtonEvent(1, NO); bjarni@2736: break; bjarni@2736: tron@3036: #if 0 bjarni@2736: /* This is not needed since openttd currently only use two buttons */ bjarni@2736: case NSOtherMouseDown: bjarni@2736: pt = QZ_GetMouseLocation(event); tron@3036: if (!QZ_MouseIsInsideView(&pt)) { bjarni@2736: QZ_ShowMouse(); bjarni@2736: [NSApp sendEvent:event]; bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: QZ_HideMouse(); tron@3036: QZ_MouseMovedEvent((int)pt.x, (int)pt.y); bjarni@2736: QZ_MouseButtonEvent([ event buttonNumber ], YES); bjarni@2736: break; tron@3036: bjarni@2736: case NSOtherMouseUp: bjarni@2736: pt = QZ_GetMouseLocation(event); tron@3036: if (!QZ_MouseIsInsideView(&pt)) { bjarni@2736: QZ_ShowMouse(); bjarni@2736: [NSApp sendEvent:event]; bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: QZ_HideMouse(); tron@3036: QZ_MouseMovedEvent((int)pt.x, (int)pt.y); bjarni@2736: QZ_MouseButtonEvent([ event buttonNumber ], NO); bjarni@2736: break; tron@3036: #endif tron@3036: bjarni@2736: case NSKeyDown: bjarni@2736: /* Quit, hide and minimize */ tron@3036: switch ([event keyCode]) { bjarni@2736: case QZ_q: bjarni@2736: case QZ_h: bjarni@2736: case QZ_m: tron@3036: if ([ event modifierFlags ] & NSCommandKeyMask) { bjarni@2736: [NSApp sendEvent:event]; tron@3036: } bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: chars = [ event characters ]; bjarni@2736: QZ_KeyEvent([event keyCode], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, YES); bjarni@2736: break; bjarni@2736: bjarni@2736: case NSKeyUp: bjarni@2736: /* Quit, hide and minimize */ tron@3036: switch ([event keyCode]) { bjarni@2736: case QZ_q: bjarni@2736: case QZ_h: bjarni@2736: case QZ_m: tron@3036: if ([ event modifierFlags ] & NSCommandKeyMask) { bjarni@2736: [NSApp sendEvent:event]; tron@3036: } bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: chars = [ event characters ]; bjarni@2736: QZ_KeyEvent([event keyCode], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, NO); bjarni@2736: break; bjarni@2736: bjarni@2736: case NSScrollWheel: bjarni@4116: if ([ event deltaY ] > 0.0) { /* Scroll up */ bjarni@2736: _cursor.wheel--; bjarni@4116: } else if ([ event deltaY ] < 0.0) { /* Scroll down */ bjarni@2736: _cursor.wheel++; bjarni@4116: } /* else: deltaY was 0.0 and we don't want to do anything */ bjarni@2736: break; bjarni@2736: bjarni@2736: default: bjarni@2736: [NSApp sendEvent:event]; bjarni@2736: } bjarni@2736: bjarni@2736: return true; bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: static void QZ_GameLoop(void) bjarni@2736: { bjarni@2736: uint32 next_tick = GetTick() + 30; bjarni@2736: uint32 cur_ticks; bjarni@2736: uint32 pal_tick = 0; bjarni@2736: #ifdef _DEBUG bjarni@2736: uint32 et0, et, st0, st; bjarni@2736: #endif bjarni@2736: int i; bjarni@2736: bjarni@2736: #ifdef _DEBUG bjarni@2736: et0 = GetTick(); bjarni@2736: st = 0; bjarni@2736: #endif bjarni@2736: bjarni@2736: _screen.dst_ptr = _cocoa_video_data.pixels; bjarni@2736: DisplaySplashImage(); bjarni@2736: QZ_CheckPaletteAnim(); bjarni@2736: QZ_Draw(); bjarni@2736: CSleep(1); bjarni@2736: tron@3036: for (i = 0; i < 2; i++) GameLoop(); bjarni@2736: bjarni@2736: _screen.dst_ptr = _cocoa_video_data.pixels; bjarni@2736: UpdateWindows(); bjarni@2736: QZ_CheckPaletteAnim(); bjarni@2736: QZ_Draw(); bjarni@2736: CSleep(1); bjarni@2736: tron@3036: for (;;) { bjarni@2736: InteractiveRandom(); // randomness bjarni@2736: bjarni@2736: while (QZ_PollEvent()) {} bjarni@2736: tron@3036: if (_exit_game) break; bjarni@2736: bjarni@2736: #if defined(_DEBUG) tron@3036: if (_cocoa_video_data.current_mods & NSShiftKeyMask) bjarni@2736: #else bjarni@2736: if (_cocoa_video_data.tab_is_down) bjarni@2736: #endif bjarni@2736: { bjarni@2736: if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2; rubidium@4536: } else if (_fast_forward & 2) { bjarni@2736: _fast_forward = 0; bjarni@2736: } bjarni@2736: bjarni@2736: cur_ticks = GetTick(); bjarni@2736: if ((_fast_forward && !_pause) || cur_ticks > next_tick) bjarni@2736: next_tick = cur_ticks; bjarni@2736: bjarni@2736: if (cur_ticks == next_tick) { bjarni@2736: next_tick += 30; bjarni@2736: bjarni@2736: _ctrl_pressed = !!(_cocoa_video_data.current_mods & NSControlKeyMask); bjarni@2736: _shift_pressed = !!(_cocoa_video_data.current_mods & NSShiftKeyMask); bjarni@2736: #ifdef _DEBUG bjarni@2736: _dbg_screen_rect = !!(_cocoa_video_data.current_mods & NSAlphaShiftKeyMask); bjarni@2736: #endif bjarni@2736: bjarni@2736: GameLoop(); bjarni@2736: bjarni@2736: _screen.dst_ptr = _cocoa_video_data.pixels; bjarni@2736: UpdateWindows(); bjarni@2736: if (++pal_tick > 4) { bjarni@2736: QZ_CheckPaletteAnim(); bjarni@2736: pal_tick = 1; bjarni@2736: } bjarni@2736: QZ_Draw(); bjarni@2736: } else { bjarni@2736: #ifdef _DEBUG bjarni@2736: st0 = GetTick(); bjarni@2736: #endif bjarni@2736: CSleep(1); bjarni@2736: #ifdef _DEBUG tron@3036: st += GetTick() - st0; bjarni@2736: #endif bjarni@2736: _screen.dst_ptr = _cocoa_video_data.pixels; bjarni@2736: DrawTextMessage(); bjarni@2736: DrawMouseCursor(); bjarni@2736: QZ_Draw(); bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: #ifdef _DEBUG bjarni@2736: et = GetTick(); bjarni@2736: rubidium@5571: DEBUG(driver, 1, "cocoa_v: nextEventMatchingMask took %i ms total", _cocoa_video_data.tEvent); rubidium@5571: DEBUG(driver, 1, "cocoa_v: game loop took %i ms total (%i ms without sleep)", et - et0, et - et0 - st); rubidium@5571: DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop total) is %f%%", (double)_cocoa_video_data.tEvent / (double)(et - et0) * 100); rubidium@5571: DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop without sleep total) is %f%%", (double)_cocoa_video_data.tEvent / (double)(et - et0 - st) * 100); bjarni@2736: #endif bjarni@2736: } bjarni@2736: bjarni@2736: tron@3036: /****************************************************************************** tron@3036: * Windowed mode * tron@3036: ******************************************************************************/ bjarni@2736: tron@3036: /* This function makes the *game region* of the window 100% opaque. tron@3036: * The genie effect uses the alpha component. Otherwise, tron@3036: * it doesn't seem to matter what value it has. tron@3036: */ tron@3036: static void QZ_SetPortAlphaOpaque(void) tron@3036: { bjarni@2736: if (_cocoa_video_data.device_bpp == 32) { tron@3036: uint32* pixels = (uint32*)_cocoa_video_data.realpixels; tron@3036: uint32 rowPixels = _cocoa_video_data.pitch / 4; tron@3036: uint32 i; tron@3036: uint32 j; bjarni@2736: bjarni@2736: for (i = 0; i < _cocoa_video_data.height; i++) bjarni@2736: for (j = 0; j < _cocoa_video_data.width; j++) { tron@3036: pixels[i * rowPixels + j] |= 0xFF000000; bjarni@2736: } bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: @implementation OTTD_QuartzWindow bjarni@2736: bjarni@2736: /* we override these methods to fix the miniaturize animation/dock icon bug */ bjarni@2736: - (void)miniaturize:(id)sender bjarni@2736: { bjarni@2736: /* make the alpha channel opaque so anim won't have holes in it */ bjarni@2736: QZ_SetPortAlphaOpaque (); bjarni@2736: bjarni@2736: /* window is hidden now */ bjarni@2736: _cocoa_video_data.active = false; bjarni@2736: bjarni@2736: QZ_ShowMouse(); bjarni@2736: bjarni@2736: [ super miniaturize:sender ]; bjarni@2736: } bjarni@2736: bjarni@2736: - (void)display bjarni@2736: { tron@3036: /* This method fires just before the window deminaturizes from the Dock. tron@3036: * We'll save the current visible surface, let the window manager redraw any tron@3036: * UI elements, and restore the surface. This way, no expose event tron@3036: * is required, and the deminiaturize works perfectly. tron@3036: */ bjarni@2736: tron@3036: QZ_SetPortAlphaOpaque(); bjarni@2736: bjarni@2736: /* save current visible surface */ bjarni@2736: [ self cacheImageInRect:[ _cocoa_video_data.qdview frame ] ]; bjarni@2736: bjarni@2736: /* let the window manager redraw controls, border, etc */ bjarni@2736: [ super display ]; bjarni@2736: bjarni@2736: /* restore visible surface */ bjarni@2736: [ self restoreCachedImage ]; bjarni@2736: bjarni@2736: /* window is visible again */ bjarni@2736: _cocoa_video_data.active = true; bjarni@2736: } bjarni@2736: bjarni@2736: - (void)setFrame:(NSRect)frameRect display:(BOOL)flag bjarni@2736: { bjarni@2736: NSRect newViewFrame; bjarni@2736: CGrafPtr thePort; bjarni@2736: bjarni@2736: [ super setFrame:frameRect display:flag ]; bjarni@2736: bjarni@2736: /* Don't do anything if the window is currently beign created */ tron@3036: if (_cocoa_video_data.issetting) return; bjarni@2736: tron@3036: if (_cocoa_video_data.window == nil) return; bjarni@2736: bjarni@2736: newViewFrame = [ _cocoa_video_data.qdview frame ]; bjarni@2736: bjarni@2736: /* Update the pixels and pitch */ bjarni@2736: thePort = [ _cocoa_video_data.qdview qdPort ]; tron@3036: LockPortBits(thePort); bjarni@2736: tron@3036: _cocoa_video_data.realpixels = GetPixBaseAddr(GetPortPixMap(thePort)); tron@3036: _cocoa_video_data.pitch = GetPixRowBytes(GetPortPixMap(thePort)); bjarni@2736: tron@3036: /* _cocoa_video_data.realpixels now points to the window's pixels tron@3036: * We want it to point to the *view's* pixels tron@3036: */ bjarni@2736: { bjarni@2736: int vOffset = [ _cocoa_video_data.window frame ].size.height - newViewFrame.size.height - newViewFrame.origin.y; bjarni@2736: int hOffset = newViewFrame.origin.x; bjarni@2736: tron@3036: _cocoa_video_data.realpixels = (uint8*)_cocoa_video_data.realpixels + (vOffset * _cocoa_video_data.pitch) + hOffset * (_cocoa_video_data.device_bpp / 8); bjarni@2736: } bjarni@2736: tron@3036: UnlockPortBits(thePort); bjarni@2736: bjarni@2736: /* Allocate new buffer */ tron@3036: free(_cocoa_video_data.pixels); tron@3036: _cocoa_video_data.pixels = (uint8*)malloc(newViewFrame.size.width * newViewFrame.size.height); bjarni@3261: assert(_cocoa_video_data.pixels != NULL); bjarni@2736: bjarni@2736: bjarni@2736: /* Tell the game that the resolution changed */ bjarni@2736: _cocoa_video_data.width = newViewFrame.size.width; bjarni@2736: _cocoa_video_data.height = newViewFrame.size.height; bjarni@2736: bjarni@2736: _screen.width = _cocoa_video_data.width; bjarni@2736: _screen.height = _cocoa_video_data.height; bjarni@2736: _screen.pitch = _cocoa_video_data.width; bjarni@2736: bjarni@2736: GameSizeChanged(); bjarni@2736: bjarni@2736: /* Redraw screen */ bjarni@2736: _cocoa_video_data.num_dirty_rects = MAX_DIRTY_RECTS; bjarni@2736: } bjarni@2736: bjarni@2736: - (void)appDidHide:(NSNotification*)note bjarni@2736: { bjarni@2736: _cocoa_video_data.active = false; bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: - (void)appWillUnhide:(NSNotification*)note bjarni@2736: { bjarni@2736: QZ_SetPortAlphaOpaque (); bjarni@2736: bjarni@2736: /* save current visible surface */ bjarni@2736: [ self cacheImageInRect:[ _cocoa_video_data.qdview frame ] ]; bjarni@2736: } bjarni@2736: bjarni@2736: - (void)appDidUnhide:(NSNotification*)note bjarni@2736: { bjarni@2736: /* restore cached image, since it may not be current, post expose event too */ bjarni@2736: [ self restoreCachedImage ]; bjarni@2736: bjarni@2736: _cocoa_video_data.active = true; bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: - (id)initWithContentRect:(NSRect)contentRect styleMask:(unsigned int)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag bjarni@2736: { bjarni@2736: /* Make our window subclass receive these application notifications */ bjarni@2736: [ [ NSNotificationCenter defaultCenter ] addObserver:self bjarni@2736: selector:@selector(appDidHide:) name:NSApplicationDidHideNotification object:NSApp ]; bjarni@2736: bjarni@2736: [ [ NSNotificationCenter defaultCenter ] addObserver:self bjarni@2736: selector:@selector(appDidUnhide:) name:NSApplicationDidUnhideNotification object:NSApp ]; bjarni@2736: bjarni@2736: [ [ NSNotificationCenter defaultCenter ] addObserver:self bjarni@2736: selector:@selector(appWillUnhide:) name:NSApplicationWillUnhideNotification object:NSApp ]; bjarni@2736: bjarni@2736: return [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]; bjarni@2736: } bjarni@2736: bjarni@2736: @end bjarni@2736: bjarni@2736: @implementation OTTD_QuartzWindowDelegate bjarni@2736: - (BOOL)windowShouldClose:(id)sender bjarni@2736: { rubidium@4548: HandleExitGameRequest(); bjarni@2736: bjarni@2736: return NO; bjarni@2736: } bjarni@2736: tron@3036: - (void)windowDidBecomeKey:(NSNotification*)aNotification bjarni@2736: { bjarni@2736: _cocoa_video_data.active = true; bjarni@2736: } bjarni@2736: tron@3036: - (void)windowDidResignKey:(NSNotification*)aNotification bjarni@2736: { bjarni@2736: _cocoa_video_data.active = false; bjarni@2736: } bjarni@2736: tron@3036: - (void)windowDidBecomeMain:(NSNotification*)aNotification bjarni@2736: { bjarni@2736: _cocoa_video_data.active = true; bjarni@2736: } bjarni@2736: tron@3036: - (void)windowDidResignMain:(NSNotification*)aNotification bjarni@2736: { bjarni@2736: _cocoa_video_data.active = false; bjarni@2736: } bjarni@2736: bjarni@2736: @end bjarni@2736: bjarni@2736: bjarni@2736: static void QZ_UpdateWindowPalette(uint start, uint count) bjarni@2736: { bjarni@2736: uint i; bjarni@2736: tron@3036: switch (_cocoa_video_data.device_bpp) { bjarni@2736: case 32: bjarni@2736: for (i = start; i < start + count; i++) { tron@3036: uint32 clr32 = 0xff000000; tron@3036: clr32 |= (uint32)_cur_palette[i].r << 16; tron@3036: clr32 |= (uint32)_cur_palette[i].g << 8; tron@3036: clr32 |= (uint32)_cur_palette[i].b; bjarni@2736: _cocoa_video_data.palette32[i] = clr32; bjarni@2736: } bjarni@2736: break; bjarni@2736: case 16: bjarni@2736: for (i = start; i < start + count; i++) { tron@3036: uint16 clr16 = 0x0000; tron@3036: clr16 |= (uint16)((_cur_palette[i].r >> 3) & 0x1f) << 10; tron@3036: clr16 |= (uint16)((_cur_palette[i].g >> 3) & 0x1f) << 5; tron@3036: clr16 |= (uint16)((_cur_palette[i].b >> 3) & 0x1f); bjarni@2736: _cocoa_video_data.palette16[i] = clr16; bjarni@2736: } bjarni@2736: break; bjarni@2736: } bjarni@2736: bjarni@2736: _cocoa_video_data.num_dirty_rects = MAX_DIRTY_RECTS; bjarni@2736: } bjarni@2736: bjarni@3038: static inline void QZ_WindowBlitIndexedPixelsToView32(uint left, uint top, uint right, uint bottom) bjarni@2736: { bjarni@3038: const uint32* pal = _cocoa_video_data.palette32; bjarni@3038: const uint8* src = _cocoa_video_data.pixels; bjarni@3038: uint32* dst = (uint32*)_cocoa_video_data.realpixels; bjarni@3038: uint width = _cocoa_video_data.width; bjarni@3038: uint pitch = _cocoa_video_data.pitch / 4; bjarni@3038: uint x; bjarni@3038: uint y; bjarni@2736: tron@3036: for (y = top; y < bottom; y++) { bjarni@3038: for (x = left; x < right; x++) { bjarni@3038: dst[y * pitch + x] = pal[src[y * width + x]]; bjarni@2736: } bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@3038: static inline void QZ_WindowBlitIndexedPixelsToView16(uint left, uint top, uint right, uint bottom) bjarni@2736: { bjarni@3038: const uint16* pal = _cocoa_video_data.palette16; bjarni@3038: const uint8* src = _cocoa_video_data.pixels; bjarni@3038: uint16* dst = (uint16*)_cocoa_video_data.realpixels; bjarni@3038: uint width = _cocoa_video_data.width; bjarni@3038: uint pitch = _cocoa_video_data.pitch / 2; bjarni@3038: uint x; bjarni@3038: uint y; bjarni@2736: tron@3036: for (y = top; y < bottom; y++) { bjarni@3038: for (x = left; x < right; x++) { bjarni@3038: dst[y * pitch + x] = pal[src[y * width + x]]; bjarni@2736: } bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: static inline void QZ_WindowBlitIndexedPixelsToView(int left, int top, int right, int bottom) bjarni@2736: { tron@3036: switch (_cocoa_video_data.device_bpp) { tron@3036: case 32: QZ_WindowBlitIndexedPixelsToView32(left, top, right, bottom); break; tron@3036: case 16: QZ_WindowBlitIndexedPixelsToView16(left, top, right, bottom); break; bjarni@2736: } bjarni@2736: } bjarni@2736: tron@3036: static bool _resize_icon[] = { tron@3036: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, tron@3036: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, tron@3036: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, tron@3036: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, tron@3036: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, tron@3036: 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, tron@3036: 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, tron@3036: 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, tron@3036: 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, tron@3036: 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, tron@3036: 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, tron@3036: 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, tron@3036: 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, tron@3036: 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, tron@3036: 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, tron@3036: 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0 bjarni@2736: }; bjarni@2736: tron@3036: static void QZ_DrawResizeIcon(void) tron@3036: { tron@3036: int xoff = _cocoa_video_data.width - 16; tron@3036: int yoff = _cocoa_video_data.height - 16; tron@3036: int x; tron@3036: int y; bjarni@2736: tron@3036: for (y = 0; y < 16; y++) { tron@3036: uint16* trg16 = (uint16*)_cocoa_video_data.realpixels + (yoff + y) * _cocoa_video_data.pitch / 2 + xoff; tron@3036: uint32* trg32 = (uint32*)_cocoa_video_data.realpixels + (yoff + y) * _cocoa_video_data.pitch / 4 + xoff; bjarni@2736: tron@3036: for (x = 0; x < 16; x++, trg16++, trg32++) { tron@3036: if (!_resize_icon[y * 16 + x]) continue; tron@3036: tron@3036: switch (_cocoa_video_data.device_bpp) { tron@3036: case 32: *trg32 = 0xff000000; break; tron@3036: case 16: *trg16 = 0x0000; break; bjarni@2736: } bjarni@2736: } bjarni@2736: } bjarni@2736: } bjarni@2736: tron@3036: static void QZ_DrawWindow(void) tron@3036: { bjarni@2736: int i; bjarni@2736: RgnHandle dirty, temp; bjarni@2736: bjarni@2736: /* Check if we need to do anything */ tron@3036: if (_cocoa_video_data.num_dirty_rects == 0 || tron@3036: [ _cocoa_video_data.window isMiniaturized ]) { bjarni@2736: return; tron@3036: } bjarni@2736: tron@3036: if (_cocoa_video_data.num_dirty_rects >= MAX_DIRTY_RECTS) { bjarni@2736: _cocoa_video_data.num_dirty_rects = 1; bjarni@2736: _cocoa_video_data.dirty_rects[0].left = 0; bjarni@2736: _cocoa_video_data.dirty_rects[0].top = 0; bjarni@2736: _cocoa_video_data.dirty_rects[0].right = _cocoa_video_data.width; bjarni@2736: _cocoa_video_data.dirty_rects[0].bottom = _cocoa_video_data.height; bjarni@2736: } bjarni@2736: tron@3036: dirty = NewRgn(); tron@3036: temp = NewRgn(); bjarni@2736: tron@3036: SetEmptyRgn(dirty); bjarni@2736: bjarni@2736: /* Build the region of dirty rectangles */ bjarni@2736: for (i = 0; i < _cocoa_video_data.num_dirty_rects; i++) { tron@3036: QZ_WindowBlitIndexedPixelsToView( tron@3036: _cocoa_video_data.dirty_rects[i].left, tron@3036: _cocoa_video_data.dirty_rects[i].top, tron@3036: _cocoa_video_data.dirty_rects[i].right, tron@3036: _cocoa_video_data.dirty_rects[i].bottom tron@3036: ); bjarni@2736: tron@3036: MacSetRectRgn( tron@3036: temp, tron@3036: _cocoa_video_data.dirty_rects[i].left, tron@3036: _cocoa_video_data.dirty_rects[i].top, tron@3036: _cocoa_video_data.dirty_rects[i].right, tron@3036: _cocoa_video_data.dirty_rects[i].bottom tron@3036: ); tron@3036: MacUnionRgn(dirty, temp, dirty); bjarni@2736: } bjarni@2736: bjarni@2736: QZ_DrawResizeIcon(); bjarni@2736: bjarni@2736: /* Flush the dirty region */ tron@3036: QDFlushPortBuffer([ _cocoa_video_data.qdview qdPort ], dirty); tron@3036: DisposeRgn(dirty); tron@3036: DisposeRgn(temp); bjarni@2736: bjarni@2736: _cocoa_video_data.num_dirty_rects = 0; bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: extern const char _openttd_revision[]; bjarni@2736: tron@3036: static const char* QZ_SetVideoWindowed(uint width, uint height) tron@3036: { bjarni@2736: char caption[50]; bjarni@2736: NSString *nsscaption; bjarni@2736: unsigned int style; bjarni@2736: NSRect contentRect; bjarni@2736: BOOL isCustom = NO; bjarni@2736: tron@3036: if (width > _cocoa_video_data.device_width) bjarni@2736: width = _cocoa_video_data.device_width; tron@3036: if (height > _cocoa_video_data.device_height) bjarni@2736: height = _cocoa_video_data.device_height; bjarni@2736: bjarni@2736: _cocoa_video_data.width = width; bjarni@2736: _cocoa_video_data.height = height; bjarni@2736: tron@3036: contentRect = NSMakeRect(0, 0, width, height); bjarni@2736: tron@3036: /* Check if we should completely destroy the previous mode tron@3036: * - If it is fullscreen tron@3036: */ tron@3036: if (_cocoa_video_data.isset && _cocoa_video_data.fullscreen) tron@3036: QZ_UnsetVideoMode(); bjarni@2736: bjarni@2736: /* Check if we should recreate the window */ bjarni@2736: if (_cocoa_video_data.window == nil) { bjarni@2736: /* Set the window style */ bjarni@2736: style = NSTitledWindowMask; tron@3036: style |= (NSMiniaturizableWindowMask | NSClosableWindowMask); tron@3036: style |= NSResizableWindowMask; bjarni@2736: bjarni@2736: /* Manually create a window, avoids having a nib file resource */ bjarni@2736: _cocoa_video_data.window = [ [ OTTD_QuartzWindow alloc ] bjarni@2736: initWithContentRect:contentRect bjarni@2736: styleMask:style bjarni@2736: backing:NSBackingStoreBuffered bjarni@2736: defer:NO ]; bjarni@2736: bjarni@2736: if (_cocoa_video_data.window == nil) bjarni@2736: return "Could not create the Cocoa window"; bjarni@2736: bjarni@2736: snprintf(caption, sizeof(caption), "OpenTTD %s", _openttd_revision); bjarni@2741: nsscaption = [ [ NSString alloc ] initWithCString:caption ]; bjarni@2736: [ _cocoa_video_data.window setTitle:nsscaption ]; bjarni@2736: [ _cocoa_video_data.window setMiniwindowTitle:nsscaption ]; bjarni@2736: [ nsscaption release ]; bjarni@2736: bjarni@2736: [ _cocoa_video_data.window setAcceptsMouseMovedEvents:YES ]; bjarni@2736: [ _cocoa_video_data.window setViewsNeedDisplay:NO ]; bjarni@2736: bjarni@2736: [ _cocoa_video_data.window setDelegate: [ [ [ OTTD_QuartzWindowDelegate alloc ] init ] autorelease ] ]; tron@3036: } else { tron@3036: /* We already have a window, just change its size */ bjarni@2736: if (!isCustom) { bjarni@2736: [ _cocoa_video_data.window setContentSize:contentRect.size ]; bjarni@2736: [ _cocoa_video_data.qdview setFrameSize:contentRect.size ]; bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: [ _cocoa_video_data.window center ]; bjarni@2736: bjarni@2736: /* Only recreate the view if it doesn't already exist */ bjarni@2736: if (_cocoa_video_data.qdview == nil) { bjarni@2736: _cocoa_video_data.qdview = [ [ NSQuickDrawView alloc ] initWithFrame:contentRect ]; bjarni@2736: [ _cocoa_video_data.qdview setAutoresizingMask: NSViewWidthSizable | NSViewHeightSizable ]; bjarni@2736: [ [ _cocoa_video_data.window contentView ] addSubview:_cocoa_video_data.qdview ]; bjarni@2736: [ _cocoa_video_data.qdview release ]; bjarni@2736: [ _cocoa_video_data.window makeKeyAndOrderFront:nil ]; bjarni@2736: } bjarni@2736: tron@3036: LockPortBits([ _cocoa_video_data.qdview qdPort ]); tron@3036: _cocoa_video_data.realpixels = GetPixBaseAddr(GetPortPixMap([ _cocoa_video_data.qdview qdPort ])); tron@3036: _cocoa_video_data.pitch = GetPixRowBytes(GetPortPixMap([ _cocoa_video_data.qdview qdPort ])); tron@3036: UnlockPortBits([ _cocoa_video_data.qdview qdPort ]); bjarni@2736: tron@3036: /* _cocoa_video_data.realpixels now points to the window's pixels tron@3036: * We want it to point to the *view's* pixels rubidium@4549: */ bjarni@2736: { bjarni@2736: int vOffset = [ _cocoa_video_data.window frame ].size.height - [ _cocoa_video_data.qdview frame ].size.height - [ _cocoa_video_data.qdview frame ].origin.y; bjarni@2736: int hOffset = [ _cocoa_video_data.qdview frame ].origin.x; bjarni@2736: tron@3036: _cocoa_video_data.realpixels = (uint8*)_cocoa_video_data.realpixels + (vOffset * _cocoa_video_data.pitch) + hOffset * (_cocoa_video_data.device_bpp / 8); bjarni@2736: } bjarni@2736: tron@3036: free(_cocoa_video_data.pixels); tron@3036: _cocoa_video_data.pixels = (uint8*)malloc(width * height); tron@3036: if (_cocoa_video_data.pixels == NULL) return "Failed to allocate 8-bit buffer"; bjarni@2736: bjarni@2736: _cocoa_video_data.fullscreen = false; bjarni@2736: bjarni@2736: return NULL; bjarni@2736: } bjarni@2736: bjarni@2736: tron@3036: /****************************************************************************** tron@3036: * Fullscreen mode * tron@3036: ******************************************************************************/ bjarni@2736: tron@3036: /* Gamma functions to try to hide the flash from a rez switch tron@3036: * Fade the display from normal to black tron@3036: * Save gamma tables for fade back to normal tron@3036: */ tron@3037: static uint32 QZ_FadeGammaOut(OTTD_QuartzGammaTable* table) tron@3036: { tron@3036: CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE]; tron@3036: CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE]; tron@3036: CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE]; bjarni@2736: float percent; bjarni@2736: int j; bjarni@2736: unsigned int actual; bjarni@2736: tron@3036: if (CGGetDisplayTransferByTable( tron@3036: _cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE, tron@3036: table->red, table->green, table->blue, &actual tron@3036: ) != CGDisplayNoErr || tron@3036: actual != QZ_GAMMA_TABLE_SIZE) { bjarni@2736: return 1; bjarni@2736: } bjarni@2736: tron@3036: memcpy(redTable, table->red, sizeof(redTable)); tron@3036: memcpy(greenTable, table->green, sizeof(greenTable)); tron@3036: memcpy(blueTable, table->blue, sizeof(greenTable)); bjarni@2736: bjarni@2736: for (percent = 1.0; percent >= 0.0; percent -= 0.01) { bjarni@2736: for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) { bjarni@2736: redTable[j] = redTable[j] * percent; bjarni@2736: greenTable[j] = greenTable[j] * percent; bjarni@2736: blueTable[j] = blueTable[j] * percent; bjarni@2736: } bjarni@2736: tron@3036: if (CGSetDisplayTransferByTable( tron@3036: _cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE, tron@3036: redTable, greenTable, blueTable tron@3036: ) != CGDisplayNoErr) { bjarni@2736: CGDisplayRestoreColorSyncSettings(); bjarni@2736: return 1; bjarni@2736: } bjarni@2736: bjarni@2736: CSleep(10); bjarni@2736: } bjarni@2736: bjarni@2736: return 0; bjarni@2736: } bjarni@2736: tron@3036: /* Fade the display from black to normal tron@3036: * Restore previously saved gamma values tron@3036: */ tron@3036: static uint32 QZ_FadeGammaIn(const OTTD_QuartzGammaTable* table) tron@3036: { tron@3036: CGGammaValue redTable[QZ_GAMMA_TABLE_SIZE]; tron@3036: CGGammaValue greenTable[QZ_GAMMA_TABLE_SIZE]; tron@3036: CGGammaValue blueTable[QZ_GAMMA_TABLE_SIZE]; tron@3036: float percent; tron@3036: int j; tron@3036: tron@3036: memset(redTable, 0, sizeof(redTable)); tron@3036: memset(greenTable, 0, sizeof(greenTable)); tron@3036: memset(blueTable, 0, sizeof(greenTable)); tron@3036: tron@3036: for (percent = 0.0; percent <= 1.0; percent += 0.01) { tron@3036: for (j = 0; j < QZ_GAMMA_TABLE_SIZE; j++) { tron@3036: redTable[j] = table->red[j] * percent; tron@3036: greenTable[j] = table->green[j] * percent; tron@3036: blueTable[j] = table->blue[j] * percent; tron@3036: } tron@3036: tron@3036: if (CGSetDisplayTransferByTable( tron@3036: _cocoa_video_data.display_id, QZ_GAMMA_TABLE_SIZE, tron@3036: redTable, greenTable, blueTable tron@3036: ) != CGDisplayNoErr) { tron@3036: CGDisplayRestoreColorSyncSettings(); tron@3036: return 1; tron@3036: } tron@3036: tron@3036: CSleep(10); tron@3036: } tron@3036: tron@3036: return 0; tron@3036: } tron@3036: tron@3036: static const char* QZ_SetVideoFullScreen(int width, int height) tron@3036: { tron@3036: const char* errstr = "QZ_SetVideoFullScreen error"; bjarni@2736: int exact_match; bjarni@2736: CFNumberRef number; bjarni@2736: int bpp; bjarni@2736: int gamma_error; bjarni@2736: OTTD_QuartzGammaTable gamma_table; bjarni@2736: NSRect screen_rect; bjarni@2736: CGError error; bjarni@2736: NSPoint pt; bjarni@2736: bjarni@2736: /* Destroy any previous mode */ tron@3036: if (_cocoa_video_data.isset) QZ_UnsetVideoMode(); bjarni@2736: bjarni@2736: /* See if requested mode exists */ bjarni@2736: _cocoa_video_data.mode = CGDisplayBestModeForParameters(_cocoa_video_data.display_id, 8, width, height, &exact_match); bjarni@2736: bjarni@2736: /* If the mode wasn't an exact match, check if it has the right bpp, and update width and height */ tron@3036: if (!exact_match) { bjarni@2736: number = CFDictionaryGetValue (_cocoa_video_data.mode, kCGDisplayBitsPerPixel); tron@3036: CFNumberGetValue(number, kCFNumberSInt32Type, &bpp); tron@3036: if (bpp != 8) { bjarni@2736: errstr = "Failed to find display resolution"; bjarni@2736: goto ERR_NO_MATCH; bjarni@2736: } bjarni@2736: tron@3036: number = CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayWidth); tron@3036: CFNumberGetValue(number, kCFNumberSInt32Type, &width); bjarni@2736: tron@3036: number = CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayHeight); tron@3036: CFNumberGetValue(number, kCFNumberSInt32Type, &height); bjarni@2736: } bjarni@2736: bjarni@2736: /* Fade display to zero gamma */ tron@3036: gamma_error = QZ_FadeGammaOut(&gamma_table); bjarni@2736: bjarni@2736: /* Put up the blanking window (a window above all other windows) */ tron@3036: error = CGDisplayCapture(_cocoa_video_data.display_id); bjarni@2736: tron@3036: if (CGDisplayNoErr != error) { bjarni@2736: errstr = "Failed capturing display"; bjarni@2736: goto ERR_NO_CAPTURE; bjarni@2736: } bjarni@2736: bjarni@2736: /* Do the physical switch */ tron@3036: if (CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.mode) != CGDisplayNoErr) { bjarni@2736: errstr = "Failed switching display resolution"; bjarni@2736: goto ERR_NO_SWITCH; bjarni@2736: } bjarni@2736: tron@3036: _cocoa_video_data.realpixels = (uint8*)CGDisplayBaseAddress(_cocoa_video_data.display_id); tron@3036: _cocoa_video_data.pitch = CGDisplayBytesPerRow(_cocoa_video_data.display_id); bjarni@2736: tron@3036: _cocoa_video_data.width = CGDisplayPixelsWide(_cocoa_video_data.display_id); tron@3036: _cocoa_video_data.height = CGDisplayPixelsHigh(_cocoa_video_data.display_id); bjarni@2736: _cocoa_video_data.fullscreen = true; bjarni@2736: bjarni@2736: /* Setup double-buffer emulation */ tron@3036: _cocoa_video_data.pixels = (uint8*)malloc(width * height); tron@3036: if (_cocoa_video_data.pixels == NULL) { bjarni@2736: errstr = "Failed to allocate memory for double buffering"; bjarni@2736: goto ERR_DOUBLEBUF; bjarni@2736: } bjarni@2736: tron@3036: if (!CGDisplayCanSetPalette(_cocoa_video_data.display_id)) { bjarni@2736: errstr = "Not an indexed display mode."; bjarni@2736: goto ERR_NOT_INDEXED; bjarni@2736: } bjarni@2736: bjarni@2736: /* If we don't hide menu bar, it will get events and interrupt the program */ tron@3036: HideMenuBar(); bjarni@2736: bjarni@2736: /* Fade the display to original gamma */ tron@3036: if (!gamma_error) QZ_FadeGammaIn(&gamma_table); bjarni@2736: tron@3036: /* There is a bug in Cocoa where NSScreen doesn't synchronize tron@3036: * with CGDirectDisplay, so the main screen's frame is wrong. tron@3036: * As a result, coordinate translation produces incorrect results. tron@3036: * We can hack around this bug by setting the screen rect ourselves. tron@3036: * This hack should be removed if/when the bug is fixed. tron@3036: */ tron@3036: screen_rect = NSMakeRect(0, 0, width, height); bjarni@2736: [ [ NSScreen mainScreen ] setFrame:screen_rect ]; bjarni@2736: bjarni@2736: /* we're fullscreen, so flag all input states... */ bjarni@2736: _cocoa_video_data.active = true; bjarni@2736: bjarni@2736: bjarni@2736: pt = [ NSEvent mouseLocation ]; tron@3036: pt.y = CGDisplayPixelsHigh(_cocoa_video_data.display_id) - pt.y; tron@3036: if (QZ_MouseIsInsideView(&pt)) QZ_HideMouse(); bjarni@2736: bjarni@2736: return NULL; bjarni@2736: bjarni@2736: /* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */ bjarni@2736: ERR_NOT_INDEXED: bjarni@2736: free(_cocoa_video_data.pixels); bjarni@2736: _cocoa_video_data.pixels = NULL; bjarni@2736: ERR_DOUBLEBUF: tron@3036: CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.save_mode); bjarni@2736: ERR_NO_SWITCH: tron@3036: CGReleaseAllDisplays(); bjarni@2736: ERR_NO_CAPTURE: tron@3036: if (!gamma_error) QZ_FadeGammaIn(&gamma_table); bjarni@2736: ERR_NO_MATCH: bjarni@2736: return errstr; bjarni@2736: } bjarni@2736: bjarni@2736: tron@3036: static void QZ_UpdateFullscreenPalette(uint first_color, uint num_colors) tron@3036: { bjarni@2736: CGTableCount index; bjarni@2736: CGDeviceColor color; bjarni@2736: bjarni@2736: for (index = first_color; index < first_color+num_colors; index++) { bjarni@2736: /* Clamp colors between 0.0 and 1.0 */ bjarni@2736: color.red = _cur_palette[index].r / 255.0; bjarni@2736: color.blue = _cur_palette[index].b / 255.0; bjarni@2736: color.green = _cur_palette[index].g / 255.0; bjarni@2736: tron@3036: CGPaletteSetColorAtIndex(_cocoa_video_data.palette, color, index); bjarni@2736: } bjarni@2736: tron@3036: CGDisplaySetPalette(_cocoa_video_data.display_id, _cocoa_video_data.palette); bjarni@2736: } bjarni@2736: bjarni@2736: /* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */ bjarni@2736: static void QZ_WaitForVerticalBlank(void) bjarni@2736: { bjarni@2736: /* The VBL delay is based on Ian Ollmann's RezLib */ bjarni@2736: double refreshRate; bjarni@2736: double linesPerSecond; bjarni@2736: double target; bjarni@2736: double position; bjarni@2736: double adjustment; bjarni@2736: CFNumberRef refreshRateCFNumber; bjarni@2736: tron@3036: refreshRateCFNumber = CFDictionaryGetValue(_cocoa_video_data.mode, kCGDisplayRefreshRate); tron@3036: if (refreshRateCFNumber == NULL) return; tron@3036: tron@3036: if (CFNumberGetValue(refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) == 0) bjarni@2736: return; bjarni@2736: tron@3036: if (refreshRate == 0) return; bjarni@2736: bjarni@2736: linesPerSecond = refreshRate * _cocoa_video_data.height; bjarni@2736: target = _cocoa_video_data.height; bjarni@2736: bjarni@2736: /* Figure out the first delay so we start off about right */ tron@3036: position = CGDisplayBeamPosition(_cocoa_video_data.display_id); tron@3036: if (position > target) position = 0; bjarni@2736: bjarni@2736: adjustment = (target - position) / linesPerSecond; bjarni@2736: tron@3036: CSleep((uint32)(adjustment * 1000)); bjarni@2736: } bjarni@2736: tron@4475: bjarni@2736: static void QZ_DrawScreen(void) bjarni@2736: { tron@4475: const uint8* src = _cocoa_video_data.pixels; tron@4475: uint8* dst = (uint8*)_cocoa_video_data.realpixels; tron@4475: uint pitch = _cocoa_video_data.pitch; tron@4475: uint width = _cocoa_video_data.width; tron@4475: uint num_dirty = _cocoa_video_data.num_dirty_rects; bjarni@3327: uint i; bjarni@2736: bjarni@3327: /* Check if we need to do anything */ tron@4475: if (num_dirty == 0) return; bjarni@3128: tron@4475: if (num_dirty >= MAX_DIRTY_RECTS) { tron@4475: num_dirty = 1; tron@4475: _cocoa_video_data.dirty_rects[0].left = 0; tron@4475: _cocoa_video_data.dirty_rects[0].top = 0; tron@4475: _cocoa_video_data.dirty_rects[0].right = _cocoa_video_data.width; bjarni@3327: _cocoa_video_data.dirty_rects[0].bottom = _cocoa_video_data.height; bjarni@3327: } bjarni@3128: bjarni@3327: QZ_WaitForVerticalBlank(); bjarni@3327: /* Build the region of dirty rectangles */ tron@4475: for (i = 0; i < num_dirty; i++) { tron@4475: uint y = _cocoa_video_data.dirty_rects[i].top; tron@4475: uint left = _cocoa_video_data.dirty_rects[i].left; tron@4475: uint length = _cocoa_video_data.dirty_rects[i].right - left; tron@4475: uint bottom = _cocoa_video_data.dirty_rects[i].bottom; bjarni@3327: tron@4475: for (; y < bottom; y++) { tron@4475: memcpy(dst + y * pitch + left, src + y * width + left, length); tron@4475: } bjarni@3081: } bjarni@3327: bjarni@3327: _cocoa_video_data.num_dirty_rects = 0; tron@4475: } bjarni@3327: bjarni@2736: tron@3036: static int QZ_ListFullscreenModes(OTTDPoint* mode_list, int max_modes) tron@3036: { bjarni@2736: CFIndex num_modes; bjarni@2736: CFIndex i; bjarni@2736: int list_size = 0; bjarni@2736: tron@3036: num_modes = CFArrayGetCount(_cocoa_video_data.mode_list); bjarni@2736: bjarni@2736: /* Build list of modes with the requested bpp */ bjarni@2736: for (i = 0; i < num_modes && list_size < max_modes; i++) { bjarni@2736: CFDictionaryRef onemode; bjarni@2736: CFNumberRef number; bjarni@2736: int bpp; bjarni@2736: int intvalue; bjarni@2736: bool hasMode; bjarni@2736: uint16 width, height; bjarni@2736: tron@3036: onemode = CFArrayGetValueAtIndex(_cocoa_video_data.mode_list, i); tron@3036: number = CFDictionaryGetValue(onemode, kCGDisplayBitsPerPixel); bjarni@2736: CFNumberGetValue (number, kCFNumberSInt32Type, &bpp); bjarni@2736: tron@3036: if (bpp != 8) continue; bjarni@2736: tron@3036: number = CFDictionaryGetValue(onemode, kCGDisplayWidth); tron@3036: CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue); tron@3036: width = (uint16)intvalue; bjarni@2736: tron@3036: number = CFDictionaryGetValue(onemode, kCGDisplayHeight); tron@3036: CFNumberGetValue(number, kCFNumberSInt32Type, &intvalue); tron@3036: height = (uint16)intvalue; bjarni@2736: bjarni@2736: /* Check if mode is already in the list */ bjarni@2736: { bjarni@2736: int i; bjarni@2736: hasMode = false; bjarni@2736: for (i = 0; i < list_size; i++) { bjarni@2736: if (mode_list[i].x == width && mode_list[i].y == height) { bjarni@2736: hasMode = true; bjarni@2736: break; bjarni@2736: } bjarni@2736: } bjarni@2736: } bjarni@2736: tron@3036: if (hasMode) continue; bjarni@2736: bjarni@2736: /* Add mode to the list */ bjarni@2736: mode_list[list_size].x = width; bjarni@2736: mode_list[list_size].y = height; bjarni@2736: list_size++; bjarni@2736: } bjarni@2736: bjarni@2736: /* Sort list smallest to largest */ bjarni@2736: { bjarni@2736: int i, j; bjarni@2736: for (i = 0; i < list_size; i++) { bjarni@2736: for (j = 0; j < list_size-1; j++) { tron@3036: if (mode_list[j].x > mode_list[j + 1].x || ( tron@3036: mode_list[j].x == mode_list[j + 1].x && tron@3036: mode_list[j].y > mode_list[j + 1].y tron@3036: )) { bjarni@2736: uint tmpw = mode_list[j].x; bjarni@2736: uint tmph = mode_list[j].y; bjarni@2736: tron@3036: mode_list[j].x = mode_list[j + 1].x; tron@3036: mode_list[j].y = mode_list[j + 1].y; bjarni@2736: tron@3036: mode_list[j + 1].x = tmpw; tron@3036: mode_list[j + 1].y = tmph; bjarni@2736: } bjarni@2736: } bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: return list_size; bjarni@2736: } bjarni@2736: bjarni@2736: tron@3036: /****************************************************************************** tron@3036: * Windowed and fullscreen common code * tron@3036: ******************************************************************************/ bjarni@2736: bjarni@2736: static void QZ_UpdatePalette(uint start, uint count) bjarni@2736: { tron@3036: if (_cocoa_video_data.fullscreen) { bjarni@2736: QZ_UpdateFullscreenPalette(start, count); bjarni@2736: } else { bjarni@2736: QZ_UpdateWindowPalette(start, count); bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: static void QZ_InitPalette(void) bjarni@2736: { bjarni@2736: QZ_UpdatePalette(0, 256); bjarni@2736: } bjarni@2736: bjarni@2736: static void QZ_Draw(void) bjarni@2736: { tron@3036: if (_cocoa_video_data.fullscreen) { bjarni@2736: QZ_DrawScreen(); bjarni@2736: } else { bjarni@2736: QZ_DrawWindow(); bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: static const OTTDPoint _default_resolutions[] = { bjarni@2736: { 640, 480}, bjarni@2736: { 800, 600}, bjarni@2736: {1024, 768}, bjarni@2736: {1152, 864}, bjarni@2736: {1280, 800}, bjarni@2736: {1280, 960}, bjarni@2736: {1280, 1024}, bjarni@2736: {1400, 1050}, bjarni@2736: {1600, 1200}, bjarni@2736: {1680, 1050}, bjarni@2736: {1920, 1200} bjarni@2736: }; bjarni@2736: bjarni@2736: static void QZ_UpdateVideoModes(void) bjarni@2736: { bjarni@2736: uint i, j, count; bjarni@2736: OTTDPoint modes[32]; bjarni@2736: const OTTDPoint *current_modes; bjarni@2736: tron@3036: if (_cocoa_video_data.fullscreen) { bjarni@2736: count = QZ_ListFullscreenModes(modes, 32); bjarni@2736: current_modes = modes; bjarni@2736: } else { bjarni@2736: count = lengthof(_default_resolutions); bjarni@2736: current_modes = _default_resolutions; bjarni@2736: } bjarni@2736: tron@3036: for (i = 0, j = 0; j < lengthof(_resolutions) && i < count; i++) { tron@3036: if (_cocoa_video_data.fullscreen || ( bjarni@3316: (uint)current_modes[i].x < _cocoa_video_data.device_width && bjarni@3316: (uint)current_modes[i].y < _cocoa_video_data.device_height) tron@3036: ) { bjarni@2736: _resolutions[j][0] = current_modes[i].x; bjarni@2736: _resolutions[j][1] = current_modes[i].y; bjarni@2736: j++; bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: _num_resolutions = j; bjarni@2736: } bjarni@2736: tron@3036: static void QZ_UnsetVideoMode(void) tron@3036: { tron@3036: if (_cocoa_video_data.fullscreen) { tron@3036: /* Release fullscreen resources */ bjarni@2736: OTTD_QuartzGammaTable gamma_table; bjarni@2736: int gamma_error; bjarni@2736: NSRect screen_rect; bjarni@2736: tron@3036: gamma_error = QZ_FadeGammaOut(&gamma_table); bjarni@2736: bjarni@2736: /* Restore original screen resolution/bpp */ tron@3036: CGDisplaySwitchToMode(_cocoa_video_data.display_id, _cocoa_video_data.save_mode); tron@3036: CGReleaseAllDisplays(); tron@3036: ShowMenuBar(); tron@3036: /* Reset the main screen's rectangle tron@3036: * See comment in QZ_SetVideoFullscreen for why we do this tron@3036: */ bjarni@2736: screen_rect = NSMakeRect(0,0,_cocoa_video_data.device_width,_cocoa_video_data.device_height); bjarni@2736: [ [ NSScreen mainScreen ] setFrame:screen_rect ]; bjarni@2736: tron@3036: if (!gamma_error) QZ_FadeGammaIn(&gamma_table); tron@3036: } else { tron@3036: /* Release window mode resources */ bjarni@2736: [ _cocoa_video_data.window close ]; bjarni@2736: _cocoa_video_data.window = nil; bjarni@2736: _cocoa_video_data.qdview = nil; bjarni@2736: } bjarni@2736: bjarni@2736: free(_cocoa_video_data.pixels); bjarni@2736: _cocoa_video_data.pixels = NULL; bjarni@2736: bjarni@2736: /* Signal successful teardown */ bjarni@2736: _cocoa_video_data.isset = false; egladil@3317: egladil@3317: QZ_ShowMouse(); bjarni@2736: } bjarni@2736: bjarni@2736: tron@3036: static const char* QZ_SetVideoMode(uint width, uint height, bool fullscreen) tron@3036: { bjarni@2736: const char *ret; bjarni@2736: tron@3036: _cocoa_video_data.issetting = true; tron@3036: if (fullscreen) { tron@3036: /* Setup full screen video */ tron@3036: ret = QZ_SetVideoFullScreen(width, height); tron@3036: } else { tron@3036: /* Setup windowed video */ tron@3036: ret = QZ_SetVideoWindowed(width, height); bjarni@2736: } tron@3036: _cocoa_video_data.issetting = false; tron@3036: if (ret != NULL) return ret; bjarni@2736: bjarni@2736: /* Signal successful completion (used internally) */ bjarni@2736: _cocoa_video_data.isset = true; bjarni@2736: bjarni@2736: /* Tell the game that the resolution has changed */ bjarni@2736: _screen.width = _cocoa_video_data.width; bjarni@2736: _screen.height = _cocoa_video_data.height; bjarni@2736: _screen.pitch = _cocoa_video_data.width; bjarni@2736: bjarni@2736: QZ_UpdateVideoModes(); bjarni@2736: GameSizeChanged(); bjarni@2736: bjarni@2736: QZ_InitPalette(); bjarni@2736: bjarni@2736: return NULL; bjarni@2736: } bjarni@2736: tron@3036: static const char* QZ_SetVideoModeAndRestoreOnFailure(uint width, uint height, bool fullscreen) bjarni@2736: { bjarni@2736: bool wasset = _cocoa_video_data.isset; bjarni@2736: uint32 oldwidth = _cocoa_video_data.width; bjarni@2736: uint32 oldheight = _cocoa_video_data.height; bjarni@2736: bool oldfullscreen = _cocoa_video_data.fullscreen; bjarni@2736: const char *ret; bjarni@2736: bjarni@2736: ret = QZ_SetVideoMode(width, height, fullscreen); tron@3036: if (ret != NULL && wasset) QZ_SetVideoMode(oldwidth, oldheight, oldfullscreen); bjarni@2736: bjarni@2736: return ret; bjarni@2736: } bjarni@2736: tron@3036: static void QZ_VideoInit(void) tron@3036: { bjarni@2736: memset(&_cocoa_video_data, 0, sizeof(_cocoa_video_data)); bjarni@2736: bjarni@2736: /* Initialize the video settings; this data persists between mode switches */ bjarni@2736: _cocoa_video_data.display_id = kCGDirectMainDisplay; tron@3036: _cocoa_video_data.save_mode = CGDisplayCurrentMode(_cocoa_video_data.display_id); tron@3036: _cocoa_video_data.mode_list = CGDisplayAvailableModes(_cocoa_video_data.display_id); tron@3036: _cocoa_video_data.palette = CGPaletteCreateDefaultColorPalette(); bjarni@2736: bjarni@2736: /* Gather some information that is useful to know about the display */ bjarni@2736: /* Maybe this should be moved to QZ_SetVideoMode, in case this is changed after startup */ tron@3036: CFNumberGetValue( tron@3036: CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayBitsPerPixel), tron@3036: kCFNumberSInt32Type, &_cocoa_video_data.device_bpp tron@3036: ); bjarni@2736: tron@3036: CFNumberGetValue( tron@3036: CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayWidth), tron@3036: kCFNumberSInt32Type, &_cocoa_video_data.device_width tron@3036: ); bjarni@2736: tron@3036: CFNumberGetValue( tron@3036: CFDictionaryGetValue(_cocoa_video_data.save_mode, kCGDisplayHeight), tron@3036: kCFNumberSInt32Type, &_cocoa_video_data.device_height tron@3036: ); bjarni@2736: bjarni@2736: _cocoa_video_data.cursor_visible = true; bjarni@2736: bjarni@2736: /* register for sleep notifications so wake from sleep generates SDL_VIDEOEXPOSE */ tron@3036: // QZ_RegisterForSleepNotifications(); bjarni@2736: } bjarni@2736: bjarni@2736: bjarni@2736: /* Convert local coordinate to window server (CoreGraphics) coordinate */ tron@3036: static CGPoint QZ_PrivateLocalToCG(NSPoint* p) tron@3036: { bjarni@2736: CGPoint cgp; bjarni@2736: tron@3036: if (!_cocoa_video_data.fullscreen) { bjarni@2736: *p = [ _cocoa_video_data.qdview convertPoint:*p toView: nil ]; bjarni@2736: *p = [ _cocoa_video_data.window convertBaseToScreen:*p ]; bjarni@2736: p->y = _cocoa_video_data.device_height - p->y; bjarni@2736: } bjarni@2736: bjarni@2736: cgp.x = p->x; bjarni@2736: cgp.y = p->y; bjarni@2736: bjarni@2736: return cgp; bjarni@2736: } bjarni@2736: tron@3036: static void QZ_WarpCursor(int x, int y) tron@3036: { bjarni@2736: NSPoint p; bjarni@2736: CGPoint cgp; bjarni@2736: bjarni@2736: /* Only allow warping when in foreground */ tron@3036: if (![ NSApp isActive ]) return; bjarni@2736: tron@3036: p = NSMakePoint(x, y); tron@3036: cgp = QZ_PrivateLocalToCG(&p); bjarni@2736: bjarni@2736: /* this is the magic call that fixes cursor "freezing" after warp */ tron@3036: CGSetLocalEventsSuppressionInterval(0.0); bjarni@2736: /* Do the actual warp */ tron@3036: CGWarpMouseCursorPosition(cgp); bjarni@2736: bjarni@2736: /* Generate the mouse moved event */ bjarni@2736: } bjarni@2736: tron@3036: static void QZ_ShowMouse(void) tron@3036: { bjarni@2736: if (!_cocoa_video_data.cursor_visible) { bjarni@2736: [ NSCursor unhide ]; bjarni@2736: _cocoa_video_data.cursor_visible = true; egladil@3317: egladil@3317: // Hide the openttd cursor when leaving the window egladil@3317: if (_cocoa_video_data.isset) egladil@3317: UndrawMouseCursor(); egladil@3317: _cursor.in_window = false; bjarni@2736: } bjarni@2736: } bjarni@2736: tron@3036: static void QZ_HideMouse(void) tron@3036: { bjarni@2736: if (_cocoa_video_data.cursor_visible) { bjarni@2736: #ifndef _DEBUG bjarni@2736: [ NSCursor hide ]; bjarni@2736: #endif bjarni@2736: _cocoa_video_data.cursor_visible = false; egladil@3317: egladil@3317: // Show the openttd cursor again egladil@3317: _cursor.in_window = true; bjarni@2736: } bjarni@2736: } bjarni@2736: bjarni@2736: tron@3036: /****************************************************************************** tron@3036: * OS X application creation * tron@3036: ******************************************************************************/ bjarni@2736: bjarni@2736: /* The main class of the application, the application's delegate */ bjarni@2736: @implementation OTTDMain bjarni@2736: /* Called when the internal event loop has just started running */ tron@3036: - (void) applicationDidFinishLaunching: (NSNotification*) note bjarni@2736: { truelight@2827: /* Hand off to main application code */ bjarni@2736: QZ_GameLoop(); bjarni@2736: bjarni@2736: /* We're done, thank you for playing */ bjarni@2736: [ NSApp stop:_ottd_main ]; bjarni@2736: } bjarni@2736: bjarni@2736: /* Display the in game quit confirmation dialog */ tron@3036: - (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*) sender bjarni@2736: { bjarni@2736: rubidium@4550: HandleExitGameRequest(); bjarni@2736: rubidium@4434: return NSTerminateCancel; // NSTerminateLater ? bjarni@2736: } bjarni@2736: @end bjarni@2736: bjarni@2736: static void setApplicationMenu(void) bjarni@2736: { bjarni@2736: /* warning: this code is very odd */ bjarni@2736: NSMenu *appleMenu; bjarni@2736: NSMenuItem *menuItem; bjarni@2736: NSString *title; bjarni@2736: NSString *appName; bjarni@2736: bjarni@2736: appName = @"OTTD"; bjarni@2736: appleMenu = [[NSMenu alloc] initWithTitle:appName]; bjarni@2736: bjarni@2736: /* Add menu items */ bjarni@2736: title = [@"About " stringByAppendingString:appName]; bjarni@2736: [appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@""]; bjarni@2736: bjarni@2736: [appleMenu addItem:[NSMenuItem separatorItem]]; bjarni@2736: bjarni@2736: title = [@"Hide " stringByAppendingString:appName]; bjarni@2736: [appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h"]; bjarni@2736: tron@3036: menuItem = (NSMenuItem*)[appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h"]; bjarni@2736: [menuItem setKeyEquivalentModifierMask:(NSAlternateKeyMask|NSCommandKeyMask)]; bjarni@2736: bjarni@2736: [appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@""]; bjarni@2736: bjarni@2736: [appleMenu addItem:[NSMenuItem separatorItem]]; bjarni@2736: bjarni@2736: title = [@"Quit " stringByAppendingString:appName]; bjarni@2736: [appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q"]; bjarni@2736: bjarni@2736: bjarni@2736: /* Put menu into the menubar */ bjarni@2736: menuItem = [[NSMenuItem alloc] initWithTitle:@"" action:nil keyEquivalent:@""]; bjarni@2736: [menuItem setSubmenu:appleMenu]; bjarni@2736: [[NSApp mainMenu] addItem:menuItem]; bjarni@2736: bjarni@2736: /* Tell the application object that this is now the application menu */ bjarni@2736: [NSApp setAppleMenu:appleMenu]; bjarni@2736: bjarni@2736: /* Finally give up our references to the objects */ bjarni@2736: [appleMenu release]; bjarni@2736: [menuItem release]; bjarni@2736: } bjarni@2736: bjarni@2736: /* Create a window menu */ bjarni@2736: static void setupWindowMenu(void) bjarni@2736: { tron@3036: NSMenu* windowMenu; tron@3036: NSMenuItem* windowMenuItem; tron@3036: NSMenuItem* menuItem; bjarni@2736: bjarni@2736: windowMenu = [[NSMenu alloc] initWithTitle:@"Window"]; bjarni@2736: bjarni@2736: /* "Minimize" item */ bjarni@2736: menuItem = [[NSMenuItem alloc] initWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m"]; bjarni@2736: [windowMenu addItem:menuItem]; bjarni@2736: [menuItem release]; bjarni@2736: bjarni@2736: /* Put menu into the menubar */ bjarni@2736: windowMenuItem = [[NSMenuItem alloc] initWithTitle:@"Window" action:nil keyEquivalent:@""]; bjarni@2736: [windowMenuItem setSubmenu:windowMenu]; bjarni@2736: [[NSApp mainMenu] addItem:windowMenuItem]; bjarni@2736: bjarni@2736: /* Tell the application object that this is now the window menu */ bjarni@2736: [NSApp setWindowsMenu:windowMenu]; bjarni@2736: bjarni@2736: /* Finally give up our references to the objects */ bjarni@2736: [windowMenu release]; bjarni@2736: [windowMenuItem release]; bjarni@2736: } bjarni@2736: bjarni@2736: static void setupApplication(void) bjarni@2736: { bjarni@2736: CPSProcessSerNum PSN; bjarni@2736: bjarni@2736: /* Ensure the application object is initialised */ bjarni@2736: [NSApplication sharedApplication]; bjarni@2736: bjarni@2736: /* Tell the dock about us */ tron@3036: if (!CPSGetCurrentProcess(&PSN) && tron@3036: !CPSEnableForegroundOperation(&PSN, 0x03, 0x3C, 0x2C, 0x1103) && tron@3036: !CPSSetFrontProcess(&PSN)) { tron@3036: [NSApplication sharedApplication]; tron@3036: } bjarni@2736: bjarni@2736: /* Set up the menubar */ bjarni@2736: [NSApp setMainMenu:[[NSMenu alloc] init]]; bjarni@2736: setApplicationMenu(); bjarni@2736: setupWindowMenu(); bjarni@2736: bjarni@2736: /* Create OTTDMain and make it the app delegate */ bjarni@2736: _ottd_main = [[OTTDMain alloc] init]; bjarni@2736: [NSApp setDelegate:_ottd_main]; bjarni@2736: } bjarni@2736: bjarni@2736: tron@3036: /****************************************************************************** tron@3036: * Video driver interface * tron@3036: ******************************************************************************/ bjarni@2736: bjarni@2736: static void CocoaVideoStop(void) bjarni@2736: { tron@3036: if (!_cocoa_video_started) return; truelight@2827: tron@3036: if (_cocoa_video_data.isset) QZ_UnsetVideoMode(); bjarni@2736: bjarni@2736: [_ottd_main release]; truelight@2827: truelight@2827: _cocoa_video_started = false; bjarni@2736: } bjarni@2736: bjarni@2736: static const char *CocoaVideoStart(const char * const *parm) bjarni@2736: { bjarni@2736: const char *ret; bjarni@2736: tron@3036: if (_cocoa_video_started) return "Already started"; truelight@2827: _cocoa_video_started = true; truelight@2827: truelight@2827: memset(&_cocoa_video_data, 0, sizeof(_cocoa_video_data)); truelight@2827: bjarni@2736: setupApplication(); bjarni@2736: truelight@2827: /* Don't create a window or enter fullscreen if we're just going to show a dialog. */ tron@3036: if (_cocoa_video_dialog) return NULL; truelight@2827: bjarni@2736: QZ_VideoInit(); bjarni@2736: bjarni@2736: ret = QZ_SetVideoMode(_cur_resolution[0], _cur_resolution[1], _fullscreen); tron@3036: if (ret != NULL) CocoaVideoStop(); bjarni@2736: bjarni@2736: return ret; bjarni@2736: } bjarni@2736: bjarni@2736: static void CocoaVideoMakeDirty(int left, int top, int width, int height) bjarni@2736: { bjarni@2736: if (_cocoa_video_data.num_dirty_rects < MAX_DIRTY_RECTS) { bjarni@2736: _cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].left = left; bjarni@2736: _cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].top = top; bjarni@2736: _cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].right = left + width; bjarni@2736: _cocoa_video_data.dirty_rects[_cocoa_video_data.num_dirty_rects].bottom = top + height; bjarni@2736: } bjarni@2736: _cocoa_video_data.num_dirty_rects++; bjarni@2736: } bjarni@2736: bjarni@2736: static void CocoaVideoMainLoop(void) bjarni@2736: { bjarni@2736: /* Start the main event loop */ bjarni@2736: [NSApp run]; bjarni@2736: } bjarni@2736: bjarni@2736: static bool CocoaVideoChangeRes(int w, int h) bjarni@2736: { rubidium@5571: const char *ret = QZ_SetVideoModeAndRestoreOnFailure((uint)w, (uint)h, _cocoa_video_data.fullscreen); tron@3036: if (ret != NULL) { rubidium@5571: DEBUG(driver, 0, "cocoa_v: CocoaVideoChangeRes failed with message: %s", ret); bjarni@2736: } bjarni@2736: bjarni@2736: return ret == NULL; bjarni@2736: } bjarni@2736: bjarni@2736: static void CocoaVideoFullScreen(bool full_screen) bjarni@2736: { rubidium@5571: const char *ret = QZ_SetVideoModeAndRestoreOnFailure(_cocoa_video_data.width, _cocoa_video_data.height, full_screen); tron@3036: if (ret != NULL) { rubidium@5571: DEBUG(driver, 0, "cocoa_v: CocoaVideoFullScreen failed with message: %s", ret); bjarni@2736: } bjarni@2736: bjarni@2736: _fullscreen = _cocoa_video_data.fullscreen; bjarni@2736: } bjarni@2736: bjarni@2736: const HalVideoDriver _cocoa_video_driver = { bjarni@2736: CocoaVideoStart, bjarni@2736: CocoaVideoStop, bjarni@2736: CocoaVideoMakeDirty, bjarni@2736: CocoaVideoMainLoop, bjarni@2736: CocoaVideoChangeRes, bjarni@2736: CocoaVideoFullScreen, bjarni@2736: }; bjarni@2736: bjarni@2736: truelight@2827: /* This is needed since sometimes assert is called before the videodriver is initialized */ tron@3036: void CocoaDialog(const char* title, const char* message, const char* buttonLabel) truelight@2827: { truelight@2827: bool wasstarted; truelight@2827: truelight@2827: _cocoa_video_dialog = true; truelight@2827: truelight@2827: wasstarted = _cocoa_video_started; tron@3036: if (!_cocoa_video_started && CocoaVideoStart(NULL) != NULL) { truelight@2827: fprintf(stderr, "%s: %s\n", title, message); truelight@2827: return; truelight@2827: } truelight@2827: truelight@2827: NSRunAlertPanel([NSString stringWithCString: title], [NSString stringWithCString: message], [NSString stringWithCString: buttonLabel], nil, nil); truelight@2827: tron@3036: if (!wasstarted) CocoaVideoStop(); truelight@2827: truelight@2827: _cocoa_video_dialog = false; truelight@2827: } truelight@2827: truelight@2827: bjarni@2736: /* This is needed since OS X applications are started with the working dir set to / when double-clicked */ bjarni@2736: void cocoaSetWorkingDirectory(void) bjarni@2736: { bjarni@2736: char parentdir[MAXPATHLEN]; bjarni@2736: int chdir_ret; bjarni@2736: CFURLRef url = CFBundleCopyBundleURL(CFBundleGetMainBundle()); bjarni@2736: CFURLRef url2 = CFURLCreateCopyDeletingLastPathComponent(0, url); tron@3036: if (CFURLGetFileSystemRepresentation(url2, true, (unsigned char*)parentdir, MAXPATHLEN)) { tron@3036: chdir_ret = chdir(parentdir); /* chdir to the binary app's parent */ bjarni@2736: assert(chdir_ret == 0); bjarni@2736: } bjarni@2736: CFRelease(url); bjarni@2736: CFRelease(url2); bjarni@2736: } bjarni@2736: bjarni@2736: /* These are called from main() to prevent a _NSAutoreleaseNoPool error when bjarni@2736: * exiting before the cocoa video driver has been loaded bjarni@2736: */ bjarni@2736: void cocoaSetupAutoreleasePool(void) bjarni@2736: { bjarni@2736: _ottd_autorelease_pool = [[NSAutoreleasePool alloc] init]; bjarni@2736: } bjarni@2736: bjarni@2736: void cocoaReleaseAutoreleasePool(void) bjarni@2736: { bjarni@2736: [_ottd_autorelease_pool release]; bjarni@2736: } bjarni@2736: bjarni@2736: #endif /* WITH_COCOA */