浏览代码

Improve macOS mouse latency and stale positioning

This commit makes several changes to input event handling in macOS:

_sapp_macos_update_mouse() has been changed to take an NSEvent*
argument, instead of no arguments. And instead of using [window
mouseLocationOutsideOfEventStream] to get the position of the mouse, it
uses event.locationInWindow. This seems to sometimes eliminate a frame
of mouse latency.

_sapp_macos_update_mouse() is no longer called immediately before
rendering a frame. Instead, it's called at the beginning of each of the
mouseMoved:, mouseDragged:, mouseDown:, etc. methods with its associated
NSEvent*. Immediately after, _sapp_macos_mouse_event() is called. This
eliminates a full frame of mouse latency. This also fixes situations
where the mouse cursor's resting apparent position would lag behind its
resting true position after the user stops moving the mouse.

_sapp_macos_poll_input_events() has been added, which calls [NSApp
nextEventMatchingMask: ...] to manually read and dispatch input events
from the event queue. _sapp_macos_poll_input_events() is called
immediately before _sapp_macos_frame(). This will sometimes catch and
dispatch input events that occurred shortly before the frame is to be
rendered, but hadn't yet been dispatched by other mechanisms. This
occasionally eliminates a frame of latency, and improves the worst-case
bounds. This seems to happen to 5% or fewer mouse events.

[NSEvent setMouseCoalescingEnabled:NO], if it even still does anything,
is now called once at during application startup instead of toggled off
and on along with mouse lock.
cancel 4 年之前
父节点
当前提交
9ea9c481aa
共有 1 个文件被更改,包括 50 次插入5 次删除
  1. 50 5
      sokol_app.h

+ 50 - 5
sokol_app.h

@@ -2917,9 +2917,9 @@ _SOKOL_PRIVATE void _sapp_macos_update_window_title(void) {
     [_sapp.macos.window setTitle: [NSString stringWithUTF8String:_sapp.window_title]];
 }
 
-_SOKOL_PRIVATE void _sapp_macos_update_mouse(void) {
+_SOKOL_PRIVATE void _sapp_macos_update_mouse(NSEvent* event) {
     if (!_sapp.mouse.locked) {
-        const NSPoint mouse_pos = [_sapp.macos.window mouseLocationOutsideOfEventStream];
+        const NSPoint mouse_pos = event.locationInWindow;
         float new_x = mouse_pos.x * _sapp.dpi_scale;
         float new_y = _sapp.framebuffer_height - (mouse_pos.y * _sapp.dpi_scale) - 1;
         /* don't update dx/dy in the very first update */
@@ -2961,19 +2961,16 @@ _SOKOL_PRIVATE void _sapp_macos_lock_mouse(bool lock) {
         stack with calls to sapp_show_mouse()
     */
     if (_sapp.mouse.locked) {
-        [NSEvent setMouseCoalescingEnabled:NO];
         CGAssociateMouseAndMouseCursorPosition(NO);
         CGDisplayHideCursor(kCGDirectMainDisplay);
     }
     else {
         CGDisplayShowCursor(kCGDirectMainDisplay);
         CGAssociateMouseAndMouseCursorPosition(YES);
-        [NSEvent setMouseCoalescingEnabled:YES];
     }
 }
 
 _SOKOL_PRIVATE void _sapp_macos_frame(void) {
-    _sapp_macos_update_mouse();
     _sapp_frame();
     if (_sapp.quit_requested || _sapp.quit_ordered) {
         [_sapp.macos.window performClose:nil];
@@ -3083,6 +3080,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
     }
     [_sapp.macos.window makeKeyAndOrderFront:nil];
     _sapp_macos_update_dimensions();
+    [NSEvent setMouseCoalescingEnabled:NO];
 }
 
 - (BOOL)applicationShouldTerminateAfterLastWindowClosed:(NSApplication*)sender {
@@ -3227,8 +3225,42 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
 }
 #endif
 
+_SOKOL_PRIVATE void _sapp_macos_poll_input_events() {
+    const NSEventMask mask = NSEventMaskLeftMouseDown |
+                             NSEventMaskLeftMouseUp|
+                             NSEventMaskRightMouseDown |
+                             NSEventMaskRightMouseUp |
+                             NSEventMaskMouseMoved |
+                             NSEventMaskLeftMouseDragged |
+                             NSEventMaskRightMouseDragged |
+                             NSEventMaskMouseEntered |
+                             NSEventMaskMouseExited |
+                             NSEventMaskKeyDown |
+                             NSEventMaskKeyUp |
+                             NSEventMaskCursorUpdate |
+                             NSEventMaskScrollWheel |
+                             NSEventMaskTabletPoint |
+                             NSEventMaskTabletProximity |
+                             NSEventMaskOtherMouseDown |
+                             NSEventMaskOtherMouseUp |
+                             NSEventMaskOtherMouseDragged |
+                             NSEventMaskPressure |
+                             NSEventMaskDirectTouch;
+    @autoreleasepool {
+        for (;;) {
+            NSEvent* event = [NSApp nextEventMatchingMask:mask untilDate:nil inMode:NSDefaultRunLoopMode dequeue:YES];
+            if (event == nil) {
+                break;
+            }
+            [NSApp sendEvent:event];
+        }
+    }
+}
+
 - (void)drawRect:(NSRect)rect {
     _SOKOL_UNUSED(rect);
+    /* Catch any last-moment input events */
+    _sapp_macos_poll_input_events();
     _sapp_macos_frame();
     #if !defined(SOKOL_METAL)
     [[_sapp.macos.view openGLContext] flushBuffer];
@@ -3260,6 +3292,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
     [super updateTrackingAreas];
 }
 - (void)mouseEntered:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     /* don't send mouse enter/leave while dragging (so that it behaves the same as
        on Windows while SetCapture is active
     */
@@ -3268,39 +3301,47 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
     }
 }
 - (void)mouseExited:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     if (0 == _sapp.macos.mouse_buttons) {
         _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_LEAVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
     }
 }
 - (void)mouseDown:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags));
     _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_LEFT);
 }
 - (void)mouseUp:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_LEFT, _sapp_macos_mod(event.modifierFlags));
     _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_LEFT);
 }
 - (void)rightMouseDown:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags));
     _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_RIGHT);
 }
 - (void)rightMouseUp:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_RIGHT, _sapp_macos_mod(event.modifierFlags));
     _sapp.macos.mouse_buttons &= ~(1<<SAPP_MOUSEBUTTON_RIGHT);
 }
 - (void)otherMouseDown:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     if (2 == event.buttonNumber) {
         _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_DOWN, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags));
         _sapp.macos.mouse_buttons |= (1<<SAPP_MOUSEBUTTON_MIDDLE);
     }
 }
 - (void)otherMouseUp:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     if (2 == event.buttonNumber) {
         _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_UP, SAPP_MOUSEBUTTON_MIDDLE, _sapp_macos_mod(event.modifierFlags));
         _sapp.macos.mouse_buttons &= (1<<SAPP_MOUSEBUTTON_MIDDLE);
     }
 }
 - (void)otherMouseDragged:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     if (2 == event.buttonNumber) {
         if (_sapp.mouse.locked) {
             _sapp.mouse.dx = [event deltaX];
@@ -3310,6 +3351,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
     }
 }
 - (void)mouseMoved:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     if (_sapp.mouse.locked) {
         _sapp.mouse.dx = [event deltaX];
         _sapp.mouse.dy = [event deltaY];
@@ -3317,6 +3359,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
     _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags));
 }
 - (void)mouseDragged:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     if (_sapp.mouse.locked) {
         _sapp.mouse.dx = [event deltaX];
         _sapp.mouse.dy = [event deltaY];
@@ -3324,6 +3367,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
     _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID , _sapp_macos_mod(event.modifierFlags));
 }
 - (void)rightMouseDragged:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     if (_sapp.mouse.locked) {
         _sapp.mouse.dx = [event deltaX];
         _sapp.mouse.dy = [event deltaY];
@@ -3331,6 +3375,7 @@ _SOKOL_PRIVATE void _sapp_macos_frame(void) {
     _sapp_macos_mouse_event(SAPP_EVENTTYPE_MOUSE_MOVE, SAPP_MOUSEBUTTON_INVALID, _sapp_macos_mod(event.modifierFlags));
 }
 - (void)scrollWheel:(NSEvent*)event {
+    _sapp_macos_update_mouse(event);
     if (_sapp_events_enabled()) {
         float dx = (float) event.scrollingDeltaX;
         float dy = (float) event.scrollingDeltaY;