Bug 1292201 - Accumulate integer deltas for scroll wheel events. r?masayuki draft
authorMarkus Stange <mstange@themasta.com>
Tue, 18 Oct 2016 14:05:55 -0400
changeset 426553 2959213da6c88acee843deead7a0dd8c939d776d
parent 426552 38ae89e3e8b374fcd17215fd5cd5257b2e3834ce
child 534207 3c7856af97e5a2d19d96684e7d5870074c01e198
push id32738
push userbmo:mstange@themasta.com
push dateTue, 18 Oct 2016 18:06:50 +0000
reviewersmasayuki
bugs1292201
milestone52.0a1
Bug 1292201 - Accumulate integer deltas for scroll wheel events. r?masayuki MozReview-Commit-ID: 9nhSngI3AAM
widget/cocoa/nsChildView.mm
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -4725,22 +4725,16 @@ NSEvent* gLastDragMouseDownEvent = nil;
   [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
   geckoEvent.button = WidgetMouseEvent::eMiddleButton;
 
   // send event into Gecko by going directly to the
   // the widget.
   mGeckoChild->DispatchInputEvent(&geckoEvent);
 }
 
-static int32_t RoundUp(double aDouble)
-{
-  return aDouble < 0 ? static_cast<int32_t>(floor(aDouble)) :
-                       static_cast<int32_t>(ceil(aDouble));
-}
-
 - (void)sendWheelStartOrStop:(EventMessage)msg forEvent:(NSEvent *)theEvent
 {
   WidgetWheelEvent wheelEvent(true, msg, mGeckoChild);
   [self convertCocoaMouseWheelEvent:theEvent toGeckoEvent:&wheelEvent];
   mExpectingWheelStop = (msg == eWheelOperationStart);
   mGeckoChild->DispatchInputEvent(wheelEvent.AsInputEvent());
 }
 
@@ -4782,16 +4776,59 @@ PanGestureTypeForEvent(NSEvent* aEvent)
           return PanGestureInput::PANGESTURE_PAN;
       }
     default:
       NS_ERROR("unexpected event phase");
       return PanGestureInput::PANGESTURE_PAN;
   }
 }
 
+static int32_t RoundUp(double aDouble)
+{
+  return aDouble < 0 ? static_cast<int32_t>(floor(aDouble)) :
+                       static_cast<int32_t>(ceil(aDouble));
+}
+
+static int32_t
+TakeLargestInt(gfx::Float* aFloat)
+{
+  int32_t result(*aFloat); // truncate towards zero
+  *aFloat -= result;
+  return result;
+}
+
+static gfx::IntPoint
+AccumulateIntegerDelta(NSEvent* aEvent)
+{
+  static gfx::Point sAccumulator(0.0f, 0.0f);
+  if (nsCocoaUtils::EventPhase(aEvent) == NSEventPhaseBegan) {
+    sAccumulator = gfx::Point(0.0f, 0.0f);
+  }
+  sAccumulator.x += [aEvent deltaX];
+  sAccumulator.y += [aEvent deltaY];
+  return gfx::IntPoint(TakeLargestInt(&sAccumulator.x),
+                       TakeLargestInt(&sAccumulator.y));
+}
+
+static gfx::IntPoint
+GetIntegerDeltaForEvent(NSEvent* aEvent)
+{
+  if (nsCocoaFeatures::OnSierraOrLater()) {
+    return AccumulateIntegerDelta(aEvent);
+  }
+
+  // Pre-10.12, deltaX/deltaY had the accumulation behavior that we want, and
+  // it worked more reliably than doing it on our own, so use it on pre-10.12
+  // versions. For example, with a traditional USB mouse, the first wheel
+  // "tick" would always senda line scroll of at least one line, but with our
+  // own accumulation you sometimes need to do multiple wheel ticks before one
+  // line has been accumulated.
+  return gfx::IntPoint(RoundUp([aEvent deltaX]), RoundUp([aEvent deltaY]));
+}
+
 - (void)scrollWheel:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   if (gfxPrefs::AsyncPanZoomSeparateEventThread() && [self apzctm]) {
     // Disable main-thread scrolling completely when using APZ with the
     // separate event thread. This is bug 1013412.
     return;
@@ -4829,18 +4866,17 @@ PanGestureTypeForEvent(NSEvent* aEvent)
   ScreenPoint position = ViewAs<ScreenPixel>(
     [self convertWindowCoordinates:locationInWindow],
     PixelCastJustification::LayoutDeviceIsScreenForUntransformedEvent);
 
   bool usePreciseDeltas = nsCocoaUtils::HasPreciseScrollingDeltas(theEvent) &&
     Preferences::GetBool("mousewheel.enable_pixel_scrolling", true);
   bool hasPhaseInformation = nsCocoaUtils::EventHasPhaseInformation(theEvent);
 
-  int32_t lineOrPageDeltaX = RoundUp(-[theEvent deltaX]);
-  int32_t lineOrPageDeltaY = RoundUp(-[theEvent deltaY]);
+  gfx::IntPoint lineOrPageDelta = -GetIntegerDeltaForEvent(theEvent);
 
   Modifiers modifiers = nsCocoaUtils::ModifiersForEvent(theEvent);
 
   NSTimeInterval beforeNow = [[NSProcessInfo processInfo] systemUptime] - [theEvent timestamp];
   PRIntervalTime eventIntervalTime = PR_IntervalNow() - PR_MillisecondsToInterval(beforeNow * 1000);
   TimeStamp eventTimeStamp = TimeStamp::Now() - TimeDuration::FromSeconds(beforeNow);
 
   ScreenPoint preciseDelta;
@@ -4850,18 +4886,18 @@ PanGestureTypeForEvent(NSEvent* aEvent)
     double scale = mGeckoChild->BackingScaleFactor();
     preciseDelta = ScreenPoint(-pixelDeltaX * scale, -pixelDeltaY * scale);
   }
 
   if (usePreciseDeltas && hasPhaseInformation) {
     PanGestureInput panEvent(PanGestureTypeForEvent(theEvent),
                              eventIntervalTime, eventTimeStamp,
                              position, preciseDelta, modifiers);
-    panEvent.mLineOrPageDeltaX = lineOrPageDeltaX;
-    panEvent.mLineOrPageDeltaY = lineOrPageDeltaY;
+    panEvent.mLineOrPageDeltaX = lineOrPageDelta.x;
+    panEvent.mLineOrPageDeltaY = lineOrPageDelta.y;
 
     if (panEvent.mType == PanGestureInput::PANGESTURE_END) {
       // Check if there's a momentum start event in the event queue, so that we
       // can annotate this event.
       NSEvent* nextWheelEvent =
         [NSApp nextEventMatchingMask:NSScrollWheelMask
                            untilDate:[NSDate distantPast]
                               inMode:NSDefaultRunLoopMode
@@ -4879,34 +4915,34 @@ PanGestureTypeForEvent(NSEvent* aEvent)
     // This is on 10.6 or old touchpads that don't have any phase information.
     ScrollWheelInput wheelEvent(eventIntervalTime, eventTimeStamp, modifiers,
                                 ScrollWheelInput::SCROLLMODE_INSTANT,
                                 ScrollWheelInput::SCROLLDELTA_PIXEL,
                                 position,
                                 preciseDelta.x,
                                 preciseDelta.y,
                                 false);
-    wheelEvent.mLineOrPageDeltaX = lineOrPageDeltaX;
-    wheelEvent.mLineOrPageDeltaY = lineOrPageDeltaY;
+    wheelEvent.mLineOrPageDeltaX = lineOrPageDelta.x;
+    wheelEvent.mLineOrPageDeltaY = lineOrPageDelta.y;
     wheelEvent.mIsMomentum = nsCocoaUtils::IsMomentumScrollEvent(theEvent);
     mGeckoChild->DispatchAPZWheelInputEvent(wheelEvent, false);
   } else {
     ScrollWheelInput::ScrollMode scrollMode = ScrollWheelInput::SCROLLMODE_INSTANT;
     if (gfxPrefs::SmoothScrollEnabled() && gfxPrefs::WheelSmoothScrollEnabled()) {
       scrollMode = ScrollWheelInput::SCROLLMODE_SMOOTH;
     }
     ScrollWheelInput wheelEvent(eventIntervalTime, eventTimeStamp, modifiers,
                                 scrollMode,
                                 ScrollWheelInput::SCROLLDELTA_LINE,
                                 position,
-                                lineOrPageDeltaX,
-                                lineOrPageDeltaY,
+                                lineOrPageDelta.x,
+                                lineOrPageDelta.y,
                                 false);
-    wheelEvent.mLineOrPageDeltaX = lineOrPageDeltaX;
-    wheelEvent.mLineOrPageDeltaY = lineOrPageDeltaY;
+    wheelEvent.mLineOrPageDeltaX = lineOrPageDelta.x;
+    wheelEvent.mLineOrPageDeltaY = lineOrPageDelta.y;
     mGeckoChild->DispatchAPZWheelInputEvent(wheelEvent, false);
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)handleAsyncScrollEvent:(CGEventRef)cgEvent ofType:(CGEventType)type
 {