diff --git a/src/video/cocoa/cocoa_v.h b/src/video/cocoa/cocoa_v.h index 4c53cdb8fa..0112f57515 100644 --- a/src/video/cocoa/cocoa_v.h +++ b/src/video/cocoa/cocoa_v.h @@ -78,11 +78,7 @@ protected: Dimension GetScreenSize() const override; private: - NSPoint GetMouseLocation(NSEvent *event); - bool MouseIsInsideView(NSPoint *pt); - CGPoint PrivateLocalToCG(NSPoint *p); bool PollEvent(); // In event.mm. - void MouseMovedEvent(int x, int y); // In event.mm. bool IsFullscreen(); void GameSizeChanged(); diff --git a/src/video/cocoa/cocoa_v.mm b/src/video/cocoa/cocoa_v.mm index 88f461234f..07640a0f83 100644 --- a/src/video/cocoa/cocoa_v.mm +++ b/src/video/cocoa/cocoa_v.mm @@ -397,6 +397,7 @@ bool VideoDriver_Cocoa::MakeWindow(int width, int height) [ this->window setContentView:this->cocoaview ]; [ this->cocoaview addSubview:draw_view ]; + [ this->window makeFirstResponder:this->cocoaview ]; [ draw_view release ]; [ this->window setColorSpace:[ NSColorSpace sRGBColorSpace ] ]; @@ -497,57 +498,6 @@ void VideoDriver_Cocoa::UpdatePalette(uint first_color, uint num_colors) this->num_dirty_rects = lengthof(this->dirty_rects); } -/** - * Convert local coordinate to window server (CoreGraphics) coordinate - * @param p local coordinates - * @return window driver coordinates - */ -CGPoint VideoDriver_Cocoa::PrivateLocalToCG(NSPoint *p) -{ - - p->y = this->window_height - p->y; - *p = [ this->cocoaview convertPoint:*p toView:nil ]; - *p = [ this->window convertRectToScreen:NSMakeRect(p->x, p->y, 0, 0) ].origin; - - p->y = NSScreen.screens[0].frame.size.height - p->y; - - CGPoint cgp; - cgp.x = p->x; - cgp.y = p->y; - - return cgp; -} - -/** - * Return the mouse location - * @param event UI event - * @return mouse location as NSPoint - */ -NSPoint VideoDriver_Cocoa::GetMouseLocation(NSEvent *event) -{ - NSPoint pt; - - if ( [ event window ] == nil) { - pt = [ this->cocoaview convertPoint:[ [ this->cocoaview window ] convertRectFromScreen:NSMakeRect([ event locationInWindow ].x, [ event locationInWindow ].y, 0, 0) ].origin fromView:nil ]; - } else { - pt = [ event locationInWindow ]; - } - - pt.y = this->window_height - pt.y; - - return pt; -} - -/** - * Return whether the mouse is within our view - * @param pt Mouse coordinates - * @return Whether mouse coordinates are within view - */ -bool VideoDriver_Cocoa::MouseIsInsideView(NSPoint *pt) -{ - return [ cocoaview mouse:*pt inRect:[ this->cocoaview bounds ] ]; -} - /** Clear buffer to opaque black. */ static void ClearWindowBuffer(uint32 *buffer, uint32 pitch, uint32 height) { diff --git a/src/video/cocoa/cocoa_wnd.h b/src/video/cocoa/cocoa_wnd.h index 449b457af1..98ed7df7ec 100644 --- a/src/video/cocoa/cocoa_wnd.h +++ b/src/video/cocoa/cocoa_wnd.h @@ -14,6 +14,13 @@ class VideoDriver_Cocoa; +/* Right Mouse Button Emulation enum */ +enum RightMouseButtonEmulationState { + RMBE_COMMAND = 0, + RMBE_CONTROL = 1, + RMBE_OFF = 2, +}; + extern NSString *OTTDMainLaunchGameEngine; /** Category of NSCursor to allow cursor showing/hiding */ @@ -34,6 +41,7 @@ extern NSString *OTTDMainLaunchGameEngine; /** Subclass of NSView to support mouse awareness and text input. */ @interface OTTD_CocoaView : NSView +- (NSPoint)mousePositionFromEvent:(NSEvent *)e; @end /** Delegate for our NSWindow to send ask for quit on close */ diff --git a/src/video/cocoa/cocoa_wnd.mm b/src/video/cocoa/cocoa_wnd.mm index dd1d269f8d..e9ea697057 100644 --- a/src/video/cocoa/cocoa_wnd.mm +++ b/src/video/cocoa/cocoa_wnd.mm @@ -27,6 +27,7 @@ #include "../../rev.h" #include "cocoa_v.h" #include "cocoa_wnd.h" +#include "../../settings_type.h" #include "../../string_func.h" #include "../../gfx_func.h" #include "../../window_func.h" @@ -348,7 +349,6 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel self->driver = drv; [ self setContentMinSize:NSMakeSize(64.0f, 64.0f) ]; - [ self setAcceptsMouseMovedEvents:YES ]; std::string caption = std::string{"OpenTTD "} + _openttd_revision; NSString *nsscaption = [ [ NSString alloc ] initWithUTF8String:caption.c_str() ]; @@ -417,7 +417,9 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel @end -@implementation OTTD_CocoaView +@implementation OTTD_CocoaView { + float _current_magnification; +} /** * Allow to handle events @@ -451,7 +453,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel - (void)viewDidMoveToWindow { /* Install mouse tracking area. */ - NSTrackingAreaOptions track_opt = NSTrackingInVisibleRect | NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited | NSTrackingCursorUpdate; + NSTrackingAreaOptions track_opt = NSTrackingInVisibleRect | NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingCursorUpdate; NSTrackingArea *track = [ [ NSTrackingArea alloc ] initWithRect:[ self bounds ] options:track_opt owner:self userInfo:nil ]; [ self addTrackingArea:track ]; [ track release ]; @@ -472,6 +474,145 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel _cursor.in_window = false; } +/** + * Return the mouse location + * @param event UI event + * @return mouse location as NSPoint + */ +- (NSPoint)mousePositionFromEvent:(NSEvent *)e +{ + NSPoint pt = e.locationInWindow; + if ([ e window ] == nil) pt = [ self.window convertRectFromScreen:NSMakeRect(pt.x, pt.y, 0, 0) ].origin; + pt = [ self convertPoint:pt fromView:nil ]; + + pt.y = self.bounds.size.height - pt.y; + + return pt; +} + +- (void)internalMouseMoveEvent:(NSEvent *)event +{ + NSPoint pt = [ self mousePositionFromEvent:event ]; + + if (_cursor.UpdateCursorPosition(pt.x, pt.y, false) && [ NSApp isActive ]) { + /* Warping cursor when in foreground */ + NSPoint warp = [ self convertPoint:NSMakePoint(_cursor.pos.x, self.bounds.size.height - _cursor.pos.y) toView:nil ]; + warp = [ self.window convertRectToScreen:NSMakeRect(warp.x, warp.y, 0, 0) ].origin; + warp.y = NSScreen.screens[0].frame.size.height - warp.y; + + /* Do the actual warp */ + CGWarpMouseCursorPosition(NSPointToCGPoint(warp)); + /* this is the magic call that fixes cursor "freezing" after warp */ + CGAssociateMouseAndMouseCursorPosition(true); + } + HandleMouseEvents(); +} + +- (BOOL)emulateRightButton:(NSEvent *)event +{ + uint32 keymask = 0; + if (_settings_client.gui.right_mouse_btn_emulation == RMBE_COMMAND) keymask |= NSCommandKeyMask; + if (_settings_client.gui.right_mouse_btn_emulation == RMBE_CONTROL) keymask |= NSControlKeyMask; + + return (event.modifierFlags & keymask) != 0; +} + +- (void)mouseMoved:(NSEvent *)event +{ + [ self internalMouseMoveEvent:event ]; +} + +- (void)mouseDragged:(NSEvent *)event +{ + [ self internalMouseMoveEvent:event ]; +} +- (void)mouseDown:(NSEvent *)event +{ + if ([ self emulateRightButton:event ]) { + [ self rightMouseDown:event ]; + } else { + _left_button_down = true; + [ self internalMouseMoveEvent:event ]; + } +} +- (void)mouseUp:(NSEvent *)event +{ + if ([ self emulateRightButton:event ]) { + [ self rightMouseUp:event ]; + } else { + _left_button_down = false; + _left_button_clicked = false; + [ self internalMouseMoveEvent:event ]; + } +} + +- (void)rightMouseDragged:(NSEvent *)event +{ + [ self internalMouseMoveEvent:event ]; +} +- (void)rightMouseDown:(NSEvent *)event +{ + _right_button_down = true; + _right_button_clicked = true; + [ self internalMouseMoveEvent:event ]; +} +- (void)rightMouseUp:(NSEvent *)event +{ + _right_button_down = false; + [ self internalMouseMoveEvent:event ]; +} + +- (void)scrollWheel:(NSEvent *)event +{ + 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 */ + + /* Update the scroll count for 2D scrolling */ + CGFloat deltaX; + CGFloat deltaY; + + /* Use precise scrolling-specific deltas if they're supported. */ + if ([ event respondsToSelector:@selector(hasPreciseScrollingDeltas) ]) { + /* No precise deltas indicates a scroll wheel is being used, so we don't want 2D scrolling. */ + if (![ event hasPreciseScrollingDeltas ]) return; + + deltaX = [ event scrollingDeltaX ] * 0.5f; + deltaY = [ event scrollingDeltaY ] * 0.5f; + } else { + deltaX = [ event deltaX ] * 5; + deltaY = [ event deltaY ] * 5; + } + + _cursor.h_wheel -= (int)(deltaX * _settings_client.gui.scrollwheel_multiplier); + _cursor.v_wheel -= (int)(deltaY * _settings_client.gui.scrollwheel_multiplier); +} + +- (void)magnifyWithEvent:(NSEvent *)event +{ + /* Pinch open or close gesture. */ + self->_current_magnification += [ event magnification ] * 5.0f; + + while (self->_current_magnification >= 1.0f) { + self->_current_magnification -= 1.0f; + _cursor.wheel--; + HandleMouseEvents(); + } + while (self->_current_magnification <= -1.0f) { + self->_current_magnification += 1.0f; + _cursor.wheel++; + HandleMouseEvents(); + } +} + +- (void)endGestureWithEvent:(NSEvent *)event +{ + /* Gesture ended. */ + self->_current_magnification = 0.0f; +} + /** Insert the given text at the given range. */ - (void)insertText:(id)aString replacementRange:(NSRange)replacementRange diff --git a/src/video/cocoa/event.mm b/src/video/cocoa/event.mm index 00559e6bce..d6c4e5552c 100644 --- a/src/video/cocoa/event.mm +++ b/src/video/cocoa/event.mm @@ -27,6 +27,7 @@ #include "../../settings_type.h" #include "../../core/geometry_type.hpp" #include "cocoa_v.h" +#include "cocoa_wnd.h" #include "cocoa_keys.h" #include "../../blitter/factory.hpp" #include "../../gfx_func.h" @@ -48,18 +49,8 @@ */ -/* Right Mouse Button Emulation enum */ -enum RightMouseButtonEmulationState { - RMBE_COMMAND, - RMBE_CONTROL, - RMBE_OFF, -}; - - static unsigned int _current_mods; static bool _tab_is_down; -static bool _emulating_right_button; -static float _current_magnification; #ifdef _DEBUG static uint32 _tEvent; #endif @@ -300,47 +291,6 @@ static void QZ_DoUnsidedModifiers(unsigned int newMods) _current_mods = newMods; } -void VideoDriver_Cocoa::MouseMovedEvent(int x, int y) -{ - if (_cursor.UpdateCursorPosition(x, y, false) && [ NSApp isActive ]) { - /* Warping cursor when in foreground */ - NSPoint p = NSMakePoint(_cursor.pos.x, _cursor.pos.y); - CGPoint cgp = this->PrivateLocalToCG(&p); - - /* Do the actual warp */ - CGWarpMouseCursorPosition(cgp); - /* this is the magic call that fixes cursor "freezing" after warp */ - CGAssociateMouseAndMouseCursorPosition(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; - } -} - @@ -365,111 +315,7 @@ bool VideoDriver_Cocoa::PollEvent() QZ_DoUnsidedModifiers( [ event modifierFlags ] ); NSString *chars; - NSPoint pt; switch ([ event type ]) { - case NSMouseMoved: - case NSOtherMouseDragged: - case NSLeftMouseDragged: - pt = this->GetMouseLocation(event); - if (!this->MouseIsInsideView(&pt) && !_emulating_right_button) { - [ NSApp sendEvent:event ]; - break; - } - - this->MouseMovedEvent((int)pt.x, (int)pt.y); - break; - - case NSRightMouseDragged: - pt = this->GetMouseLocation(event); - this->MouseMovedEvent((int)pt.x, (int)pt.y); - break; - - case NSLeftMouseDown: - { - uint32 keymask = 0; - if (_settings_client.gui.right_mouse_btn_emulation == RMBE_COMMAND) keymask |= NSCommandKeyMask; - if (_settings_client.gui.right_mouse_btn_emulation == RMBE_CONTROL) keymask |= NSControlKeyMask; - - pt = this->GetMouseLocation(event); - - if (!([ event modifierFlags ] & keymask) || !this->MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - } - - this->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 = this->GetMouseLocation(event); - - this->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 = this->GetMouseLocation(event); - if (!this->MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - break; - } - - this->MouseMovedEvent((int)pt.x, (int)pt.y); - QZ_MouseButtonEvent(1, YES); - break; - - case NSRightMouseUp: - pt = this->GetMouseLocation(event); - if (!this->MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - break; - } - - this->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)) { - [ NSApp sendEvent:event ]; - break; - } - - this->MouseMovedEvent((int)pt.x, (int)pt.y); - QZ_MouseButtonEvent([ event buttonNumber ], YES); - break; - - case NSOtherMouseUp: - pt = QZ_GetMouseLocation(event); - if (!QZ_MouseIsInsideView(&pt)) { - [ NSApp sendEvent:event ]; - break; - } - - this->MouseMovedEvent((int)pt.x, (int)pt.y); - QZ_MouseButtonEvent([ event buttonNumber ], NO); - break; -#endif - case NSKeyDown: { /* Quit, hide and minimize */ switch ([ event keyCode ]) { @@ -513,64 +359,6 @@ bool VideoDriver_Cocoa::PollEvent() 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 */ - - /* Update the scroll count for 2D scrolling */ - CGFloat deltaX; - CGFloat deltaY; - - /* Use precise scrolling-specific deltas if they're supported. */ - if ([event respondsToSelector:@selector(hasPreciseScrollingDeltas)]) { - /* No precise deltas indicates a scroll wheel is being used, so we don't want 2D scrolling. */ - if (![ event hasPreciseScrollingDeltas ]) break; - - deltaX = [ event scrollingDeltaX ] * 0.5f; - deltaY = [ event scrollingDeltaY ] * 0.5f; - } else { - deltaX = [ event deltaX ] * 5; - deltaY = [ event deltaY ] * 5; - } - - _cursor.h_wheel -= (int)(deltaX * _settings_client.gui.scrollwheel_multiplier); - _cursor.v_wheel -= (int)(deltaY * _settings_client.gui.scrollwheel_multiplier); - - break; - - case NSEventTypeMagnify: - /* Pinch open or close gesture. */ - _current_magnification += [ event magnification ] * 5.0f; - - while (_current_magnification >= 1.0f) { - _current_magnification -= 1.0f; - _cursor.wheel--; - HandleMouseEvents(); - } - while (_current_magnification <= -1.0f) { - _current_magnification += 1.0f; - _cursor.wheel++; - HandleMouseEvents(); - } - break; - - case NSEventTypeEndGesture: - /* Gesture ended. */ - _current_magnification = 0.0f; - break; - - case NSCursorUpdate: - case NSMouseEntered: - case NSMouseExited: - /* Catch these events if the cursor is dragging. During dragging, we reset - * the mouse position programmatically, which would trigger OS X to show - * the default arrow cursor if the events are propagated. */ - if (_cursor.fix_at) break; - FALLTHROUGH; - default: [ NSApp sendEvent:event ]; }