src/video/cocoa/event.mm
branchNewGRF_ports
changeset 6871 5a9dc001e1ad
child 6872 1c4a4a609f85
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/src/video/cocoa/event.mm	Mon Dec 03 23:39:38 2007 +0000
@@ -0,0 +1,705 @@
+/* $Id$ */
+
+/******************************************************************************
+ *                             Cocoa video driver                             *
+ * Known things left to do:                                                   *
+ *  Nothing at the moment.                                                    *
+ ******************************************************************************/
+
+#ifdef WITH_COCOA
+
+#define MAC_OS_X_VERSION_MIN_REQUIRED    MAC_OS_X_VERSION_10_3
+#include <AvailabilityMacros.h>
+
+#import <Cocoa/Cocoa.h>
+#import <sys/time.h> /* gettimeofday */
+#import <sys/param.h> /* for MAXPATHLEN */
+#import <unistd.h>
+
+/**
+ * Important notice regarding all modifications!!!!!!!
+ * There are certain limitations because the file is objective C++.
+ * gdb has limitations.
+ * C++ and objective C code can't be joined in all cases (classes stuff).
+ * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information.
+ */
+
+
+/* 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 "../../os/macosx/splash.h"
+#include "../../variables.h"
+#include "cocoa_v.h"
+#include "cocoa_keys.h"
+#include "../../blitter/factory.hpp"
+
+#undef Point
+#undef Rect
+
+/* Right Mouse Button Emulation enum */
+enum {
+	RMBE_COMMAND,
+	RMBE_CONTROL,
+	RMBE_OFF,
+};
+
+
+static bool _show_mouse = true;
+static unsigned int _current_mods;
+static bool _tab_is_down;
+static bool _emulating_right_button;
+#ifdef _DEBUG
+static uint32 _tEvent;
+#endif
+
+
+static uint32 GetTick()
+{
+	struct timeval tim;
+
+	gettimeofday(&tim, NULL);
+	return tim.tv_usec / 1000 + tim.tv_sec * 1000;
+}
+
+
+void QZ_ShowMouse()
+{
+	if (!_show_mouse) {
+		[ NSCursor unhide ];
+		_show_mouse = true;
+
+		// Hide the openttd cursor when leaving the window
+		if (_cocoa_subdriver != NULL)
+			UndrawMouseCursor();
+		_cursor.in_window = false;
+	}
+}
+
+void QZ_HideMouse()
+{
+	if (_show_mouse) {
+		/*
+		 * Don't hide the cursor when compiling in debug mode.
+		 * Note: Not hiding the cursor will cause artefacts around it in 8bpp fullscreen mode.
+		 */
+#ifndef _DEBUG
+		[ NSCursor hide ];
+#endif
+		_show_mouse = false;
+
+		// Show the openttd cursor again
+		_cursor.in_window = true;
+	}
+}
+
+static void QZ_WarpCursor(int x, int y)
+{
+	NSPoint p;
+	CGPoint cgp;
+
+	assert(_cocoa_subdriver);
+
+	/* Only allow warping when in foreground */
+	if (![ NSApp isActive ]) return;
+
+	p = NSMakePoint(x, y);
+	cgp = _cocoa_subdriver->PrivateLocalToCG(&p);
+
+	/* this is the magic call that fixes cursor "freezing" after warp */
+	CGSetLocalEventsSuppressionInterval(0.0);
+	/* Do the actual warp */
+	CGWarpMouseCursorPosition(cgp);
+
+	/* Generate the mouse moved event */
+}
+
+
+static void QZ_CheckPaletteAnim()
+{
+	if (_pal_count_dirty != 0) {
+		Blitter *blitter = BlitterFactoryBase::GetCurrentBlitter();
+
+		switch (blitter->UsePaletteAnimation()) {
+			case Blitter::PALETTE_ANIMATION_VIDEO_BACKEND:
+				_cocoa_subdriver->UpdatePalette(_pal_first_dirty, _pal_count_dirty);
+				break;
+
+			case Blitter::PALETTE_ANIMATION_BLITTER:
+				blitter->PaletteAnimate(_pal_first_dirty, _pal_count_dirty);
+				break;
+
+			case Blitter::PALETTE_ANIMATION_NONE:
+				break;
+
+			default:
+				NOT_REACHED();
+		}
+		_pal_count_dirty = 0;
+	}
+}
+
+
+
+struct VkMapping {
+	unsigned short vk_from;
+	byte map_to;
+};
+
+#define AS(x, z) {x, z}
+
+static const VkMapping _vk_mapping[] = {
+	AS(QZ_BACKQUOTE,  WKC_BACKQUOTE), // key left of '1'
+	AS(QZ_BACKQUOTE2, WKC_BACKQUOTE), // some keyboards have it on another scancode
+
+	/* Pageup stuff + up/down */
+	AS(QZ_PAGEUP,   WKC_PAGEUP),
+	AS(QZ_PAGEDOWN, WKC_PAGEDOWN),
+
+	AS(QZ_UP,    WKC_UP),
+	AS(QZ_DOWN,  WKC_DOWN),
+	AS(QZ_LEFT,  WKC_LEFT),
+	AS(QZ_RIGHT, WKC_RIGHT),
+
+	AS(QZ_HOME, WKC_HOME),
+	AS(QZ_END,  WKC_END),
+
+	AS(QZ_INSERT, WKC_INSERT),
+	AS(QZ_DELETE, WKC_DELETE),
+
+	/* Letters. QZ_[a-z] is not in numerical order so we can't use AM(...) */
+	AS(QZ_a, 'A'),
+	AS(QZ_b, 'B'),
+	AS(QZ_c, 'C'),
+	AS(QZ_d, 'D'),
+	AS(QZ_e, 'E'),
+	AS(QZ_f, 'F'),
+	AS(QZ_g, 'G'),
+	AS(QZ_h, 'H'),
+	AS(QZ_i, 'I'),
+	AS(QZ_j, 'J'),
+	AS(QZ_k, 'K'),
+	AS(QZ_l, 'L'),
+	AS(QZ_m, 'M'),
+	AS(QZ_n, 'N'),
+	AS(QZ_o, 'O'),
+	AS(QZ_p, 'P'),
+	AS(QZ_q, 'Q'),
+	AS(QZ_r, 'R'),
+	AS(QZ_s, 'S'),
+	AS(QZ_t, 'T'),
+	AS(QZ_u, 'U'),
+	AS(QZ_v, 'V'),
+	AS(QZ_w, 'W'),
+	AS(QZ_x, 'X'),
+	AS(QZ_y, 'Y'),
+	AS(QZ_z, 'Z'),
+	/* Same thing for digits */
+	AS(QZ_0, '0'),
+	AS(QZ_1, '1'),
+	AS(QZ_2, '2'),
+	AS(QZ_3, '3'),
+	AS(QZ_4, '4'),
+	AS(QZ_5, '5'),
+	AS(QZ_6, '6'),
+	AS(QZ_7, '7'),
+	AS(QZ_8, '8'),
+	AS(QZ_9, '9'),
+
+	AS(QZ_ESCAPE,    WKC_ESC),
+	AS(QZ_PAUSE,     WKC_PAUSE),
+	AS(QZ_BACKSPACE, WKC_BACKSPACE),
+
+	AS(QZ_SPACE,  WKC_SPACE),
+	AS(QZ_RETURN, WKC_RETURN),
+	AS(QZ_TAB,    WKC_TAB),
+
+	/* Function keys */
+	AS(QZ_F1,  WKC_F1),
+	AS(QZ_F2,  WKC_F2),
+	AS(QZ_F3,  WKC_F3),
+	AS(QZ_F4,  WKC_F4),
+	AS(QZ_F5,  WKC_F5),
+	AS(QZ_F6,  WKC_F6),
+	AS(QZ_F7,  WKC_F7),
+	AS(QZ_F8,  WKC_F8),
+	AS(QZ_F9,  WKC_F9),
+	AS(QZ_F10, WKC_F10),
+	AS(QZ_F11, WKC_F11),
+	AS(QZ_F12, WKC_F12),
+
+	/* Numeric part */
+	AS(QZ_KP0,         WKC_NUM_0),
+	AS(QZ_KP1,         WKC_NUM_1),
+	AS(QZ_KP2,         WKC_NUM_2),
+	AS(QZ_KP3,         WKC_NUM_3),
+	AS(QZ_KP4,         WKC_NUM_4),
+	AS(QZ_KP5,         WKC_NUM_5),
+	AS(QZ_KP6,         WKC_NUM_6),
+	AS(QZ_KP7,         WKC_NUM_7),
+	AS(QZ_KP8,         WKC_NUM_8),
+	AS(QZ_KP9,         WKC_NUM_9),
+	AS(QZ_KP_DIVIDE,   WKC_NUM_DIV),
+	AS(QZ_KP_MULTIPLY, WKC_NUM_MUL),
+	AS(QZ_KP_MINUS,    WKC_NUM_MINUS),
+	AS(QZ_KP_PLUS,     WKC_NUM_PLUS),
+	AS(QZ_KP_ENTER,    WKC_NUM_ENTER),
+	AS(QZ_KP_PERIOD,   WKC_NUM_DECIMAL),
+
+	/* Other non-letter keys */
+	AS(QZ_SLASH,        WKC_SLASH),
+	AS(QZ_SEMICOLON,    WKC_SEMICOLON),
+	AS(QZ_EQUALS,       WKC_EQUALS),
+	AS(QZ_LEFTBRACKET,  WKC_L_BRACKET),
+	AS(QZ_BACKSLASH,    WKC_BACKSLASH),
+	AS(QZ_RIGHTBRACKET, WKC_R_BRACKET),
+
+	AS(QZ_QUOTE,   WKC_SINGLEQUOTE),
+	AS(QZ_COMMA,   WKC_COMMA),
+	AS(QZ_MINUS,   WKC_MINUS),
+	AS(QZ_PERIOD,  WKC_PERIOD)
+};
+
+
+static uint32 QZ_MapKey(unsigned short sym)
+{
+	const VkMapping *map;
+	uint32 key = 0;
+
+	for (map = _vk_mapping; map != endof(_vk_mapping); ++map) {
+		if (sym == map->vk_from) {
+			key = map->map_to;
+			break;
+		}
+	}
+
+	if (_current_mods & NSShiftKeyMask)     key |= WKC_SHIFT;
+	if (_current_mods & NSControlKeyMask)   key |= (_patches.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_CTRL : WKC_META);
+	if (_current_mods & NSAlternateKeyMask) key |= WKC_ALT;
+	if (_current_mods & NSCommandKeyMask)   key |= (_patches.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_META : WKC_CTRL);
+
+	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: _tab_is_down = down; break;
+
+		case QZ_RETURN:
+		case QZ_f:
+			if (down && (_current_mods & NSCommandKeyMask)) {
+				_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 (_current_mods == newMods) return;
+
+	/* Iterate through the bits, testing each against the current modifiers */
+	for (i = 0, bit = NSAlphaShiftKeyMask; bit <= NSCommandKeyMask; bit <<= 1, ++i) {
+		unsigned int currentMask, newMask;
+
+		currentMask = _current_mods & bit;
+		newMask     = newMods & bit;
+
+		if (currentMask && currentMask != newMask) { /* modifier up event */
+			/* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */
+			if (bit == NSAlphaShiftKeyMask) QZ_KeyEvent(mapping[i], 0, YES);
+			QZ_KeyEvent(mapping[i], 0, NO);
+		} else if (newMask && currentMask != newMask) { /* modifier down event */
+			QZ_KeyEvent(mapping[i], 0, YES);
+			/* If this was Caps Lock, we need some additional voodoo to make SDL happy (is this needed in ottd?) */
+			if (bit == NSAlphaShiftKeyMask) QZ_KeyEvent(mapping[i], 0, NO);
+		}
+	}
+
+	_current_mods = newMods;
+}
+
+static void QZ_MouseMovedEvent(int x, int y)
+{
+	if (_cursor.fix_at) {
+		int dx = x - _cursor.pos.x;
+		int dy = y - _cursor.pos.y;
+
+		if (dx != 0 || dy != 0) {
+			_cursor.delta.x += dx;
+			_cursor.delta.y += dy;
+
+			QZ_WarpCursor(_cursor.pos.x, _cursor.pos.y);
+		}
+	} else {
+		_cursor.delta.x = x - _cursor.pos.x;
+		_cursor.delta.y = y - _cursor.pos.y;
+		_cursor.pos.x = x;
+		_cursor.pos.y = y;
+		_cursor.dirty = true;
+	}
+	HandleMouseEvents();
+}
+
+
+static void QZ_MouseButtonEvent(int button, BOOL down)
+{
+	switch (button) {
+		case 0:
+			if (down) {
+				_left_button_down = true;
+			} else {
+				_left_button_down = false;
+				_left_button_clicked = false;
+			}
+			HandleMouseEvents();
+			break;
+
+		case 1:
+			if (down) {
+				_right_button_down = true;
+				_right_button_clicked = true;
+			} else {
+				_right_button_down = false;
+			}
+			HandleMouseEvents();
+			break;
+	}
+}
+
+
+
+
+static bool QZ_PollEvent()
+{
+	NSEvent *event;
+	NSPoint pt;
+	NSString *chars;
+#ifdef _DEBUG
+	uint32 et0, et;
+#endif
+
+	assert(_cocoa_subdriver != NULL);
+
+#ifdef _DEBUG
+	et0 = GetTick();
+#endif
+	event = [ NSApp nextEventMatchingMask:NSAnyEventMask
+			untilDate: [ NSDate distantPast ]
+			inMode: NSDefaultRunLoopMode dequeue:YES ];
+#ifdef _DEBUG
+	et = GetTick();
+	_tEvent+= et - et0;
+#endif
+
+	if (event == nil) return false;
+	if (!_cocoa_subdriver->IsActive()) {
+		QZ_ShowMouse();
+		[NSApp sendEvent:event];
+		return true;
+	}
+
+	QZ_DoUnsidedModifiers( [ event modifierFlags ] );
+
+	switch ([event type]) {
+		case NSMouseMoved:
+		case NSOtherMouseDragged:
+		case NSLeftMouseDragged:
+			pt = _cocoa_subdriver->GetMouseLocation(event);
+			if (!_cocoa_subdriver->MouseIsInsideView(&pt) &&
+					!_emulating_right_button) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
+			break;
+
+		case NSRightMouseDragged:
+			pt = _cocoa_subdriver->GetMouseLocation(event);
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
+			break;
+
+		case NSLeftMouseDown:
+		{
+			uint32 keymask = 0;
+			if (_patches.right_mouse_btn_emulation == RMBE_COMMAND) keymask |= NSCommandKeyMask;
+			if (_patches.right_mouse_btn_emulation == RMBE_CONTROL) keymask |= NSControlKeyMask;
+
+			pt = _cocoa_subdriver->GetMouseLocation(event);
+
+			if (!([ event modifierFlags ] & keymask) ||
+					!_cocoa_subdriver->MouseIsInsideView(&pt)) {
+				[NSApp sendEvent:event];
+			}
+
+			if (!_cocoa_subdriver->MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
+
+			/* Right mouse button emulation */
+			if ([ event modifierFlags ] & keymask) {
+				_emulating_right_button = true;
+				QZ_MouseButtonEvent(1, YES);
+			} else {
+				QZ_MouseButtonEvent(0, YES);
+			}
+			break;
+		}
+		case NSLeftMouseUp:
+			[NSApp sendEvent:event];
+
+			pt = _cocoa_subdriver->GetMouseLocation(event);
+			if (!_cocoa_subdriver->MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
+
+			/* Right mouse button emulation */
+			if (_emulating_right_button) {
+				_emulating_right_button = false;
+				QZ_MouseButtonEvent(1, NO);
+			} else {
+				QZ_MouseButtonEvent(0, NO);
+			}
+			break;
+
+		case NSRightMouseDown:
+			pt = _cocoa_subdriver->GetMouseLocation(event);
+			if (!_cocoa_subdriver->MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
+			QZ_MouseButtonEvent(1, YES);
+			break;
+
+		case NSRightMouseUp:
+			pt = _cocoa_subdriver->GetMouseLocation(event);
+			if (!_cocoa_subdriver->MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
+			QZ_MouseButtonEvent(1, NO);
+			break;
+
+#if 0
+		/* This is not needed since openttd currently only use two buttons */
+		case NSOtherMouseDown:
+			pt = QZ_GetMouseLocation(event);
+			if (!QZ_MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
+			QZ_MouseButtonEvent([ event buttonNumber ], YES);
+			break;
+
+		case NSOtherMouseUp:
+			pt = QZ_GetMouseLocation(event);
+			if (!QZ_MouseIsInsideView(&pt)) {
+				QZ_ShowMouse();
+				[NSApp sendEvent:event];
+				break;
+			}
+
+			QZ_HideMouse();
+			QZ_MouseMovedEvent((int)pt.x, (int)pt.y);
+			QZ_MouseButtonEvent([ event buttonNumber ], NO);
+			break;
+#endif
+
+		case NSKeyDown:
+			/* Quit, hide and minimize */
+			switch ([event keyCode]) {
+				case QZ_q:
+				case QZ_h:
+				case QZ_m:
+					if ([ event modifierFlags ] & NSCommandKeyMask) {
+						[NSApp sendEvent:event];
+					}
+					break;
+			}
+
+			chars = [ event characters ];
+			QZ_KeyEvent([event keyCode], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, YES);
+			break;
+
+		case NSKeyUp:
+			/* Quit, hide and minimize */
+			switch ([event keyCode]) {
+				case QZ_q:
+				case QZ_h:
+				case QZ_m:
+					if ([ event modifierFlags ] & NSCommandKeyMask) {
+						[NSApp sendEvent:event];
+					}
+					break;
+			}
+
+			chars = [ event characters ];
+			QZ_KeyEvent([event keyCode], [ chars length ] ? [ chars characterAtIndex:0 ] : 0, NO);
+			break;
+
+		case NSScrollWheel:
+			if ([ event deltaY ] > 0.0) { /* Scroll up */
+				_cursor.wheel--;
+			} else if ([ event deltaY ] < 0.0) { /* Scroll down */
+				_cursor.wheel++;
+			} /* else: deltaY was 0.0 and we don't want to do anything */
+
+			/* Set the scroll count for scrollwheel scrolling */
+			_cursor.h_wheel -= (int)([ event deltaX ]* 5 * _patches.scrollwheel_multiplier);
+			_cursor.v_wheel -= (int)([ event deltaY ]* 5 * _patches.scrollwheel_multiplier);
+			break;
+
+		default:
+			[NSApp sendEvent:event];
+	}
+
+	return true;
+}
+
+
+void QZ_GameLoop()
+{
+	uint32 cur_ticks = GetTick();
+	uint32 last_cur_ticks = cur_ticks;
+	uint32 next_tick = cur_ticks + 30;
+	uint32 pal_tick = 0;
+#ifdef _DEBUG
+	uint32 et0, et, st0, st;
+#endif
+	int i;
+
+#ifdef _DEBUG
+	et0 = GetTick();
+	st = 0;
+#endif
+
+	_screen.dst_ptr = _cocoa_subdriver->GetPixelBuffer();
+	DisplaySplashImage();
+	QZ_CheckPaletteAnim();
+	_cocoa_subdriver->Draw();
+	CSleep(1);
+
+	for (i = 0; i < 2; i++) GameLoop();
+
+	_screen.dst_ptr = _cocoa_subdriver->GetPixelBuffer();
+	UpdateWindows();
+	QZ_CheckPaletteAnim();
+	_cocoa_subdriver->Draw();
+	CSleep(1);
+
+	for (;;) {
+		uint32 prev_cur_ticks = cur_ticks; // to check for wrapping
+		InteractiveRandom(); // randomness
+
+		while (QZ_PollEvent()) {}
+
+		if (_exit_game) break;
+
+#if defined(_DEBUG)
+		if (_current_mods & NSShiftKeyMask)
+#else
+		if (_tab_is_down)
+#endif
+		{
+			if (!_networking && _game_mode != GM_MENU) _fast_forward |= 2;
+		} else if (_fast_forward & 2) {
+			_fast_forward = 0;
+		}
+
+		cur_ticks = GetTick();
+		if (cur_ticks >= next_tick || (_fast_forward && !_pause_game) || cur_ticks < prev_cur_ticks) {
+			_realtime_tick += cur_ticks - last_cur_ticks;
+			last_cur_ticks = cur_ticks;
+			next_tick = cur_ticks + 30;
+
+			_ctrl_pressed = !!(_current_mods & ( _patches.right_mouse_btn_emulation != RMBE_CONTROL ? NSControlKeyMask : NSCommandKeyMask));
+			_shift_pressed = !!(_current_mods & NSShiftKeyMask);
+
+			GameLoop();
+
+			_screen.dst_ptr = _cocoa_subdriver->GetPixelBuffer();
+			UpdateWindows();
+			if (++pal_tick > 4) {
+				QZ_CheckPaletteAnim();
+				pal_tick = 1;
+			}
+			_cocoa_subdriver->Draw();
+		} else {
+#ifdef _DEBUG
+			st0 = GetTick();
+#endif
+			CSleep(1);
+#ifdef _DEBUG
+			st += GetTick() - st0;
+#endif
+			_screen.dst_ptr = _cocoa_subdriver->GetPixelBuffer();
+			DrawChatMessage();
+			DrawMouseCursor();
+			_cocoa_subdriver->Draw();
+		}
+	}
+
+#ifdef _DEBUG
+	et = GetTick();
+
+	DEBUG(driver, 1, "cocoa_v: nextEventMatchingMask took %i ms total", _tEvent);
+	DEBUG(driver, 1, "cocoa_v: game loop took %i ms total (%i ms without sleep)", et - et0, et - et0 - st);
+	DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop total) is %f%%", (double)_tEvent / (double)(et - et0) * 100);
+	DEBUG(driver, 1, "cocoa_v: (nextEventMatchingMask total)/(game loop without sleep total) is %f%%", (double)_tEvent / (double)(et - et0 - st) * 100);
+#endif
+}
+
+#endif /* WITH_COCOA */