From 4dbb9ba388e133f42fde26e722e7fc2dd4c1644d Mon Sep 17 00:00:00 2001 From: stuartmorgan Date: Wed, 22 May 2019 11:30:32 -0400 Subject: [PATCH] Expose pointer type and buttons in embedder.h (#9034) Rather than hard-coding the type of incoming events to mouse, and synthesizing a primary button press for kDown/kUp, expose device kind and buttons in the API. For backwards compatibility, if the type is not set, the old behavior is used. If an embedder sets the type to mouse explicitly, however, they must also set correct button information. For the touch type, the API abstracts away the framework's internal expectation that a button is set for touch down/move for simplicity. Fixes flutter/flutter#32854 --- shell/platform/embedder/embedder.cc | 66 +++++++++++++++++++---------- shell/platform/embedder/embedder.h | 51 ++++++++++++++++++++++ 2 files changed, 95 insertions(+), 22 deletions(-) diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index 390e93883640..25b7152b0ad2 100644 --- a/shell/platform/embedder/embedder.cc +++ b/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/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 3068a30befe2..54af56fbe00b 100644 --- a/shell/platform/embedder/embedder.h +++ b/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;