diff --git a/engine/src/flutter/shell/platform/embedder/embedder.cc b/engine/src/flutter/shell/platform/embedder/embedder.cc index 390e9388364..25b7152b0ad 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.cc +++ b/engine/src/flutter/shell/platform/embedder/embedder.cc @@ -699,6 +699,19 @@ inline flutter::PointerData::Change ToPointerDataChange( return flutter::PointerData::Change::kCancel; } +// Returns the flutter::PointerData::DeviceKind for the given +// FlutterPointerDeviceKind. +inline flutter::PointerData::DeviceKind ToPointerDataKind( + FlutterPointerDeviceKind device_kind) { + switch (device_kind) { + case kFlutterPointerDeviceKindMouse: + return flutter::PointerData::DeviceKind::kMouse; + case kFlutterPointerDeviceKindTouch: + return flutter::PointerData::DeviceKind::kTouch; + } + return flutter::PointerData::DeviceKind::kMouse; +} + // Returns the flutter::PointerData::SignalKind for the given // FlutterPointerSignaKind. inline flutter::PointerData::SignalKind ToPointerDataSignalKind( @@ -712,33 +725,24 @@ inline flutter::PointerData::SignalKind ToPointerDataSignalKind( return flutter::PointerData::SignalKind::kNone; } -// Returns the buttons for PointerData for the given buttons and change from a -// FlutterPointerEvent. -inline int64_t ToPointerDataButtons(int64_t buttons, - flutter::PointerData::Change change) { +// Returns the buttons to synthesize for a PointerData from a +// FlutterPointerEvent with no type or buttons set. +inline int64_t PointerDataButtonsForLegacyEvent( + flutter::PointerData::Change change) { switch (change) { case flutter::PointerData::Change::kDown: case flutter::PointerData::Change::kMove: // These kinds of change must have a non-zero `buttons`, otherwise gesture - // recognizers will ignore these events. To avoid breaking legacy - // embedders, it synthesizes a primary button when seeing `button = 0` and - // logs a warning to inform them to update. - if (buttons == 0) { - // TODO: Log a warning to inform the embedder to send the - // correct buttons. See - // https://github.com/flutter/flutter/issues/32052#issuecomment-489278965 - return flutter::kPointerButtonMousePrimary; - } - return buttons; - + // recognizers will ignore these events. + return flutter::kPointerButtonMousePrimary; case flutter::PointerData::Change::kCancel: case flutter::PointerData::Change::kAdd: case flutter::PointerData::Change::kRemove: case flutter::PointerData::Change::kHover: case flutter::PointerData::Change::kUp: - return buttons; + return 0; } - return buttons; + return 0; } FlutterEngineResult FlutterEngineSendPointerEvent( @@ -759,7 +763,6 @@ FlutterEngineResult FlutterEngineSendPointerEvent( pointer_data.time_stamp = SAFE_ACCESS(current, timestamp, 0); pointer_data.change = ToPointerDataChange( SAFE_ACCESS(current, phase, FlutterPointerPhase::kCancel)); - pointer_data.kind = flutter::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.device = SAFE_ACCESS(current, device, 0); @@ -767,10 +770,29 @@ FlutterEngineResult FlutterEngineSendPointerEvent( SAFE_ACCESS(current, signal_kind, kFlutterPointerSignalKindNone)); 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); - // TODO: Change 0 to a SAFE_ACCESS to current.buttons once this - // field is added. See - // https://github.com/flutter/flutter/issues/32052#issuecomment-489278965 - pointer_data.buttons = ToPointerDataButtons(0, pointer_data.change); + FlutterPointerDeviceKind device_kind = SAFE_ACCESS(current, device_kind, 0); + // For backwards compatibilty with embedders written before the device kind + // and buttons were exposed, if the device kind is not set treat it as a + // mouse, with a synthesized primary button state based on the phase. + if (device_kind == 0) { + pointer_data.kind = flutter::PointerData::DeviceKind::kMouse; + pointer_data.buttons = + PointerDataButtonsForLegacyEvent(pointer_data.change); + + } else { + pointer_data.kind = ToPointerDataKind(device_kind); + if (pointer_data.kind == flutter::PointerData::DeviceKind::kTouch) { + // For touch events, set the button internally rather than requiring + // it at the API level, since it's a confusing construction to expose. + if (pointer_data.change == flutter::PointerData::Change::kDown || + pointer_data.change == flutter::PointerData::Change::kMove) { + pointer_data.buttons = flutter::kPointerButtonTouchContact; + } + } else { + // Buttons use the same mask values, so pass them through directly. + pointer_data.buttons = SAFE_ACCESS(current, buttons, 0); + } + } packet->SetPointerData(i, pointer_data); current = reinterpret_cast( reinterpret_cast(current) + current->struct_size); diff --git a/engine/src/flutter/shell/platform/embedder/embedder.h b/engine/src/flutter/shell/platform/embedder/embedder.h index 3068a30befe..54af56fbe00 100644 --- a/engine/src/flutter/shell/platform/embedder/embedder.h +++ b/engine/src/flutter/shell/platform/embedder/embedder.h @@ -280,14 +280,57 @@ typedef struct { // The phase of the pointer event. typedef enum { kCancel, + // The pointer, which must have been down (see kDown), is now up. + // + // For touch, this means that the pointer is no longer in contact with the + // screen. For a mouse, it means the last button was released. Note that if + // any other buttons are still pressed when one button is released, that + // should be sent as a kMove rather than a kUp. kUp, + // The pointer, which must have been been up, is now down. + // + // For touch, this means that the pointer has come into contact with the + // screen. For a mouse, it means a button is now pressed. Note that if any + // other buttons are already pressed when a new button is pressed, that should + // be sent as a kMove rather than a kDown. kDown, + // The pointer moved while down. + // + // This is also used for changes in button state that don't cause a kDown or + // kUp, such as releasing one of two pressed buttons. kMove, + // The pointer is now sending input to Flutter. For instance, a mouse has + // entered the area where the Flutter content is displayed. + // + // A pointer should always be added before sending any other events. kAdd, + // The pointer is no longer sending input to Flutter. For instance, a mouse + // has left the area where the Flutter content is displayed. + // + // A removed pointer should no longer send events until sending a new kAdd. kRemove, + // The pointer moved while up. kHover, } FlutterPointerPhase; +// The device type that created a pointer event. +typedef enum { + kFlutterPointerDeviceKindMouse = 1, + kFlutterPointerDeviceKindTouch, +} FlutterPointerDeviceKind; + +// Flags for the |buttons| field of |FlutterPointerEvent| when |device_kind| +// is |kFlutterPointerDeviceKindMouse|. +typedef enum { + kFlutterPointerButtonMousePrimary = 1 << 0, + kFlutterPointerButtonMouseSecondary = 1 << 1, + kFlutterPointerButtonMouseMiddle = 1 << 2, + kFlutterPointerButtonMouseBack = 1 << 3, + kFlutterPointerButtonMouseForward = 1 << 4, + // If a mouse has more than five buttons, send higher bit shifted values + // corresponding to the button number: 1 << 5 for the 6th, etc. +} FlutterPointerMouseButtons; + // The type of a pointer signal. typedef enum { kFlutterPointerSignalKindNone, @@ -307,6 +350,14 @@ typedef struct { FlutterPointerSignalKind signal_kind; double scroll_delta_x; double scroll_delta_y; + // The type of the device generating this event. + // Backwards compatibility note: If this is not set, the device will be + // treated as a mouse, with the primary button set for |kDown| and |kMove|. + // If set explicitly to |kFlutterPointerDeviceKindMouse|, you must set the + // correct buttons. + FlutterPointerDeviceKind device_kind; + // The buttons currently pressed, if any. + int64_t buttons; } FlutterPointerEvent; struct _FlutterPlatformMessageResponseHandle;