changeset 16844:cade9c3e698e draft

(svn r21578) -Fix [FS#2585]: [OSX] A double mouse cursor was shown under certain circumstances (based on patch by matheweis)
author planetmaker <planetmaker@openttd.org>
date Tue, 21 Dec 2010 16:05:25 +0000
parents 6519afe07cae
children a20977db6ead
files src/video/cocoa/cocoa_v.h src/video/cocoa/cocoa_v.mm src/video/cocoa/event.mm src/video/cocoa/fullscreen.mm src/video/cocoa/wnd_quartz.mm src/video/cocoa/wnd_quickdraw.mm
diffstat 6 files changed, 115 insertions(+), 69 deletions(-) [+]
line wrap: on
line diff
--- a/src/video/cocoa/cocoa_v.h
+++ b/src/video/cocoa/cocoa_v.h
@@ -122,12 +122,14 @@
 
 void QZ_GameLoop();
 
-void QZ_ShowMouse();
-void QZ_HideMouse();
-
 uint QZ_ListModes(OTTD_Point *modes, uint max_modes, CGDirectDisplayID display_id, int display_depth);
 
-/* Subclass of NSWindow to cater our special needs */
+/** Category of NSCursor to allow cursor showing/hiding */
+@interface NSCursor (OTTD_QuickdrawCursor)
++ (NSCursor *) clearCocoaCursor;
+@end
+
+/** Subclass of NSWindow to cater our special needs */
 @interface OTTD_CocoaWindow : NSWindow {
 	CocoaSubdriver *driver;
 }
@@ -143,13 +145,23 @@
 - (id)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag;
 @end
 
-/* Subclass of NSView to fix Quartz rendering */
+/** Subclass of NSView to fix Quartz rendering and mouse awareness */
 @interface OTTD_CocoaView : NSView {
 	CocoaSubdriver *driver;
+	NSTrackingRectTag trackingtag;
 }
 - (void)setDriver:(CocoaSubdriver*)drv;
 - (void)drawRect:(NSRect)rect;
 - (BOOL)isOpaque;
+- (BOOL)acceptsFirstResponder;
+- (BOOL)becomeFirstResponder;
+- (void)setTrackingRect;
+- (void)clearTrackingRect;
+- (void)resetCursorRects;
+- (void)viewWillMoveToWindow:(NSWindow *)win;
+- (void)viewDidMoveToWindow;
+- (void)mouseEntered:(NSEvent *)theEvent;
+- (void)mouseExited:(NSEvent *)theEvent;
 @end
 
 /** Delegate for our NSWindow to send ask for quit on close */
--- a/src/video/cocoa/cocoa_v.mm
+++ b/src/video/cocoa/cocoa_v.mm
@@ -369,7 +369,6 @@
 		return;
 	}
 
-	QZ_ShowMouse();
 	NSRunAlertPanel([ NSString stringWithUTF8String:title ], [ NSString stringWithUTF8String:message ], [ NSString stringWithUTF8String:buttonLabel ], nil, nil);
 
 	if (!wasstarted && _video_driver != NULL) _video_driver->Stop();
@@ -407,6 +406,26 @@
 }
 
 
+/**
+ * Re-implement the system cursor in order to allow hiding and showing it nicely
+ */
+@implementation NSCursor (OTTD_CocoaCursor)
++ (NSCursor *) clearCocoaCursor
+{
+	/* RAW 16x16 transparent GIF */
+	unsigned char clearGIFBytes[] = {
+		0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x00,
+		0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00,
+		0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,
+		0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED, 0x0F, 0xA3, 0x9C, 0xB4,
+		0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B};
+	NSData *clearGIFData = [ NSData dataWithBytesNoCopy:&clearGIFBytes[0] length:55 freeWhenDone:NO ];
+	NSImage *clearImg = [ [ NSImage alloc ] initWithData:clearGIFData ];
+	return [ [ NSCursor alloc ] initWithImage:clearImg hotSpot:NSMakePoint(0.0,0.0) ];
+}
+@end
+
+
 
 @implementation OTTD_CocoaWindow
 
@@ -425,8 +444,6 @@
 	/* window is hidden now */
 	driver->active = false;
 
-	QZ_ShowMouse();
-
 	[ super miniaturize:sender ];
 }
 
@@ -538,6 +555,76 @@
 {
 	return;
 }
+/**
+ * Allow to handle events
+ */
+- (BOOL)acceptsFirstResponder
+{
+	return YES;
+}
+/**
+ * Actually handle events
+ */
+- (BOOL)becomeFirstResponder
+{
+	return YES;
+}
+/**
+ * Define the rectangle where we draw our application window
+ */
+- (void)setTrackingRect
+{
+	NSPoint loc = [ self convertPoint:[ [ self window ] mouseLocationOutsideOfEventStream ] fromView:nil ];
+	BOOL inside = ([ self hitTest:loc ]==self);
+	if(inside) [ [ self window] makeFirstResponder:self ];
+	trackingtag = [ self addTrackingRect:[self visibleRect] owner:self userData:nil assumeInside:inside ];
+}
+/**
+ * Return responsibility for the application window to system
+ */
+- (void)clearTrackingRect
+{
+	[ self removeTrackingRect:trackingtag ];
+}
+/**
+ * Declare responsibility for the cursor within our application rect
+ */
+- (void)resetCursorRects
+{
+	[ super resetCursorRects ];
+	[ self clearTrackingRect ];
+	[ self setTrackingRect ];
+	[ self addCursorRect:[ self bounds ] cursor:[ NSCursor clearCocoaCursor ] ];
+}
+/**
+ * Prepare for moving the application window
+ */
+- (void)viewWillMoveToWindow:(NSWindow *)win
+{
+	if (!win && [ self window ]) [ self clearTrackingRect ];
+}
+/**
+ * Restore our responsibility for our application window after moving
+ */
+- (void)viewDidMoveToWindow
+{
+	if([ self window ]) [ self setTrackingRect ];
+}
+/**
+ * Make OpenTTD aware that it has control over the mouse
+ */
+- (void)mouseEntered:(NSEvent *)theEvent
+{
+	_cursor.in_window = true;
+}
+/**
+ * Make OpenTTD aware that it has NOT control over the mouse
+ */
+- (void)mouseExited:(NSEvent *)theEvent
+{
+	if (_cocoa_subdriver != NULL) UndrawMouseCursor();
+	_cursor.in_window = false;
+}
 @end
 
 
--- a/src/video/cocoa/event.mm
+++ b/src/video/cocoa/event.mm
@@ -55,7 +55,6 @@
 };
 
 
-static bool _show_mouse = true;
 static unsigned int _current_mods;
 static bool _tab_is_down;
 static bool _emulating_right_button;
@@ -72,34 +71,6 @@
 	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)
 {
 	assert(_cocoa_subdriver != NULL);
@@ -403,7 +374,6 @@
 
 	if (event == nil) return false;
 	if (!_cocoa_subdriver->IsActive()) {
-		QZ_ShowMouse();
 		[ NSApp sendEvent:event ];
 		return true;
 	}
@@ -419,18 +389,15 @@
 		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;
 
@@ -446,12 +413,6 @@
 				[ 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 */
@@ -467,12 +428,7 @@
 			[ 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 */
@@ -487,12 +443,10 @@
 		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;
@@ -500,12 +454,10 @@
 		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;
@@ -515,12 +467,10 @@
 		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;
@@ -528,12 +478,10 @@
 		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;
--- a/src/video/cocoa/fullscreen.mm
+++ b/src/video/cocoa/fullscreen.mm
@@ -293,9 +293,6 @@
 		mouseLocation.x /= this->device_width;
 		mouseLocation.y /= this->device_height;
 
-		/* Hide mouse in order to avoid glitch in 8bpp */
-		QZ_HideMouse();
-
 		/* Fade display to zero gamma */
 		OTTD_QuartzGammaTable gamma_table;
 		gamma_error = this->FadeGammaOut(&gamma_table);
@@ -333,6 +330,9 @@
 		/* If we don't hide menu bar, it will get events and interrupt the program */
 		HideMenuBar();
 
+		/* Hide the OS cursor */
+		CGDisplayHideCursor(this->display_id);
+
 		/* Fade the display to original gamma */
 		if (!gamma_error) FadeGammaIn(&gamma_table);
 
@@ -352,6 +352,8 @@
 		display_mouseLocation.x = mouseLocation.x * this->device_width;
 		display_mouseLocation.y = this->device_height - (mouseLocation.y * this->device_height);
 
+		_cursor.in_window = true;
+
 		CGDisplayMoveCursorToPoint(this->display_id, display_mouseLocation);
 
 		return true;
@@ -384,6 +386,9 @@
 
 		CGReleaseAllDisplays();
 
+		/* Bring back the cursor */
+		CGDisplayShowCursor(this->display_id);
+
 		ShowMenuBar();
 
 		/* Reset the main screen's rectangle
@@ -391,8 +396,6 @@
 		NSRect screen_rect = NSMakeRect(0, 0, CGDisplayPixelsWide(this->display_id), CGDisplayPixelsHigh(this->display_id));
 		[ [ NSScreen mainScreen ] setFrame:screen_rect ];
 
-		QZ_ShowMouse();
-
 		/* Destroy the pixel buffer */
 		if (this->pixel_buffer != NULL) {
 			free(this->pixel_buffer);
--- a/src/video/cocoa/wnd_quartz.mm
+++ b/src/video/cocoa/wnd_quartz.mm
@@ -351,8 +351,6 @@
 
 WindowQuartzSubdriver::~WindowQuartzSubdriver()
 {
-	QZ_ShowMouse();
-
 	/* Release window mode resources */
 	if (this->window != nil) [ this->window close ];
 
--- a/src/video/cocoa/wnd_quickdraw.mm
+++ b/src/video/cocoa/wnd_quickdraw.mm
@@ -353,8 +353,6 @@
 
 WindowQuickdrawSubdriver::~WindowQuickdrawSubdriver()
 {
-	QZ_ShowMouse();
-
 	/* Release window mode resources */
 	if (this->window != nil) [ this->window close ];