diff --git a/lib/ui/hooks.dart b/lib/ui/hooks.dart index f644ef8481c0d..f35a151cbe723 100644 --- a/lib/ui/hooks.dart +++ b/lib/ui/hooks.dart @@ -179,7 +179,13 @@ void _invoke3(void callback(A1 a1, A2 a2, A3 a3), Zone zone, A1 arg1 // // * pointer_data.cc // * FlutterView.java -const int _kPointerDataFieldCount = 19; +// * pointer_data.h +// * pointer.dart +// +// While we're only sending 21 values across, we leave this as 22 due to a bug that +// writes the last 8 bytes to 0 after a few hundred calls. The last value in the packet +// then is not written to any variable. +const int _kPointerDataFieldCount = 22; PointerDataPacket _unpackPointerDataPacket(ByteData packet) { const int kStride = Int64List.bytesPerElement; @@ -208,7 +214,10 @@ PointerDataPacket _unpackPointerDataPacket(ByteData packet) { radiusMin: packet.getFloat64(kStride * offset++, _kFakeHostEndian), radiusMax: packet.getFloat64(kStride * offset++, _kFakeHostEndian), orientation: packet.getFloat64(kStride * offset++, _kFakeHostEndian), - tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian) + tilt: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + scrollDeltaX: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + scrollDeltaY: packet.getFloat64(kStride * offset++, _kFakeHostEndian), + sentinal: packet.getFloat64(kStride * offset++, _kFakeHostEndian), ); assert(offset == (i + 1) * _kPointerDataFieldCount); } diff --git a/lib/ui/pointer.dart b/lib/ui/pointer.dart index 98a4f92385e95..0a4aa7f4913bc 100644 --- a/lib/ui/pointer.dart +++ b/lib/ui/pointer.dart @@ -34,6 +34,9 @@ enum PointerChange { /// The pointer has stopped making contact with the device. up, + + /// The pointer has scrolled + scroll, } /// The kind of pointer device. @@ -51,7 +54,7 @@ enum PointerDeviceKind { invertedStylus, /// An unknown pointer device. - unknown + unknown, } /// Information about the state of a pointer. @@ -76,7 +79,10 @@ class PointerData { this.radiusMin: 0.0, this.radiusMax: 0.0, this.orientation: 0.0, - this.tilt: 0.0 + this.scrollDeltaX: 0.0, + this.scrollDeltaY: 0.0, + this.tilt: 0.0, + this.sentinal: 0.0, }); /// Time of event dispatch, relative to an arbitrary timeline. @@ -190,6 +196,19 @@ class PointerData { /// the stylus is flat on that surface). final double tilt; + /// For PointerDeviceKind.gesture with PointerGestureKind.scroll: + /// + /// The amount to scroll in the x direction, in physical pixels. + final double scrollDeltaX; + + /// For PointerDeviceKind.gesture with PointerGestureKind.scroll: + /// + /// The amount to scroll in the y direction, in physical pixels. + final double scrollDeltaY; + + /// A final value that is passed so that we are passing more than 21 values in the packet + final double sentinal; + @override String toString() => '$runtimeType(x: $physicalX, y: $physicalY)'; @@ -203,6 +222,7 @@ class PointerData { 'physicalX: $physicalX, ' 'physicalY: $physicalY, ' 'buttons: $buttons, ' + 'obscured: $obscured' 'pressure: $pressure, ' 'pressureMin: $pressureMin, ' 'pressureMax: $pressureMax, ' @@ -213,7 +233,10 @@ class PointerData { 'radiusMin: $radiusMin, ' 'radiusMax: $radiusMax, ' 'orientation: $orientation, ' - 'tilt: $tilt' + 'tilt: $tilt, ' + 'scrollDeltaX: $scrollDeltaX, ' + 'scrollDeltaY: $scrollDeltaY,' + 'sentinal: $sentinal' ')'; } } diff --git a/lib/ui/window/pointer_data.cc b/lib/ui/window/pointer_data.cc index c85a1a50b8e72..c61411e2a77da 100644 --- a/lib/ui/window/pointer_data.cc +++ b/lib/ui/window/pointer_data.cc @@ -9,7 +9,7 @@ namespace blink { // If this value changes, update the pointer data unpacking code in hooks.dart. -static constexpr int kPointerDataFieldCount = 19; +static constexpr int kPointerDataFieldCount = 22; static_assert(sizeof(PointerData) == sizeof(int64_t) * kPointerDataFieldCount, "PointerData has the wrong size"); diff --git a/lib/ui/window/pointer_data.h b/lib/ui/window/pointer_data.h index 6a23c3d689d2e..55a83b867cc4c 100644 --- a/lib/ui/window/pointer_data.h +++ b/lib/ui/window/pointer_data.h @@ -20,6 +20,7 @@ struct alignas(8) PointerData { kDown, kMove, kUp, + kScroll, }; // Must match the PointerDeviceKind enum in pointer.dart. @@ -48,7 +49,10 @@ struct alignas(8) PointerData { double radius_min; double radius_max; double orientation; + double scroll_delta_x; + double scroll_delta_y; double tilt; + double sentinal; void Clear(); }; diff --git a/lib/ui/window/window.cc b/lib/ui/window/window.cc index 783b025b7189a..5b251f65505ca 100644 --- a/lib/ui/window/window.cc +++ b/lib/ui/window/window.cc @@ -238,6 +238,7 @@ void Window::DispatchPointerDataPacket(const PointerDataPacket& packet) { Dart_Handle data_handle = ToByteData(packet.data()); if (Dart_IsError(data_handle)) return; + DartInvokeField(library_.value(), "_dispatchPointerDataPacket", {data_handle}); } diff --git a/shell/platform/android/io/flutter/view/FlutterView.java b/shell/platform/android/io/flutter/view/FlutterView.java index 8a46370248365..ae8ca19df6613 100644 --- a/shell/platform/android/io/flutter/view/FlutterView.java +++ b/shell/platform/android/io/flutter/view/FlutterView.java @@ -371,6 +371,7 @@ public InputConnection onCreateInputConnection(EditorInfo outAttrs) { private static final int kPointerChangeDown = 4; private static final int kPointerChangeMove = 5; private static final int kPointerChangeUp = 6; + private static final int kPointerChangeScroll = 7; // Must match the PointerDeviceKind enum in pointer.dart. private static final int kPointerDeviceKindTouch = 0; @@ -401,6 +402,9 @@ private int getPointerChangeForAction(int maskedAction) { if (maskedAction == MotionEvent.ACTION_CANCEL) { return kPointerChangeCancel; } + if (maskedAction == MotionEvent.ACTION_SCROLL) { + return kPointerChangeScroll; + } return -1; } @@ -428,6 +432,8 @@ private void addPointerForIndex(MotionEvent event, int pointerIndex, ByteBuffer int pointerKind = getPointerDeviceTypeForToolType(event.getToolType(pointerIndex)); + // This is ignored for non-gesture deviced kinds. + long timeStamp = event.getEventTime() * 1000; // Convert from milliseconds to microseconds. packet.putLong(timeStamp); // time_stamp @@ -437,6 +443,7 @@ private void addPointerForIndex(MotionEvent event, int pointerIndex, ByteBuffer packet.putDouble(event.getX(pointerIndex)); // physical_x packet.putDouble(event.getY(pointerIndex)); // physical_y + if (pointerKind == kPointerDeviceKindMouse) { packet.putLong(event.getButtonState() & 0x1F); // buttons } else if (pointerKind == kPointerDeviceKindStylus) { @@ -468,12 +475,55 @@ private void addPointerForIndex(MotionEvent event, int pointerIndex, ByteBuffer packet.putDouble(0.0); // radius_max packet.putDouble(event.getAxisValue(MotionEvent.AXIS_ORIENTATION, pointerIndex)); // orientation - + if (pointerKind == kPointerDeviceKindStylus) { packet.putDouble(event.getAxisValue(MotionEvent.AXIS_TILT, pointerIndex)); // tilt } else { - packet.putDouble(0.0); // tilt + packet.putDouble(4.0); // tilt + } + packet.putDouble(event.getAxisValue(MotionEvent.AXIS_HSCROLL)); // scroll_delta_x + packet.putDouble(event.getAxisValue(MotionEvent.AXIS_VSCROLL)); // scroll_delta_y + + // Dummy value that is needed due to bug in the converter writing the last 8 bytes + // of the packet to 0. + packet.putDouble(0.0); + } + + @Override + public boolean onGenericMotionEvent(MotionEvent event) { + if (!isAttached()) { + return false; + } + + int maskedAction = event.getAction(); + if (maskedAction != MotionEvent.ACTION_SCROLL) + return false; + + // TODO(abarth): This version check might not be effective in some + // versions of Android that statically compile code and will be upset + // at the lack of |requestUnbufferedDispatch|. Instead, we should factor + // version-dependent code into separate classes for each supported + // version and dispatch dynamically. + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + requestUnbufferedDispatch(event); } + + // These values must match the unpacking code in hooks.dart. + final int kPointerDataFieldCount = 22; + final int kBytePerField = 8; + + int pointerCount = event.getPointerCount(); + + ByteBuffer packet = ByteBuffer.allocateDirect(pointerCount * kPointerDataFieldCount * kBytePerField); + packet.order(ByteOrder.LITTLE_ENDIAN); + + for (int p = 0; p < pointerCount; p++) { + addPointerForIndex(event, p, packet); + } + + assert packet.position() % (kPointerDataFieldCount * kBytePerField) == 0; + nativeDispatchPointerDataPacket(mNativeView.get(), packet, packet.position()); + return true; } @Override @@ -492,7 +542,7 @@ public boolean onTouchEvent(MotionEvent event) { } // These values must match the unpacking code in hooks.dart. - final int kPointerDataFieldCount = 19; + final int kPointerDataFieldCount = 22; final int kBytePerField = 8; int pointerCount = event.getPointerCount(); diff --git a/shell/platform/darwin/desktop/flutter_window.mm b/shell/platform/darwin/desktop/flutter_window.mm index 28591b950dc7f..b08d1fe813f3b 100644 --- a/shell/platform/darwin/desktop/flutter_window.mm +++ b/shell/platform/darwin/desktop/flutter_window.mm @@ -226,6 +226,7 @@ - (void)dispatchEvent:(NSEvent*)event phase:(NSEventPhase)phase { break; case blink::PointerData::Change::kAdd: case blink::PointerData::Change::kRemove: + case blink::PointerData::Change::kScroll: case blink::PointerData::Change::kHover: FML_DCHECK(!_mouseIsDown); break; diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 184f63e639edf..764c4bc2f7d2f 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -416,6 +416,18 @@ FlutterResult FlutterEngineSendWindowMetricsEvent( : kInvalidArguments; } +// Returns the blink::PointerData::DeviceKind for the given +// FlutterPointerDeviceKind. +inline blink::PointerData::DeviceKind ToPointerDataDeviceKind( + FlutterPointerDeviceKind kind) { + switch (kind) { + case kMouse: + return blink::PointerData::DeviceKind::kMouse; + } + return blink::PointerData::DeviceKind::kMouse; +} + +// Returns the blink::PointerData::Change for the given FlutterPointerPhase. inline blink::PointerData::Change ToPointerDataChange( FlutterPointerPhase phase) { switch (phase) { @@ -427,6 +439,12 @@ inline blink::PointerData::Change ToPointerDataChange( return blink::PointerData::Change::kDown; case kMove: return blink::PointerData::Change::kMove; + case kAdd: + return blink::PointerData::Change::kAdd; + case kRemove: + return blink::PointerData::Change::kRemove; + case kHover: + return blink::PointerData::Change::kHover; } return blink::PointerData::Change::kCancel; } @@ -451,6 +469,10 @@ FlutterResult FlutterEngineSendPointerEvent(FlutterEngine engine, pointer_data.kind = blink::PointerData::DeviceKind::kMouse; pointer_data.physical_x = SAFE_ACCESS(current, x, 0.0); pointer_data.physical_y = SAFE_ACCESS(current, y, 0.0); + pointer_data.kind = ToPointerDataDeviceKind( + SAFE_ACCESS(current, kind, FlutterPointerDeviceKind::kMouse)); + pointer_data.scroll_delta_x = SAFE_ACCESS(current, scroll_delta_x, 0.0); + pointer_data.scroll_delta_y = SAFE_ACCESS(current, scroll_delta_y, 0.0); packet->SetPointerData(i, pointer_data); current = reinterpret_cast( reinterpret_cast(current) + current->struct_size); diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 6a4928f0d0172..6196a1c9943cd 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -110,11 +110,20 @@ typedef struct { double pixel_ratio; } FlutterWindowMetricsEvent; +// The kind of device generating a pointer event. +typedef enum { + kMouse, +} FlutterPointerDeviceKind; + +// The phase of the pointer event. typedef enum { kCancel, kUp, kDown, kMove, + kAdd, + kRemove, + kHover, } FlutterPointerPhase; typedef struct { @@ -124,6 +133,9 @@ typedef struct { size_t timestamp; // in microseconds. double x; double y; + FlutterPointerDeviceKind kind; + double scroll_delta_x; + double scroll_delta_y; } FlutterPointerEvent; struct _FlutterPlatformMessageResponseHandle;