From b233cb640daa15880256d5e5441f22edf37d1cc1 Mon Sep 17 00:00:00 2001 From: bruvzg <7645683+bruvzg@users.noreply.github.com> Date: Thu, 2 Jul 2020 15:57:19 +0300 Subject: [PATCH] [macOS, 3.2] Implement confined mouse mode. --- platform/osx/os_osx.h | 7 +++++ platform/osx/os_osx.mm | 60 ++++++++++++++++++++++++++++++++++++++---- 2 files changed, 62 insertions(+), 5 deletions(-) diff --git a/platform/osx/os_osx.h b/platform/osx/os_osx.h index fb2485b2305..a0c6373c7c3 100644 --- a/platform/osx/os_osx.h +++ b/platform/osx/os_osx.h @@ -65,6 +65,13 @@ public: uint32_t unicode; }; + struct WarpEvent { + NSTimeInterval timestamp; + NSPoint delta; + }; + List warp_events; + NSTimeInterval last_warp = 0; + Vector key_event_buffer; int key_event_pos; diff --git a/platform/osx/os_osx.mm b/platform/osx/os_osx.mm index 54fb302bf3f..f330ec43a8f 100644 --- a/platform/osx/os_osx.mm +++ b/platform/osx/os_osx.mm @@ -716,12 +716,58 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { - (void)mouseMoved:(NSEvent *)event { + NSPoint delta = NSMakePoint([event deltaX], [event deltaY]); + NSPoint mpos = [event locationInWindow]; + + if (OS_OSX::singleton->mouse_mode == OS::MOUSE_MODE_CONFINED) { + // Discard late events + if (([event timestamp]) < OS_OSX::singleton->last_warp) { + return; + } + + // Warp affects next event delta, subtract previous warp deltas + List::Element *F = OS_OSX::singleton->warp_events.front(); + while (F) { + if (F->get().timestamp < [event timestamp]) { + List::Element *E = F; + delta.x -= E->get().delta.x; + delta.y -= E->get().delta.y; + F = F->next(); + OS_OSX::singleton->warp_events.erase(E); + } else { + F = F->next(); + } + } + + // Confine mouse position to the window, and update delta + NSRect frame = [OS_OSX::singleton->window_object frame]; + NSPoint conf_pos = mpos; + conf_pos.x = CLAMP(conf_pos.x + delta.x, 0.f, frame.size.width); + conf_pos.y = CLAMP(conf_pos.y - delta.y, 0.f, frame.size.height); + delta.x = conf_pos.x - mpos.x; + delta.y = mpos.y - conf_pos.y; + mpos = conf_pos; + + // Move mouse cursor + NSRect pointInWindowRect = NSMakeRect(conf_pos.x, conf_pos.y, 0, 0); + conf_pos = [[OS_OSX::singleton->window_view window] convertRectToScreen:pointInWindowRect].origin; + conf_pos.y = CGDisplayBounds(CGMainDisplayID()).size.height - conf_pos.y; + CGWarpMouseCursorPosition(conf_pos); + + // Save warp data + OS_OSX::singleton->last_warp = [[NSProcessInfo processInfo] systemUptime]; + OS_OSX::WarpEvent ev; + ev.timestamp = OS_OSX::singleton->last_warp; + ev.delta = delta; + OS_OSX::singleton->warp_events.push_back(ev); + } + Ref mm; mm.instance(); mm->set_button_mask(button_mask); const CGFloat backingScaleFactor = [[event window] backingScaleFactor]; - const Vector2 pos = get_mouse_pos([event locationInWindow], backingScaleFactor); + const Vector2 pos = get_mouse_pos(mpos, backingScaleFactor); mm->set_position(pos); mm->set_pressure([event pressure]); if ([event subtype] == NSEventSubtypeTabletPoint) { @@ -730,9 +776,7 @@ static void _mouseDownEvent(NSEvent *event, int index, int mask, bool pressed) { } mm->set_global_position(pos); mm->set_speed(OS_OSX::singleton->input->get_last_mouse_speed()); - Vector2 relativeMotion = Vector2(); - relativeMotion.x = [event deltaX] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor); - relativeMotion.y = [event deltaY] * OS_OSX::singleton -> _mouse_scale(backingScaleFactor); + const Vector2 relativeMotion = Vector2(delta.x, delta.y) * OS_OSX::singleton->_mouse_scale(backingScaleFactor); mm->set_relative(relativeMotion); get_key_modifier_state([event modifierFlags], mm); @@ -2042,7 +2086,9 @@ void OS_OSX::warp_mouse_position(const Point2 &p_to) { CGEventSourceSetLocalEventsSuppressionInterval(lEventRef, 0.0); CGAssociateMouseAndMouseCursorPosition(false); CGWarpMouseCursorPosition(lMouseWarpPos); - CGAssociateMouseAndMouseCursorPosition(true); + if (mouse_mode != MOUSE_MODE_CONFINED) { + CGAssociateMouseAndMouseCursorPosition(true); + } } } @@ -3093,11 +3139,15 @@ void OS_OSX::set_mouse_mode(MouseMode p_mode) { CGDisplayHideCursor(kCGDirectMainDisplay); } CGAssociateMouseAndMouseCursorPosition(true); + } else if (p_mode == MOUSE_MODE_CONFINED) { + CGDisplayShowCursor(kCGDirectMainDisplay); + CGAssociateMouseAndMouseCursorPosition(false); } else { CGDisplayShowCursor(kCGDirectMainDisplay); CGAssociateMouseAndMouseCursorPosition(true); } + warp_events.clear(); mouse_mode = p_mode; }