diff --git a/shell/platform/embedder/embedder.cc b/shell/platform/embedder/embedder.cc index b1f673ca1a8cc2d85a420384bb5dd31c3e6b9013..02d282a4a15ed596efe56341cd91c03ceb0207d8 100644 --- a/shell/platform/embedder/embedder.cc +++ b/shell/platform/embedder/embedder.cc @@ -1390,6 +1390,8 @@ inline flutter::PointerData::DeviceKind ToPointerDataKind( return flutter::PointerData::DeviceKind::kMouse; case kFlutterPointerDeviceKindTouch: return flutter::PointerData::DeviceKind::kTouch; + case kFlutterPointerDeviceKindStylus: + return flutter::PointerData::DeviceKind::kStylus; } return flutter::PointerData::DeviceKind::kMouse; } diff --git a/shell/platform/embedder/embedder.h b/shell/platform/embedder/embedder.h index 5678455b445c76806d667191ec5a74675a9e8c17..1729836fa7d971b28482963d03f84bbe2f2795a2 100644 --- a/shell/platform/embedder/embedder.h +++ b/shell/platform/embedder/embedder.h @@ -603,6 +603,7 @@ typedef enum { typedef enum { kFlutterPointerDeviceKindMouse = 1, kFlutterPointerDeviceKindTouch, + kFlutterPointerDeviceKindStylus, } FlutterPointerDeviceKind; /// Flags for the `buttons` field of `FlutterPointerEvent` when `device_kind` diff --git a/shell/platform/windows/flutter_window_win32.cc b/shell/platform/windows/flutter_window_win32.cc index 48c201cc0f52cde079f23b21811cd58007232724..b0e5d88a1e71f018cac3ae18175c33255abfd3dd 100644 --- a/shell/platform/windows/flutter_window_win32.cc +++ b/shell/platform/windows/flutter_window_win32.cc @@ -120,6 +120,24 @@ static uint64_t ConvertWinButtonToFlutterButton(UINT button) { return 0; } +// This method is only valid during a window message related to mouse/touch +// input. +// See +// https://docs.microsoft.com/en-us/windows/win32/tablet/system-events-and-mouse-messages?redirectedfrom=MSDN#distinguishing-pen-input-from-mouse-and-touch. +static FlutterPointerDeviceKind GetFlutterPointerDeviceKind() { + constexpr LPARAM kTouchOrPenSignature = 0xFF515700; + constexpr LPARAM kTouchSignature = kTouchOrPenSignature | 0x80; + constexpr LPARAM kSignatureMask = 0xFFFFFF00; + LPARAM info = GetMessageExtraInfo(); + if ((info & kSignatureMask) == kTouchOrPenSignature) { + if ((info & kTouchSignature) == kTouchSignature) { + return kFlutterPointerDeviceKindTouch; + } + return kFlutterPointerDeviceKindStylus; + } + return kFlutterPointerDeviceKindMouse; +} + void FlutterWindowWin32::OnDpiScale(unsigned int dpi){}; // When DesktopWindow notifies that a WM_Size message has come in @@ -131,14 +149,15 @@ void FlutterWindowWin32::OnResize(unsigned int width, unsigned int height) { } void FlutterWindowWin32::OnPointerMove(double x, double y) { - binding_handler_delegate_->OnPointerMove(x, y); + binding_handler_delegate_->OnPointerMove(x, y, GetFlutterPointerDeviceKind()); } void FlutterWindowWin32::OnPointerDown(double x, double y, UINT button) { uint64_t flutter_button = ConvertWinButtonToFlutterButton(button); if (flutter_button != 0) { binding_handler_delegate_->OnPointerDown( - x, y, static_cast(flutter_button)); + x, y, GetFlutterPointerDeviceKind(), + static_cast(flutter_button)); } } @@ -146,12 +165,13 @@ void FlutterWindowWin32::OnPointerUp(double x, double y, UINT button) { uint64_t flutter_button = ConvertWinButtonToFlutterButton(button); if (flutter_button != 0) { binding_handler_delegate_->OnPointerUp( - x, y, static_cast(flutter_button)); + x, y, GetFlutterPointerDeviceKind(), + static_cast(flutter_button)); } } void FlutterWindowWin32::OnPointerLeave() { - binding_handler_delegate_->OnPointerLeave(); + binding_handler_delegate_->OnPointerLeave(GetFlutterPointerDeviceKind()); } void FlutterWindowWin32::OnSetCursor() { diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index 2685da42c34aceeb48e807b29afabb3e368f76b1..68417005083ca6c5d00e3a84831ac00a830e4d0b 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -159,6 +159,38 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32 { MOCK_METHOD1(UpdateCursorRect, void(const Rect&)); }; +class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate { + public: + MockWindowBindingHandlerDelegate() {} + + // Prevent copying. + MockWindowBindingHandlerDelegate(MockWindowBindingHandlerDelegate const&) = + delete; + MockWindowBindingHandlerDelegate& operator=( + MockWindowBindingHandlerDelegate const&) = delete; + + MOCK_METHOD2(OnWindowSizeChanged, void(size_t, size_t)); + MOCK_METHOD3(OnPointerMove, void(double, double, FlutterPointerDeviceKind)); + MOCK_METHOD4(OnPointerDown, + void(double, + double, + FlutterPointerDeviceKind, + FlutterPointerMouseButtons)); + MOCK_METHOD4(OnPointerUp, + void(double, + double, + FlutterPointerDeviceKind, + FlutterPointerMouseButtons)); + MOCK_METHOD1(OnPointerLeave, void(FlutterPointerDeviceKind)); + MOCK_METHOD1(OnText, void(const std::u16string&)); + MOCK_METHOD6(OnKey, bool(int, int, int, char32_t, bool, bool)); + MOCK_METHOD0(OnComposeBegin, void()); + MOCK_METHOD0(OnComposeCommit, void()); + MOCK_METHOD0(OnComposeEnd, void()); + MOCK_METHOD2(OnComposeChange, void(const std::u16string&, int)); + MOCK_METHOD5(OnScroll, void(double, double, double, double, int)); +}; + // A FlutterWindowsView that overrides the RegisterKeyboardHandlers function // to register the keyboard hook handlers that can be spied upon. class TestFlutterWindowsView : public FlutterWindowsView { @@ -509,5 +541,76 @@ TEST(FlutterWindowWin32Test, OnCursorRectUpdatedHighDPI) { win32window.OnCursorRectUpdated(cursor_rect); } +TEST(FlutterWindowWin32Test, OnPointerStarSendsDeviceType) { + FlutterWindowWin32 win32window(100, 100); + MockWindowBindingHandlerDelegate delegate; + win32window.SetView(&delegate); + // Move + EXPECT_CALL(delegate, + OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindMouse)) + .Times(1); + EXPECT_CALL(delegate, + OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindTouch)) + .Times(1); + EXPECT_CALL(delegate, + OnPointerMove(10.0, 10.0, kFlutterPointerDeviceKindStylus)) + .Times(1); + + // Down + EXPECT_CALL(delegate, + OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindMouse, + kFlutterPointerButtonMousePrimary)) + .Times(1); + EXPECT_CALL(delegate, + OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindTouch, + kFlutterPointerButtonMousePrimary)) + .Times(1); + EXPECT_CALL(delegate, + OnPointerDown(10.0, 10.0, kFlutterPointerDeviceKindStylus, + kFlutterPointerButtonMousePrimary)) + .Times(1); + + // Up + EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindMouse, + kFlutterPointerButtonMousePrimary)) + .Times(1); + EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindTouch, + kFlutterPointerButtonMousePrimary)) + .Times(1); + EXPECT_CALL(delegate, OnPointerUp(10.0, 10.0, kFlutterPointerDeviceKindStylus, + kFlutterPointerButtonMousePrimary)) + .Times(1); + + // Leave + EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindMouse)) + .Times(1); + EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindTouch)) + .Times(1); + EXPECT_CALL(delegate, OnPointerLeave(kFlutterPointerDeviceKindStylus)) + .Times(1); + + win32window.OnPointerMove(10.0, 10.0); + win32window.OnPointerDown(10.0, 10.0, WM_LBUTTONDOWN); + win32window.OnPointerUp(10.0, 10.0, WM_LBUTTONDOWN); + win32window.OnPointerLeave(); + + // Touch + LPARAM original_lparam = SetMessageExtraInfo(0xFF51578b); + win32window.OnPointerMove(10.0, 10.0); + win32window.OnPointerDown(10.0, 10.0, WM_LBUTTONDOWN); + win32window.OnPointerUp(10.0, 10.0, WM_LBUTTONDOWN); + win32window.OnPointerLeave(); + + // Pen + SetMessageExtraInfo(0xFF515700); + win32window.OnPointerMove(10.0, 10.0); + win32window.OnPointerDown(10.0, 10.0, WM_LBUTTONDOWN); + win32window.OnPointerUp(10.0, 10.0, WM_LBUTTONDOWN); + win32window.OnPointerLeave(); + + // Reset extra info for other tests. + SetMessageExtraInfo(original_lparam); +} + } // namespace testing } // namespace flutter diff --git a/shell/platform/windows/flutter_window_winuwp.cc b/shell/platform/windows/flutter_window_winuwp.cc index a8bd92277a79b219d7846ec99d3493fc971b3e6f..447477fc3db2d7ec0f00ca4f86753e80209e297b 100644 --- a/shell/platform/windows/flutter_window_winuwp.cc +++ b/shell/platform/windows/flutter_window_winuwp.cc @@ -126,9 +126,11 @@ void FlutterWindowWinUWP::OnPointerPressed( winrt::Windows::UI::Core::PointerEventArgs const& args) { double x = GetPosX(args); double y = GetPosY(args); + FlutterPointerDeviceKind device_kind = GetPointerDeviceKind(args); binding_handler_delegate_->OnPointerDown( - x, y, FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary); + x, y, device_kind, + FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary); } void FlutterWindowWinUWP::OnPointerReleased( @@ -136,9 +138,11 @@ void FlutterWindowWinUWP::OnPointerReleased( winrt::Windows::UI::Core::PointerEventArgs const& args) { double x = GetPosX(args); double y = GetPosY(args); + FlutterPointerDeviceKind device_kind = GetPointerDeviceKind(args); binding_handler_delegate_->OnPointerUp( - x, y, FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary); + x, y, device_kind, + FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary); } void FlutterWindowWinUWP::OnPointerMoved( @@ -146,8 +150,9 @@ void FlutterWindowWinUWP::OnPointerMoved( winrt::Windows::UI::Core::PointerEventArgs const& args) { double x = GetPosX(args); double y = GetPosY(args); + FlutterPointerDeviceKind device_kind = GetPointerDeviceKind(args); - binding_handler_delegate_->OnPointerMove(x, y); + binding_handler_delegate_->OnPointerMove(x, y, device_kind); } void FlutterWindowWinUWP::OnPointerWheelChanged( @@ -176,6 +181,19 @@ double FlutterWindowWinUWP::GetPosY( inverse_dpi_scale); } +FlutterPointerDeviceKind FlutterWindowWinUWP::GetPointerDeviceKind( + winrt::Windows::UI::Core::PointerEventArgs const& args) { + switch (args.CurrentPoint().PointerDevice().PointerDeviceType()) { + case winrt::Windows::Devices::Input::PointerDeviceType::Mouse: + return kFlutterPointerDeviceKindMouse; + case winrt::Windows::Devices::Input::PointerDeviceType::Pen: + return kFlutterPointerDeviceKindStylus; + case winrt::Windows::Devices::Input::PointerDeviceType::Touch: + return kFlutterPointerDeviceKindTouch; + } + return kFlutterPointerDeviceKindMouse; +} + void FlutterWindowWinUWP::OnBoundsChanged( winrt::Windows::UI::ViewManagement::ApplicationView const& app_view, winrt::Windows::Foundation::IInspectable const&) { diff --git a/shell/platform/windows/flutter_window_winuwp.h b/shell/platform/windows/flutter_window_winuwp.h index e28684dbd61ae721c151aade236b549a71e88bf9..9870b96d759e6bd2012e98040dc1861dea20f320 100644 --- a/shell/platform/windows/flutter_window_winuwp.h +++ b/shell/platform/windows/flutter_window_winuwp.h @@ -5,6 +5,7 @@ #ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_UWP_FLUTTER_WINDOW_H_ #define FLUTTER_SHELL_PLATFORM_WINDOWS_UWP_FLUTTER_WINDOW_H_ +#include #include #include #include @@ -111,6 +112,10 @@ class FlutterWindowWinUWP : public WindowBindingHandler { // Converts from logical point to physical Y value. double GetPosY(winrt::Windows::UI::Core::PointerEventArgs const& args); + // Gets the pointer kind. + FlutterPointerDeviceKind GetPointerDeviceKind( + winrt::Windows::UI::Core::PointerEventArgs const& args); + // Backing CoreWindow. nullptr if not set. winrt::Windows::UI::Core::CoreWindow window_{nullptr}; diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index bc09824979e404013270ade46519e1165e48cdc4..aeac73f9d0b84319bd9a05460d1199b219c4ae07 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -167,34 +167,38 @@ void FlutterWindowsView::OnWindowSizeChanged(size_t width, size_t height) { } } -void FlutterWindowsView::OnPointerMove(double x, double y) { - SendPointerMove(x, y); +void FlutterWindowsView::OnPointerMove(double x, + double y, + FlutterPointerDeviceKind device_kind) { + SendPointerMove(x, y, device_kind); } void FlutterWindowsView::OnPointerDown( double x, double y, + FlutterPointerDeviceKind device_kind, FlutterPointerMouseButtons flutter_button) { if (flutter_button != 0) { uint64_t mouse_buttons = mouse_state_.buttons | flutter_button; SetMouseButtons(mouse_buttons); - SendPointerDown(x, y); + SendPointerDown(x, y, device_kind); } } void FlutterWindowsView::OnPointerUp( double x, double y, + FlutterPointerDeviceKind device_kind, FlutterPointerMouseButtons flutter_button) { if (flutter_button != 0) { uint64_t mouse_buttons = mouse_state_.buttons & ~flutter_button; SetMouseButtons(mouse_buttons); - SendPointerUp(x, y); + SendPointerUp(x, y, device_kind); } } -void FlutterWindowsView::OnPointerLeave() { - SendPointerLeave(); +void FlutterWindowsView::OnPointerLeave(FlutterPointerDeviceKind device_kind) { + SendPointerLeave(device_kind); } void FlutterWindowsView::OnText(const std::u16string& text) { @@ -275,36 +279,47 @@ void FlutterWindowsView::SetEventPhaseFromCursorButtonState( } } -void FlutterWindowsView::SendPointerMove(double x, double y) { +void FlutterWindowsView::SendPointerMove(double x, + double y, + FlutterPointerDeviceKind device_kind) { FlutterPointerEvent event = {}; event.x = x; event.y = y; + event.device_kind = device_kind; SetEventPhaseFromCursorButtonState(&event); SendPointerEventWithData(event); } -void FlutterWindowsView::SendPointerDown(double x, double y) { +void FlutterWindowsView::SendPointerDown(double x, + double y, + FlutterPointerDeviceKind device_kind) { FlutterPointerEvent event = {}; SetEventPhaseFromCursorButtonState(&event); event.x = x; event.y = y; + event.device_kind = device_kind; SendPointerEventWithData(event); SetMouseFlutterStateDown(true); } -void FlutterWindowsView::SendPointerUp(double x, double y) { +void FlutterWindowsView::SendPointerUp(double x, + double y, + FlutterPointerDeviceKind device_kind) { FlutterPointerEvent event = {}; SetEventPhaseFromCursorButtonState(&event); event.x = x; event.y = y; + event.device_kind = device_kind; SendPointerEventWithData(event); if (event.phase == FlutterPointerPhase::kUp) { SetMouseFlutterStateDown(false); } } -void FlutterWindowsView::SendPointerLeave() { +void FlutterWindowsView::SendPointerLeave( + FlutterPointerDeviceKind device_kind) { FlutterPointerEvent event = {}; + event.device_kind = device_kind; event.phase = FlutterPointerPhase::kRemove; SendPointerEventWithData(event); } @@ -392,7 +407,6 @@ void FlutterWindowsView::SendPointerEventWithData( } FlutterPointerEvent event = event_data; - event.device_kind = kFlutterPointerDeviceKindMouse; event.buttons = mouse_state_.buttons; // Set metadata that's always the same regardless of the event. diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 31923960b36b14b38e635ca1276797f8e97acf20..8b3ef7153a7a5577d6129f150fed74798aa049d4 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -87,20 +87,24 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, void OnWindowSizeChanged(size_t width, size_t height) override; // |WindowBindingHandlerDelegate| - void OnPointerMove(double x, double y) override; + void OnPointerMove(double x, + double y, + FlutterPointerDeviceKind device_kind) override; // |WindowBindingHandlerDelegate| void OnPointerDown(double x, double y, + FlutterPointerDeviceKind device_kind, FlutterPointerMouseButtons button) override; // |WindowBindingHandlerDelegate| void OnPointerUp(double x, double y, + FlutterPointerDeviceKind device_kind, FlutterPointerMouseButtons button) override; // |WindowBindingHandlerDelegate| - void OnPointerLeave() override; + void OnPointerLeave(FlutterPointerDeviceKind device_kind) override; // |WindowBindingHandlerDelegate| void OnText(const std::u16string&) override; @@ -178,20 +182,24 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, void SendWindowMetrics(size_t width, size_t height, double dpiscale) const; // Reports a mouse movement to Flutter engine. - void SendPointerMove(double x, double y); + void SendPointerMove(double x, + double y, + FlutterPointerDeviceKind device_kind); // Reports mouse press to Flutter engine. - void SendPointerDown(double x, double y); + void SendPointerDown(double x, + double y, + FlutterPointerDeviceKind device_kind); // Reports mouse release to Flutter engine. - void SendPointerUp(double x, double y); + void SendPointerUp(double x, double y, FlutterPointerDeviceKind device_kind); // Reports mouse left the window client area. // // Win32 api doesn't have "mouse enter" event. Therefore, there is no // SendPointerEnter method. A mouse enter event is tracked then the "move" // event is called. - void SendPointerLeave(); + void SendPointerLeave(FlutterPointerDeviceKind device_kind); // Reports a keyboard character to Flutter engine. void SendText(const std::u16string&); diff --git a/shell/platform/windows/game_pad_cursor_winuwp.cc b/shell/platform/windows/game_pad_cursor_winuwp.cc index c9daeee8b785f2798dcc0596943c63fd0c9ef8a3..676b5350445bb61d725dac6b34f8a05cb6f6cd54 100644 --- a/shell/platform/windows/game_pad_cursor_winuwp.cc +++ b/shell/platform/windows/game_pad_cursor_winuwp.cc @@ -159,7 +159,10 @@ void GamepadCursorWinUWP::OnGamepadLeftStickMoved(double x, double y) { winrt::Windows::Foundation::Numerics::float3 scaled = GetScaledInput(cursor_visual_.Offset()); - binding_handler_delegate_->OnPointerMove(scaled.x, scaled.y); + // TODO(dnfield): Support for gamepad as a distinct device type? + // https://github.com/flutter/flutter/issues/80472 + binding_handler_delegate_->OnPointerMove(scaled.x, scaled.y, + kFlutterPointerDeviceKindMouse); } } @@ -183,8 +186,10 @@ void GamepadCursorWinUWP::OnGamepadButtonPressed( // handling defered delivery, remove the need for action value. // https://github.com/flutter/flutter/issues/70202 + // TODO(dnfield): Support for gamepad as a distinct device type? + // https://github.com/flutter/flutter/issues/80472 binding_handler_delegate_->OnPointerDown( - scaled.x, scaled.y, + scaled.x, scaled.y, kFlutterPointerDeviceKindMouse, FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary); } else if ((buttons & winrt::Windows::Gaming::Input::GamepadButtons::DPadLeft) == @@ -225,8 +230,10 @@ void GamepadCursorWinUWP::OnGamepadButtonReleased( // handling defered delivery, remove the need for action value. // https://github.com/flutter/flutter/issues/70202 + // TODO(dnfield): Support for gamepad as a distinct device type? + // https://github.com/flutter/flutter/issues/80472 binding_handler_delegate_->OnPointerUp( - scaled.x, scaled.y, + scaled.x, scaled.y, kFlutterPointerDeviceKindMouse, FlutterPointerMouseButtons::kFlutterPointerButtonMousePrimary); } else if ((buttons & winrt::Windows::Gaming::Input::GamepadButtons::DPadLeft) == diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index fe839e6295316b9fe428fb3d066e04fdc14f803c..420bb9704d7c944688a6d23b46c140c207b3cc63 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -19,23 +19,27 @@ class WindowBindingHandlerDelegate { // Notifies delegate that backing window mouse has moved. // Typically called by currently configured WindowBindingHandler - virtual void OnPointerMove(double x, double y) = 0; + virtual void OnPointerMove(double x, + double y, + FlutterPointerDeviceKind device_kind) = 0; // Notifies delegate that backing window mouse pointer button has been // pressed. Typically called by currently configured WindowBindingHandler virtual void OnPointerDown(double x, double y, + FlutterPointerDeviceKind device_kind, FlutterPointerMouseButtons button) = 0; // Notifies delegate that backing window mouse pointer button has been // released. Typically called by currently configured WindowBindingHandler virtual void OnPointerUp(double x, double y, + FlutterPointerDeviceKind device_kind, FlutterPointerMouseButtons button) = 0; // Notifies delegate that backing window mouse pointer has left the window. // Typically called by currently configured WindowBindingHandler - virtual void OnPointerLeave() = 0; + virtual void OnPointerLeave(FlutterPointerDeviceKind device_kind) = 0; // Notifies delegate that backing window has received text. // Typically called by currently configured WindowBindingHandler