From adc1760b0d495547ad15f6b11a34edf8ec13e36d Mon Sep 17 00:00:00 2001 From: planetmaker Date: Tue, 21 Dec 2010 16:05:25 +0000 Subject: (svn r21578) -Fix [FS#2585]: [OSX] A double mouse cursor was shown under certain circumstances (based on patch by matheweis) --- src/video/cocoa/cocoa_v.h | 22 +++++++--- src/video/cocoa/cocoa_v.mm | 93 ++++++++++++++++++++++++++++++++++++++-- src/video/cocoa/event.mm | 52 ---------------------- src/video/cocoa/fullscreen.mm | 13 +++--- src/video/cocoa/wnd_quartz.mm | 2 - src/video/cocoa/wnd_quickdraw.mm | 2 - 6 files changed, 115 insertions(+), 69 deletions(-) diff --git a/src/video/cocoa/cocoa_v.h b/src/video/cocoa/cocoa_v.h index e570c6a86..087b2072c 100644 --- a/src/video/cocoa/cocoa_v.h +++ b/src/video/cocoa/cocoa_v.h @@ -122,12 +122,14 @@ void QZ_GameSizeChanged(); 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 @@ uint QZ_ListModes(OTTD_Point *modes, uint max_modes, CGDirectDisplayID display_i - (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 */ diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm index 7df4fab15..aa7e5d3ad 100644 --- a/src/video/cocoa/cocoa_v.mm +++ b/src/video/cocoa/cocoa_v.mm @@ -369,7 +369,6 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel 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 @@ void cocoaReleaseAutoreleasePool() } +/** + * 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 @@ void cocoaReleaseAutoreleasePool() /* window is hidden now */ driver->active = false; - QZ_ShowMouse(); - [ super miniaturize:sender ]; } @@ -538,6 +555,76 @@ void cocoaReleaseAutoreleasePool() { 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 diff --git a/src/video/cocoa/event.mm b/src/video/cocoa/event.mm index 7d7fa4cf8..a0d79a529 100644 --- a/src/video/cocoa/event.mm +++ b/src/video/cocoa/event.mm @@ -55,7 +55,6 @@ enum RightMouseButtonEmulationState { }; -static bool _show_mouse = true; static unsigned int _current_mods; static bool _tab_is_down; static bool _emulating_right_button; @@ -72,34 +71,6 @@ static uint32 GetTick() 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 @@ static bool QZ_PollEvent() if (event == nil) return false; if (!_cocoa_subdriver->IsActive()) { - QZ_ShowMouse(); [ NSApp sendEvent:event ]; return true; } @@ -419,18 +389,15 @@ static bool QZ_PollEvent() 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 @@ static bool QZ_PollEvent() [ 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 @@ static bool QZ_PollEvent() [ 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 @@ static bool QZ_PollEvent() 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 @@ static bool QZ_PollEvent() 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 @@ static bool QZ_PollEvent() 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 @@ static bool QZ_PollEvent() 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; diff --git a/src/video/cocoa/fullscreen.mm b/src/video/cocoa/fullscreen.mm index e41849c70..aa28422d3 100644 --- a/src/video/cocoa/fullscreen.mm +++ b/src/video/cocoa/fullscreen.mm @@ -293,9 +293,6 @@ class FullscreenSubdriver: public CocoaSubdriver { 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 @@ class FullscreenSubdriver: public CocoaSubdriver { /* 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 @@ class FullscreenSubdriver: public CocoaSubdriver { 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 @@ ERR_NO_MATCH: CGReleaseAllDisplays(); + /* Bring back the cursor */ + CGDisplayShowCursor(this->display_id); + ShowMenuBar(); /* Reset the main screen's rectangle @@ -391,8 +396,6 @@ ERR_NO_MATCH: 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); diff --git a/src/video/cocoa/wnd_quartz.mm b/src/video/cocoa/wnd_quartz.mm index 211f5f11e..6a3f404b2 100644 --- a/src/video/cocoa/wnd_quartz.mm +++ b/src/video/cocoa/wnd_quartz.mm @@ -351,8 +351,6 @@ WindowQuartzSubdriver::WindowQuartzSubdriver(int bpp) WindowQuartzSubdriver::~WindowQuartzSubdriver() { - QZ_ShowMouse(); - /* Release window mode resources */ if (this->window != nil) [ this->window close ]; diff --git a/src/video/cocoa/wnd_quickdraw.mm b/src/video/cocoa/wnd_quickdraw.mm index 5ec3af6f5..be07646ce 100644 --- a/src/video/cocoa/wnd_quickdraw.mm +++ b/src/video/cocoa/wnd_quickdraw.mm @@ -353,8 +353,6 @@ WindowQuickdrawSubdriver::WindowQuickdrawSubdriver(int bpp) WindowQuickdrawSubdriver::~WindowQuickdrawSubdriver() { - QZ_ShowMouse(); - /* Release window mode resources */ if (this->window != nil) [ this->window close ]; -- cgit v1.2.3-70-g09d2