From 908a1d350757fd0ce7ae58515801c112860440a3 Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Fri, 11 Feb 2022 21:29:33 -0500 Subject: [PATCH 1/4] Win32 trackpad gestures --- ci/licenses_golden/licenses_flutter | 5 + shell/platform/windows/BUILD.gn | 11 +- shell/platform/windows/direct_manipulation.cc | 225 ++++++++++++++++++ shell/platform/windows/direct_manipulation.h | 86 +++++++ .../windows/direct_manipulation_unittests.cc | 195 +++++++++++++++ .../platform/windows/flutter_window_win32.cc | 8 + shell/platform/windows/flutter_window_win32.h | 2 + .../windows/flutter_window_win32_unittests.cc | 47 +--- .../platform/windows/flutter_windows_view.cc | 59 +++++ shell/platform/windows/flutter_windows_view.h | 29 +++ .../testing/mock_window_binding_handler.h | 1 + .../mock_window_binding_handler_delegate.h | 67 ++++++ .../platform/windows/window_binding_handler.h | 9 + .../windows/window_binding_handler_delegate.h | 8 + shell/platform/windows/window_win32.cc | 32 +++ shell/platform/windows/window_win32.h | 6 + 16 files changed, 743 insertions(+), 47 deletions(-) create mode 100644 shell/platform/windows/direct_manipulation.cc create mode 100644 shell/platform/windows/direct_manipulation.h create mode 100644 shell/platform/windows/direct_manipulation_unittests.cc create mode 100644 shell/platform/windows/testing/mock_window_binding_handler_delegate.h diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index bce31a41fce11..15257515af862 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2245,6 +2245,11 @@ FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plu FILE: ../../../flutter/shell/platform/windows/client_wrapper/plugin_registrar_windows_unittests.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.cc FILE: ../../../flutter/shell/platform/windows/cursor_handler.h +FILE: ../../../flutter/shell/platform/windows/direct_manipulation.cc +FILE: ../../../flutter/shell/platform/windows/direct_manipulation.h +FILE: ../../../flutter/shell/platform/windows/direct_manipulation_unittests.cc +FILE: ../../../flutter/shell/platform/windows/display_helper_winuwp.cc +FILE: ../../../flutter/shell/platform/windows/display_helper_winuwp.h FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32.cc FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32.h FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32_unittests.cc diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 51157138fdd69..78563bbffd25d 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -44,6 +44,8 @@ source_set("flutter_windows_source") { "angle_surface_manager.h", "cursor_handler.cc", "cursor_handler.h", + "direct_manipulation.cc", + "direct_manipulation.h", "dpi_utils_win32.cc", "dpi_utils_win32.h", "event_watcher_win32.cc", @@ -121,7 +123,10 @@ source_set("flutter_windows_source") { public_configs = [ ":relative_angle_headers" ] - defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES" ] + defines = [ + "_SILENCE_CLANG_COROUTINE_MESSAGE", + "FLUTTER_ENGINE_NO_PROTOTYPES", + ] public_deps = [ "//flutter/fml:string_conversion", @@ -130,6 +135,7 @@ source_set("flutter_windows_source") { deps = [ ":flutter_windows_headers", + "//flutter/fml:fml", "//flutter/shell/platform/common:common_cpp", "//flutter/shell/platform/common:common_cpp_input", "//flutter/shell/platform/common:common_cpp_switches", @@ -172,6 +178,7 @@ executable("flutter_windows_unittests") { # "flutter_project_bundle_unittests.cc", //TODO failing due to switches test failing. Blocked on https://github.com/flutter/flutter/issues/74153 # "flutter_windows_engine_unittests.cc", //TODO failing to send / receive platform message get plugins working first. Blocked on https://github.com/flutter/flutter/issues/74155 "accessibility_bridge_delegate_win32_unittests.cc", + "direct_manipulation_unittests.cc", "dpi_utils_win32_unittests.cc", "flutter_project_bundle_unittests.cc", "flutter_window_win32_unittests.cc", @@ -210,6 +217,8 @@ executable("flutter_windows_unittests") { public_configs = [ "//flutter:config" ] + defines = [ "_SILENCE_CLANG_COROUTINE_MESSAGE" ] + deps = [ ":flutter_windows_fixtures", ":flutter_windows_headers", diff --git a/shell/platform/windows/direct_manipulation.cc b/shell/platform/windows/direct_manipulation.cc new file mode 100644 index 0000000000000..cccda98184ffa --- /dev/null +++ b/shell/platform/windows/direct_manipulation.cc @@ -0,0 +1,225 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/fml/logging.h" + +#include "flutter/shell/platform/windows/direct_manipulation.h" +#include "flutter/shell/platform/windows/window_binding_handler_delegate.h" +#include "flutter/shell/platform/windows/window_win32.h" + +#define VERIFY_HR(operation) \ + if (FAILED(operation)) { \ + FML_LOG(ERROR) << #operation << " failed"; \ + manager_ = nullptr; \ + updateManager_ = nullptr; \ + viewport_ = nullptr; \ + return -1; \ + } + +#define WARN_HR(operation) \ + if (FAILED(operation)) { \ + FML_LOG(ERROR) << #operation << " failed"; \ + } + +namespace flutter { + +STDMETHODIMP DirectManipulationEventHandler::QueryInterface(REFIID iid, + void** ppv) { + if ((iid == IID_IUnknown) || + (iid == IID_IDirectManipulationViewportEventHandler)) { + *ppv = static_cast(this); + AddRef(); + return S_OK; + } else if (iid == IID_IDirectManipulationInteractionEventHandler) { + *ppv = static_cast(this); + AddRef(); + return S_OK; + } + return E_NOINTERFACE; +} + +HRESULT DirectManipulationEventHandler::OnViewportStatusChanged( + IDirectManipulationViewport* viewport, + DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) { + if (current == DIRECTMANIPULATION_RUNNING) { + if (!resetting_) { + // Not a false event + if (owner_->binding_handler_delegate) { + owner_->binding_handler_delegate->OnPointerPanZoomStart( + (int32_t) reinterpret_cast(this)); + } + } + } else if (previous == DIRECTMANIPULATION_RUNNING) { + if (resetting_) { + // The resetting transition has concluded + resetting_ = false; + } else { + if (owner_->binding_handler_delegate) { + owner_->binding_handler_delegate->OnPointerPanZoomEnd( + (int32_t) reinterpret_cast(this)); + } + // Need to reset the content transform to its original position + // so that we are ready for the next gesture + // Use resetting_ flag to prevent sending reset also to the framework + resetting_ = true; + RECT rect; + HRESULT hr = viewport->GetViewportRect(&rect); + if (FAILED(hr)) { + FML_LOG(ERROR) << "Failed to get the current viewport rect"; + return E_FAIL; + } + hr = viewport->ZoomToRect(rect.left, rect.top, rect.right, rect.bottom, + false); + if (FAILED(hr)) { + FML_LOG(ERROR) << "Failed to reset the gesture using ZoomToRect"; + return E_FAIL; + } + } + } + return S_OK; +} + +HRESULT DirectManipulationEventHandler::OnViewportUpdated( + IDirectManipulationViewport* viewport) { + return S_OK; +} + +HRESULT DirectManipulationEventHandler::OnContentUpdated( + IDirectManipulationViewport* viewport, + IDirectManipulationContent* content) { + float transform[6]; + HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform)); + if (FAILED(hr)) { + FML_LOG(ERROR) << "GetContentTransform failed"; + return S_OK; + } + if (!resetting_) { + // DirectManipulation provides updates with very high precision. If the user + // holds their fingers steady on a trackpad, DirectManipulation sends + // jittery updates. This calculation will reduce the precision of the scale + // value of the event to avoid jitter + const int mantissa_bits_chop = 2; + const float factor = (1 << mantissa_bits_chop) + 1; + float c = factor * transform[0]; + float scale = c - (c - transform[0]); + float pan_x = transform[4]; + float pan_y = transform[5]; + if (owner_->binding_handler_delegate) { + owner_->binding_handler_delegate->OnPointerPanZoomUpdate( + (int32_t) reinterpret_cast(this), pan_x, pan_y, scale, 0); + } + } + return S_OK; +} + +HRESULT DirectManipulationEventHandler::OnInteraction( + IDirectManipulationViewport2* viewport, + DIRECTMANIPULATION_INTERACTION_TYPE interaction) { + return S_OK; +} + +ULONG STDMETHODCALLTYPE DirectManipulationEventHandler::AddRef() { + RefCountedThreadSafe::AddRef(); + return 0; +} + +ULONG STDMETHODCALLTYPE DirectManipulationEventHandler::Release() { + RefCountedThreadSafe::Release(); + return 0; +} + +DirectManipulationOwner::DirectManipulationOwner(WindowWin32* window) + : window_(window) {} + +int DirectManipulationOwner::Init(unsigned int width, unsigned int height) { + VERIFY_HR(CoCreateInstance(CLSID_DirectManipulationManager, nullptr, + CLSCTX_INPROC_SERVER, + IID_IDirectManipulationManager, &manager_)); + VERIFY_HR(manager_->GetUpdateManager(IID_IDirectManipulationUpdateManager, + &updateManager_)); + VERIFY_HR(manager_->CreateViewport(nullptr, window_->GetWindowHandle(), + IID_IDirectManipulationViewport, + &viewport_)); + DIRECTMANIPULATION_CONFIGURATION configuration = + DIRECTMANIPULATION_CONFIGURATION_INTERACTION | + DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X | + DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y | + DIRECTMANIPULATION_CONFIGURATION_SCALING; + VERIFY_HR(viewport_->ActivateConfiguration(configuration)); + VERIFY_HR(viewport_->SetViewportOptions( + DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE)); + handler_ = fml::MakeRefCounted(window_, this); + VERIFY_HR(viewport_->AddEventHandler( + window_->GetWindowHandle(), handler_.get(), &viewportHandlerCookie_)); + RECT rect = {0, 0, (LONG)width, (LONG)height}; + VERIFY_HR(viewport_->SetViewportRect(&rect)); + VERIFY_HR(manager_->Activate(window_->GetWindowHandle())); + VERIFY_HR(viewport_->Enable()); + VERIFY_HR(updateManager_->Update(nullptr)); + return 0; +} + +void DirectManipulationOwner::ResizeViewport(unsigned int width, + unsigned int height) { + if (viewport_) { + RECT rect = {0, 0, (LONG)width, (LONG)height}; + WARN_HR(viewport_->SetViewportRect(&rect)); + } +} + +void DirectManipulationOwner::Destroy() { + if (handler_) { + handler_->window_ = nullptr; + handler_->owner_ = nullptr; + } + + if (viewport_) { + WARN_HR(viewport_->Disable()); + WARN_HR(viewport_->Disable()); + WARN_HR(viewport_->RemoveEventHandler(viewportHandlerCookie_)); + WARN_HR(viewport_->Abandon()); + } + + if (window_ && manager_) { + WARN_HR(manager_->Deactivate(window_->GetWindowHandle())); + } + + handler_ = nullptr; + viewport_ = nullptr; + updateManager_ = nullptr; + manager_ = nullptr; + window_ = nullptr; +} + +void DirectManipulationOwner::SetContact(UINT contactId) { + if (viewport_) { + viewport_->SetContact(contactId); + } +} + +void DirectManipulationOwner::SetBindingHandlerDelegate( + WindowBindingHandlerDelegate* delegate) { + binding_handler_delegate = delegate; +} + +void DirectManipulationOwner::Update() { + if (updateManager_) { + HRESULT hr = updateManager_->Update(nullptr); + if (FAILED(hr)) { + FML_LOG(ERROR) << "updateManager_->Update failed"; + auto error = GetLastError(); + FML_LOG(ERROR) << error; + LPWSTR message = nullptr; + size_t size = FormatMessageW( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), + reinterpret_cast(&message), 0, NULL); + FML_LOG(ERROR) << message; + } + } +} + +} // namespace flutter diff --git a/shell/platform/windows/direct_manipulation.h b/shell/platform/windows/direct_manipulation.h new file mode 100644 index 0000000000000..36e0b2edd0b6a --- /dev/null +++ b/shell/platform/windows/direct_manipulation.h @@ -0,0 +1,86 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_DIRECT_MANIPULATION_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_DIRECT_MANIPULATION_H_ + +#include "flutter/fml/memory/ref_counted.h" + +#include +#include "directmanipulation.h" + +namespace flutter { + +class WindowWin32; +class WindowBindingHandlerDelegate; + +class DirectManipulationEventHandler; + +class DirectManipulationOwner { + public: + explicit DirectManipulationOwner(WindowWin32* window); + int Init(unsigned int width, unsigned int height); + void ResizeViewport(unsigned int width, unsigned int height); + void SetBindingHandlerDelegate( + WindowBindingHandlerDelegate* binding_handler_delegate); + void SetContact(UINT contactId); + void Update(); + void Destroy(); + WindowBindingHandlerDelegate* binding_handler_delegate; + + private: + WindowWin32* window_; + DWORD viewportHandlerCookie_; + Microsoft::WRL::ComPtr manager_; + Microsoft::WRL::ComPtr updateManager_; + Microsoft::WRL::ComPtr viewport_; + fml::RefPtr handler_; +}; + +class DirectManipulationEventHandler + : public fml::RefCountedThreadSafe, + public IDirectManipulationViewportEventHandler, + public IDirectManipulationInteractionEventHandler { + friend class DirectManipulationOwner; + FML_FRIEND_REF_COUNTED_THREAD_SAFE(DirectManipulationEventHandler); + FML_FRIEND_MAKE_REF_COUNTED(DirectManipulationEventHandler); + + public: + explicit DirectManipulationEventHandler(WindowWin32* window, + DirectManipulationOwner* owner) + : window_(window), owner_(owner) {} + + STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; + + ULONG STDMETHODCALLTYPE AddRef() override; + ULONG STDMETHODCALLTYPE Release() override; + + HRESULT STDMETHODCALLTYPE + OnViewportStatusChanged(IDirectManipulationViewport* viewport, + DIRECTMANIPULATION_STATUS current, + DIRECTMANIPULATION_STATUS previous) override; + + HRESULT STDMETHODCALLTYPE + OnViewportUpdated(IDirectManipulationViewport* viewport) override; + + HRESULT STDMETHODCALLTYPE + OnContentUpdated(IDirectManipulationViewport* viewport, + IDirectManipulationContent* content) override; + + HRESULT STDMETHODCALLTYPE + OnInteraction(IDirectManipulationViewport2* viewport, + DIRECTMANIPULATION_INTERACTION_TYPE interaction) override; + + private: + WindowWin32* window_; + DirectManipulationOwner* owner_; + // We need to reset some parts of DirectManipulation after each gesture + // A flag is needed to ensure that false events created as the reset occurs + // are not sent to the flutter framework. + bool resetting_ = false; +}; + +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_DIRECT_MANIPULATION_H_ diff --git a/shell/platform/windows/direct_manipulation_unittests.cc b/shell/platform/windows/direct_manipulation_unittests.cc new file mode 100644 index 0000000000000..dd32ee97c9a22 --- /dev/null +++ b/shell/platform/windows/direct_manipulation_unittests.cc @@ -0,0 +1,195 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "flutter/shell/platform/windows/direct_manipulation.h" +#include "flutter/shell/platform/windows/testing/mock_window_binding_handler_delegate.h" + +#include "gtest/gtest.h" + +using testing::_; + +namespace flutter { +namespace testing { + +class MockIDirectManipulationViewport : public IDirectManipulationViewport { + public: + MockIDirectManipulationViewport() {} + + // Prevent copying. + MockIDirectManipulationViewport(MockIDirectManipulationViewport const&) = + delete; + MockIDirectManipulationViewport& operator=( + MockIDirectManipulationViewport const&) = delete; + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, AddRef, ULONG()); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Release, ULONG()); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + QueryInterface, + HRESULT(REFIID, void**)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Abandon, HRESULT()); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + ActivateConfiguration, + HRESULT(DIRECTMANIPULATION_CONFIGURATION)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + AddConfiguration, + HRESULT(DIRECTMANIPULATION_CONFIGURATION)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + AddContent, + HRESULT(IDirectManipulationContent*)); + MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, + AddEventHandler, + HRESULT(HWND, + IDirectManipulationViewportEventHandler*, + DWORD*)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Disable, HRESULT()); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Enable, HRESULT()); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetPrimaryContent, + HRESULT(REFIID, void**)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetStatus, + HRESULT(DIRECTMANIPULATION_STATUS*)); + MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetTag, + HRESULT(REFIID, void**, UINT32*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetViewportRect, + HRESULT(RECT*)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, ReleaseAllContacts, HRESULT()); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + ReleaseContact, + HRESULT(UINT32)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + RemoveConfiguration, + HRESULT(DIRECTMANIPULATION_CONFIGURATION)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + RemoveContent, + HRESULT(IDirectManipulationContent*)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + RemoveEventHandler, + HRESULT(DWORD)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetChaining, + HRESULT(DIRECTMANIPULATION_MOTION_TYPES)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, SetContact, HRESULT(UINT32)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetInputMode, + HRESULT(DIRECTMANIPULATION_INPUT_MODE)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetManualGesture, + HRESULT(DIRECTMANIPULATION_GESTURE_CONFIGURATION)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetTag, + HRESULT(IUnknown*, UINT32)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetUpdateMode, + HRESULT(DIRECTMANIPULATION_INPUT_MODE)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetViewportOptions, + HRESULT(DIRECTMANIPULATION_VIEWPORT_OPTIONS)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetViewportRect, + HRESULT(const RECT*)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetViewportTransform, + HRESULT(const float*, DWORD)); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Stop, HRESULT()); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + SyncDisplayTransform, + HRESULT(const float*, DWORD)); + MOCK_METHOD5_WITH_CALLTYPE( + STDMETHODCALLTYPE, + ZoomToRect, + HRESULT(const float, const float, const float, const float, BOOL)); +}; + +class MockIDirectManipulationContent : public IDirectManipulationContent { + public: + MockIDirectManipulationContent() {} + + // Prevent copying. + MockIDirectManipulationContent(MockIDirectManipulationContent const&) = + delete; + MockIDirectManipulationContent& operator=( + MockIDirectManipulationContent const&) = delete; + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, AddRef, ULONG()); + MOCK_METHOD0_WITH_CALLTYPE(STDMETHODCALLTYPE, Release, ULONG()); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + QueryInterface, + HRESULT(REFIID, void**)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, GetContentRect, HRESULT(RECT*)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetContentTransform, + HRESULT(float*, DWORD)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetOutputTransform, + HRESULT(float*, DWORD)); + MOCK_METHOD3_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetTag, + HRESULT(REFIID, void**, UINT32*)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + GetViewport, + HRESULT(REFIID, void**)); + MOCK_METHOD1_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetContentRect, + HRESULT(const RECT*)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + SetTag, + HRESULT(IUnknown*, UINT32)); + MOCK_METHOD2_WITH_CALLTYPE(STDMETHODCALLTYPE, + SyncContentTransform, + HRESULT(const float*, DWORD)); +}; + +TEST(DirectManipulationTest, TestGesture) { + MockIDirectManipulationContent content; + MockWindowBindingHandlerDelegate delegate; + MockIDirectManipulationViewport viewport; + const float scale = 1.1; + const float scale_rounded = 1.0999999046325684; + const float pan_x = 32.0; + const float pan_y = 16.0; + const int DISPLAY_WIDTH = 800; + const int DISPLAY_HEIGHT = 600; + auto owner = std::make_unique(nullptr); + owner->SetBindingHandlerDelegate(&delegate); + auto handler = + fml::MakeRefCounted(nullptr, owner.get()); + int32_t device_id = (int32_t) reinterpret_cast(handler.get()); + EXPECT_CALL(delegate, OnPointerPanZoomStart(device_id)); + handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport, + DIRECTMANIPULATION_RUNNING, + DIRECTMANIPULATION_READY); + EXPECT_CALL(content, GetContentTransform(_, 6)) + .WillOnce(::testing::Invoke( + [scale, pan_x, pan_y](float* transform, DWORD size) { + transform[0] = scale; + transform[4] = pan_x; + transform[5] = pan_y; + return S_OK; + })); + EXPECT_CALL(delegate, OnPointerPanZoomUpdate(device_id, pan_x, pan_y, + scale_rounded, 0)); + handler->OnContentUpdated((IDirectManipulationViewport*)&viewport, + (IDirectManipulationContent*)&content); + EXPECT_CALL(delegate, OnPointerPanZoomEnd(device_id)); + EXPECT_CALL(viewport, GetViewportRect(_)) + .WillOnce(::testing::Invoke([DISPLAY_WIDTH, DISPLAY_HEIGHT](RECT* rect) { + rect->left = 0; + rect->top = 0; + rect->right = DISPLAY_WIDTH; + rect->bottom = DISPLAY_HEIGHT; + return S_OK; + })); + EXPECT_CALL(viewport, ZoomToRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, false)) + .WillOnce(::testing::Return(S_OK)); + handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport, + DIRECTMANIPULATION_INERTIA, + DIRECTMANIPULATION_RUNNING); + handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport, + DIRECTMANIPULATION_READY, + DIRECTMANIPULATION_INERTIA); +} + +} // namespace testing +} // namespace flutter diff --git a/shell/platform/windows/flutter_window_win32.cc b/shell/platform/windows/flutter_window_win32.cc index 15c87d6ed7016..96488675a95a1 100644 --- a/shell/platform/windows/flutter_window_win32.cc +++ b/shell/platform/windows/flutter_window_win32.cc @@ -75,6 +75,7 @@ FlutterWindowWin32::~FlutterWindowWin32() {} void FlutterWindowWin32::SetView(WindowBindingHandlerDelegate* window) { binding_handler_delegate_ = window; + direct_manipulation_owner_->SetBindingHandlerDelegate(window); } WindowsRenderTarget FlutterWindowWin32::GetRenderTarget() { @@ -263,4 +264,11 @@ gfx::NativeViewAccessible FlutterWindowWin32::GetNativeViewAccessible() { return binding_handler_delegate_->GetNativeViewAccessible(); } +PointerLocation FlutterWindowWin32::GetPrimaryPointerLocation() { + POINT point; + GetCursorPos(&point); + ScreenToClient(GetWindowHandle(), &point); + return {(size_t)point.x, (size_t)point.y}; +} + } // namespace flutter diff --git a/shell/platform/windows/flutter_window_win32.h b/shell/platform/windows/flutter_window_win32.h index fdcd2740298ce..26a7325f55456 100644 --- a/shell/platform/windows/flutter_window_win32.h +++ b/shell/platform/windows/flutter_window_win32.h @@ -134,6 +134,8 @@ class FlutterWindowWin32 : public WindowWin32, public WindowBindingHandler { size_t row_bytes, size_t height) override; + PointerLocation GetPrimaryPointerLocation() override; + private: // A pointer to a FlutterWindowsView that can be used to update engine // windowing and input state. diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index 549a9d09be80a..c86f3e2b7189a 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -11,6 +11,7 @@ #include "flutter/shell/platform/windows/testing/engine_modifier.h" #include "flutter/shell/platform/windows/testing/flutter_window_win32_test.h" #include "flutter/shell/platform/windows/testing/mock_window_binding_handler.h" +#include "flutter/shell/platform/windows/testing/mock_window_binding_handler_delegate.h" #include "flutter/shell/platform/windows/testing/test_keyboard.h" #include "flutter/shell/platform/windows/text_input_plugin.h" #include "flutter/shell/platform/windows/text_input_plugin_delegate.h" @@ -138,52 +139,6 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32 { } }; -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_METHOD4(OnPointerMove, - void(double, double, FlutterPointerDeviceKind, int32_t)); - MOCK_METHOD5(OnPointerDown, - void(double, - double, - FlutterPointerDeviceKind, - int32_t, - FlutterPointerMouseButtons)); - MOCK_METHOD5(OnPointerUp, - void(double, - double, - FlutterPointerDeviceKind, - int32_t, - FlutterPointerMouseButtons)); - MOCK_METHOD2(OnPointerLeave, void(FlutterPointerDeviceKind, int32_t)); - MOCK_METHOD1(OnText, void(const std::u16string&)); - MOCK_METHOD7(OnKey, - void(int, int, int, char32_t, bool, bool, KeyEventCallback)); - MOCK_METHOD0(OnComposeBegin, void()); - MOCK_METHOD0(OnComposeCommit, void()); - MOCK_METHOD0(OnComposeEnd, void()); - MOCK_METHOD2(OnComposeChange, void(const std::u16string&, int)); - MOCK_METHOD1(OnUpdateSemanticsEnabled, void(bool)); - MOCK_METHOD0(GetNativeViewAccessible, gfx::NativeViewAccessible()); - MOCK_METHOD7(OnScroll, - void(double, - double, - double, - double, - int, - FlutterPointerDeviceKind, - int32_t)); - MOCK_METHOD0(OnPlatformBrightnessChanged, void()); -}; - // A FlutterWindowsView that overrides the RegisterKeyboardHandlers function // to register the keyboard hook handlers that can be spied upon. class TestFlutterWindowsView : public FlutterWindowsView { diff --git a/shell/platform/windows/flutter_windows_view.cc b/shell/platform/windows/flutter_windows_view.cc index e0ccb8cab6eab..4d9dba11b7db0 100644 --- a/shell/platform/windows/flutter_windows_view.cc +++ b/shell/platform/windows/flutter_windows_view.cc @@ -194,6 +194,23 @@ void FlutterWindowsView::OnPointerLeave(FlutterPointerDeviceKind device_kind, SendPointerLeave(GetOrCreatePointerState(device_kind, device_id)); } +void FlutterWindowsView::OnPointerPanZoomStart(int32_t device_id) { + PointerLocation point = binding_handler_->GetPrimaryPointerLocation(); + SendPointerPanZoomStart(device_id, point.x, point.y); +} + +void FlutterWindowsView::OnPointerPanZoomUpdate(int32_t device_id, + double pan_x, + double pan_y, + double scale, + double rotation) { + SendPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, rotation); +} + +void FlutterWindowsView::OnPointerPanZoomEnd(int32_t device_id) { + SendPointerPanZoomEnd(device_id); +} + void FlutterWindowsView::OnText(const std::u16string& text) { SendText(text); } @@ -368,6 +385,48 @@ void FlutterWindowsView::SendPointerLeave(PointerState* state) { SendPointerEventWithData(event, state); } +void FlutterWindowsView::SendPointerPanZoomStart(int32_t device_id, + double x, + double y) { + auto state = + GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id); + state->pan_zoom_start_x = x; + state->pan_zoom_start_y = y; + FlutterPointerEvent event = {}; + event.x = x; + event.y = y; + event.phase = FlutterPointerPhase::kPanZoomStart; + SendPointerEventWithData(event, state); +} + +void FlutterWindowsView::SendPointerPanZoomUpdate(int32_t device_id, + double pan_x, + double pan_y, + double scale, + double rotation) { + auto state = + GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id); + FlutterPointerEvent event = {}; + event.x = state->pan_zoom_start_x; + event.y = state->pan_zoom_start_y; + event.pan_x = pan_x; + event.pan_y = pan_y; + event.scale = scale; + event.rotation = rotation; + event.phase = FlutterPointerPhase::kPanZoomUpdate; + SendPointerEventWithData(event, state); +} + +void FlutterWindowsView::SendPointerPanZoomEnd(int32_t device_id) { + auto state = + GetOrCreatePointerState(kFlutterPointerDeviceKindTrackpad, device_id); + FlutterPointerEvent event = {}; + event.x = state->pan_zoom_start_x; + event.y = state->pan_zoom_start_y; + event.phase = FlutterPointerPhase::kPanZoomEnd; + SendPointerEventWithData(event, state); +} + void FlutterWindowsView::SendText(const std::u16string& text) { text_input_plugin_->TextHook(text); } diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 51a4a6ca2bb73..02cb1b2496486 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -123,6 +123,19 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, void OnPointerLeave(FlutterPointerDeviceKind device_kind, int32_t device_id = 0) override; + // |WindowBindingHandlerDelegate| + virtual void OnPointerPanZoomStart(int32_t device_id) override; + + // |WindowBindingHandlerDelegate| + virtual void OnPointerPanZoomUpdate(int32_t device_id, + double pan_x, + double pan_y, + double scale, + double rotation) override; + + // |WindowBindingHandlerDelegate| + virtual void OnPointerPanZoomEnd(int32_t device_id) override; + // |WindowBindingHandlerDelegate| void OnText(const std::u16string&) override; @@ -205,6 +218,12 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // The currently pressed buttons, as represented in FlutterPointerEvent. uint64_t buttons = 0; + + // The x position where the last pan/zoom started + double pan_zoom_start_x = 0; + + // The y position where the last pan/zoom started + double pan_zoom_start_y = 0; }; // States a resize event can be in. @@ -245,6 +264,16 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // event is called. void SendPointerLeave(PointerState* state); + void SendPointerPanZoomStart(int32_t device_id, double x, double y); + + void SendPointerPanZoomUpdate(int32_t device_id, + double pan_x, + double pan_y, + double scale, + double rotation); + + void SendPointerPanZoomEnd(int32_t device_id); + // Reports a keyboard character to Flutter engine. void SendText(const std::u16string&); diff --git a/shell/platform/windows/testing/mock_window_binding_handler.h b/shell/platform/windows/testing/mock_window_binding_handler.h index a5395c96cca02..a9eac85ba4320 100644 --- a/shell/platform/windows/testing/mock_window_binding_handler.h +++ b/shell/platform/windows/testing/mock_window_binding_handler.h @@ -35,6 +35,7 @@ class MockWindowBindingHandler : public WindowBindingHandler { MOCK_METHOD0(OnResetImeComposing, void()); MOCK_METHOD3(OnBitmapSurfaceUpdated, bool(const void* allocation, size_t row_bytes, size_t height)); + MOCK_METHOD0(GetPrimaryPointerLocation, PointerLocation()); }; } // namespace testing diff --git a/shell/platform/windows/testing/mock_window_binding_handler_delegate.h b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h new file mode 100644 index 0000000000000..67bbac201c0ce --- /dev/null +++ b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h @@ -0,0 +1,67 @@ +// Copyright 2013 The Flutter Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_WINDOW_BINDING_HANDLER_DELEGATE_H_ +#define FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_WINDOW_BINDING_HANDLER_DELEGATE_H_ + +#include "flutter/shell/platform/windows/window_binding_handler_delegate.h" +#include "gmock/gmock.h" + +namespace flutter { +namespace testing { + +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_METHOD4(OnPointerMove, + void(double, double, FlutterPointerDeviceKind, int32_t)); + MOCK_METHOD5(OnPointerDown, + void(double, + double, + FlutterPointerDeviceKind, + int32_t, + FlutterPointerMouseButtons)); + MOCK_METHOD5(OnPointerUp, + void(double, + double, + FlutterPointerDeviceKind, + int32_t, + FlutterPointerMouseButtons)); + MOCK_METHOD2(OnPointerLeave, void(FlutterPointerDeviceKind, int32_t)); + MOCK_METHOD1(OnPointerPanZoomStart, void(int32_t)); + MOCK_METHOD5(OnPointerPanZoomUpdate, + void(int32_t, double, double, double, double)); + MOCK_METHOD1(OnPointerPanZoomEnd, void(int32_t)); + MOCK_METHOD1(OnText, void(const std::u16string&)); + MOCK_METHOD7(OnKey, + void(int, int, int, char32_t, bool, bool, KeyEventCallback)); + MOCK_METHOD0(OnComposeBegin, void()); + MOCK_METHOD0(OnComposeCommit, void()); + MOCK_METHOD0(OnComposeEnd, void()); + MOCK_METHOD2(OnComposeChange, void(const std::u16string&, int)); + MOCK_METHOD1(OnUpdateSemanticsEnabled, void(bool)); + MOCK_METHOD0(GetNativeViewAccessible, gfx::NativeViewAccessible()); + MOCK_METHOD7(OnScroll, + void(double, + double, + double, + double, + int, + FlutterPointerDeviceKind, + int32_t)); + MOCK_METHOD0(OnPlatformBrightnessChanged, void()); +}; + +} // namespace testing +} // namespace flutter + +#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_TESTING_MOCK_WINDOW_BINDING_HANDLER_DELEGATE_H_ diff --git a/shell/platform/windows/window_binding_handler.h b/shell/platform/windows/window_binding_handler.h index 40788092ab98d..7a4cc27424eef 100644 --- a/shell/platform/windows/window_binding_handler.h +++ b/shell/platform/windows/window_binding_handler.h @@ -24,6 +24,12 @@ struct PhysicalWindowBounds { size_t height; }; +// Structure containing the position of a mouse pointer +struct PointerLocation { + size_t x; + size_t y; +}; + // Type representing an underlying platform window. using PlatformWindow = HWND; @@ -78,6 +84,9 @@ class WindowBindingHandler { // Invoked when the app ends IME composing, such when the active text input // client is cleared. virtual void OnResetImeComposing() = 0; + + // Returns the last known position of the primary pointer + virtual PointerLocation GetPrimaryPointerLocation() = 0; }; } // namespace flutter diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index 1f971ce151000..2411737fdef17 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -50,6 +50,14 @@ class WindowBindingHandlerDelegate { virtual void OnPointerLeave(FlutterPointerDeviceKind device_kind, int32_t device_id) = 0; + virtual void OnPointerPanZoomStart(int32_t device_id) = 0; + virtual void OnPointerPanZoomUpdate(int32_t device_id, + double pan_x, + double pan_y, + double scale, + double rotation) = 0; + virtual void OnPointerPanZoomEnd(int32_t device_id) = 0; + // Notifies delegate that backing window has received text. // Typically called by currently configured WindowBindingHandler virtual void OnText(const std::u16string&) = 0; diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index d3a8ecf134cf7..446c77778393d 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -95,6 +95,17 @@ void WindowWin32::InitializeChild(const char* title, OutputDebugString(message); LocalFree(message); } + DEVMODE dmi; + ZeroMemory(&dmi, sizeof(dmi)); + dmi.dmSize = sizeof(dmi); + if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmi)) { + framerate_ = dmi.dmDisplayFrequency; + SetTimer(result, kVsyncTimer, 1000 / framerate_, nullptr); + } else { + OutputDebugString(L"Failed to get framerate"); + } + direct_manipulation_owner_ = std::make_unique(this); + direct_manipulation_owner_->Init(width, height); } std::wstring WindowWin32::NarrowToWide(const char* source) { @@ -442,6 +453,24 @@ WindowWin32::HandleMessage(UINT const message, } break; } + case WM_TIMER: + if (wparam == kVsyncTimer) { + direct_manipulation_owner_->Update(); + SetTimer(window_handle_, kVsyncTimer, 1000 / framerate_, nullptr); + return 0; + } + break; + case DM_POINTERHITTEST: { + if (direct_manipulation_owner_) { + UINT contactId = GET_POINTERID_WPARAM(wparam); + POINTER_INPUT_TYPE pointerType; + if (GetPointerType(contactId, &pointerType) && + pointerType == PT_TOUCHPAD) { + direct_manipulation_owner_->SetContact(contactId); + } + } + break; + } case WM_INPUTLANGCHANGE: // TODO(cbracken): pass this to TextInputManager to aid with // language-specific issues. @@ -529,6 +558,9 @@ void WindowWin32::Destroy() { void WindowWin32::HandleResize(UINT width, UINT height) { current_width_ = width; current_height_ = height; + if (direct_manipulation_owner_) { + direct_manipulation_owner_->ResizeViewport(width, height); + } OnResize(width, height); } diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index 4c30275fcf37d..b16ab7de14065 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -14,6 +14,7 @@ #include #include "flutter/shell/platform/embedder/embedder.h" +#include "flutter/shell/platform/windows/direct_manipulation.h" #include "flutter/shell/platform/windows/keyboard_manager_win32.h" #include "flutter/shell/platform/windows/sequential_id_generator.h" #include "flutter/shell/platform/windows/text_input_manager_win32.h" @@ -209,6 +210,8 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { // Returns the root view accessibility node, or nullptr if none. virtual gfx::NativeViewAccessible GetNativeViewAccessible() = 0; + std::unique_ptr direct_manipulation_owner_; + private: // Release OS resources associated with window. void Destroy(); @@ -254,6 +257,9 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { // Generates touch point IDs for touch events. SequentialIdGenerator touch_id_generator_; + + const static int kVsyncTimer = 1; + int framerate_ = 1; }; } // namespace flutter From 4bd33fdb9e6b6ece1440517d8cd4a6a6b05cd6e4 Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Sat, 30 Apr 2022 01:04:16 -0400 Subject: [PATCH 2/4] Address feedback --- ci/licenses_golden/licenses_flutter | 2 - shell/platform/windows/BUILD.gn | 7 +- shell/platform/windows/direct_manipulation.cc | 105 +++++++++--------- shell/platform/windows/direct_manipulation.h | 40 ++++++- .../windows/direct_manipulation_unittests.cc | 9 +- shell/platform/windows/flutter_window_win32.h | 1 + shell/platform/windows/flutter_windows_view.h | 4 +- .../platform/windows/window_binding_handler.h | 6 +- .../windows/window_binding_handler_delegate.h | 8 ++ shell/platform/windows/window_win32.cc | 16 ++- shell/platform/windows/window_win32.h | 9 +- 11 files changed, 124 insertions(+), 83 deletions(-) diff --git a/ci/licenses_golden/licenses_flutter b/ci/licenses_golden/licenses_flutter index 15257515af862..7c87155fafc9d 100644 --- a/ci/licenses_golden/licenses_flutter +++ b/ci/licenses_golden/licenses_flutter @@ -2248,8 +2248,6 @@ FILE: ../../../flutter/shell/platform/windows/cursor_handler.h FILE: ../../../flutter/shell/platform/windows/direct_manipulation.cc FILE: ../../../flutter/shell/platform/windows/direct_manipulation.h FILE: ../../../flutter/shell/platform/windows/direct_manipulation_unittests.cc -FILE: ../../../flutter/shell/platform/windows/display_helper_winuwp.cc -FILE: ../../../flutter/shell/platform/windows/display_helper_winuwp.h FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32.cc FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32.h FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32_unittests.cc diff --git a/shell/platform/windows/BUILD.gn b/shell/platform/windows/BUILD.gn index 78563bbffd25d..ba24be8dca84c 100644 --- a/shell/platform/windows/BUILD.gn +++ b/shell/platform/windows/BUILD.gn @@ -123,10 +123,7 @@ source_set("flutter_windows_source") { public_configs = [ ":relative_angle_headers" ] - defines = [ - "_SILENCE_CLANG_COROUTINE_MESSAGE", - "FLUTTER_ENGINE_NO_PROTOTYPES", - ] + defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES" ] public_deps = [ "//flutter/fml:string_conversion", @@ -217,8 +214,6 @@ executable("flutter_windows_unittests") { public_configs = [ "//flutter:config" ] - defines = [ "_SILENCE_CLANG_COROUTINE_MESSAGE" ] - deps = [ ":flutter_windows_fixtures", ":flutter_windows_headers", diff --git a/shell/platform/windows/direct_manipulation.cc b/shell/platform/windows/direct_manipulation.cc index cccda98184ffa..60eba79fb84dc 100644 --- a/shell/platform/windows/direct_manipulation.cc +++ b/shell/platform/windows/direct_manipulation.cc @@ -8,7 +8,7 @@ #include "flutter/shell/platform/windows/window_binding_handler_delegate.h" #include "flutter/shell/platform/windows/window_win32.h" -#define VERIFY_HR(operation) \ +#define RETURN_IF_FAILED(operation) \ if (FAILED(operation)) { \ FML_LOG(ERROR) << #operation << " failed"; \ manager_ = nullptr; \ @@ -17,7 +17,7 @@ return -1; \ } -#define WARN_HR(operation) \ +#define WARN_IF_FAILED(operation) \ if (FAILED(operation)) { \ FML_LOG(ERROR) << #operation << " failed"; \ } @@ -43,39 +43,37 @@ HRESULT DirectManipulationEventHandler::OnViewportStatusChanged( IDirectManipulationViewport* viewport, DIRECTMANIPULATION_STATUS current, DIRECTMANIPULATION_STATUS previous) { - if (current == DIRECTMANIPULATION_RUNNING) { - if (!resetting_) { - // Not a false event + if (during_synthesized_reset_ && previous == DIRECTMANIPULATION_RUNNING) { + during_synthesized_reset_ = false; + } else if (current == DIRECTMANIPULATION_RUNNING) { + if (!during_synthesized_reset_) { + // Not a false event. if (owner_->binding_handler_delegate) { owner_->binding_handler_delegate->OnPointerPanZoomStart( (int32_t) reinterpret_cast(this)); } } } else if (previous == DIRECTMANIPULATION_RUNNING) { - if (resetting_) { - // The resetting transition has concluded - resetting_ = false; - } else { - if (owner_->binding_handler_delegate) { - owner_->binding_handler_delegate->OnPointerPanZoomEnd( - (int32_t) reinterpret_cast(this)); - } - // Need to reset the content transform to its original position - // so that we are ready for the next gesture - // Use resetting_ flag to prevent sending reset also to the framework - resetting_ = true; - RECT rect; - HRESULT hr = viewport->GetViewportRect(&rect); - if (FAILED(hr)) { - FML_LOG(ERROR) << "Failed to get the current viewport rect"; - return E_FAIL; - } - hr = viewport->ZoomToRect(rect.left, rect.top, rect.right, rect.bottom, - false); - if (FAILED(hr)) { - FML_LOG(ERROR) << "Failed to reset the gesture using ZoomToRect"; - return E_FAIL; - } + if (owner_->binding_handler_delegate) { + owner_->binding_handler_delegate->OnPointerPanZoomEnd( + (int32_t) reinterpret_cast(this)); + } + // Need to reset the content transform to its original position + // so that we are ready for the next gesture. + // Use during_synthesized_reset_ flag to prevent sending reset also to the + // framework. + during_synthesized_reset_ = true; + RECT rect; + HRESULT hr = viewport->GetViewportRect(&rect); + if (FAILED(hr)) { + FML_LOG(ERROR) << "Failed to get the current viewport rect"; + return E_FAIL; + } + hr = viewport->ZoomToRect(rect.left, rect.top, rect.right, rect.bottom, + false); + if (FAILED(hr)) { + FML_LOG(ERROR) << "Failed to reset the gesture using ZoomToRect"; + return E_FAIL; } } return S_OK; @@ -95,11 +93,11 @@ HRESULT DirectManipulationEventHandler::OnContentUpdated( FML_LOG(ERROR) << "GetContentTransform failed"; return S_OK; } - if (!resetting_) { + if (!during_synthesized_reset_) { // DirectManipulation provides updates with very high precision. If the user // holds their fingers steady on a trackpad, DirectManipulation sends // jittery updates. This calculation will reduce the precision of the scale - // value of the event to avoid jitter + // value of the event to avoid jitter. const int mantissa_bits_chop = 2; const float factor = (1 << mantissa_bits_chop) + 1; float c = factor * transform[0]; @@ -134,30 +132,30 @@ DirectManipulationOwner::DirectManipulationOwner(WindowWin32* window) : window_(window) {} int DirectManipulationOwner::Init(unsigned int width, unsigned int height) { - VERIFY_HR(CoCreateInstance(CLSID_DirectManipulationManager, nullptr, - CLSCTX_INPROC_SERVER, - IID_IDirectManipulationManager, &manager_)); - VERIFY_HR(manager_->GetUpdateManager(IID_IDirectManipulationUpdateManager, - &updateManager_)); - VERIFY_HR(manager_->CreateViewport(nullptr, window_->GetWindowHandle(), - IID_IDirectManipulationViewport, - &viewport_)); + RETURN_IF_FAILED(CoCreateInstance(CLSID_DirectManipulationManager, nullptr, + CLSCTX_INPROC_SERVER, + IID_IDirectManipulationManager, &manager_)); + RETURN_IF_FAILED(manager_->GetUpdateManager( + IID_IDirectManipulationUpdateManager, &updateManager_)); + RETURN_IF_FAILED(manager_->CreateViewport(nullptr, window_->GetWindowHandle(), + IID_IDirectManipulationViewport, + &viewport_)); DIRECTMANIPULATION_CONFIGURATION configuration = DIRECTMANIPULATION_CONFIGURATION_INTERACTION | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X | DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y | DIRECTMANIPULATION_CONFIGURATION_SCALING; - VERIFY_HR(viewport_->ActivateConfiguration(configuration)); - VERIFY_HR(viewport_->SetViewportOptions( + RETURN_IF_FAILED(viewport_->ActivateConfiguration(configuration)); + RETURN_IF_FAILED(viewport_->SetViewportOptions( DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE)); - handler_ = fml::MakeRefCounted(window_, this); - VERIFY_HR(viewport_->AddEventHandler( + handler_ = fml::MakeRefCounted(this); + RETURN_IF_FAILED(viewport_->AddEventHandler( window_->GetWindowHandle(), handler_.get(), &viewportHandlerCookie_)); RECT rect = {0, 0, (LONG)width, (LONG)height}; - VERIFY_HR(viewport_->SetViewportRect(&rect)); - VERIFY_HR(manager_->Activate(window_->GetWindowHandle())); - VERIFY_HR(viewport_->Enable()); - VERIFY_HR(updateManager_->Update(nullptr)); + RETURN_IF_FAILED(viewport_->SetViewportRect(&rect)); + RETURN_IF_FAILED(manager_->Activate(window_->GetWindowHandle())); + RETURN_IF_FAILED(viewport_->Enable()); + RETURN_IF_FAILED(updateManager_->Update(nullptr)); return 0; } @@ -165,25 +163,24 @@ void DirectManipulationOwner::ResizeViewport(unsigned int width, unsigned int height) { if (viewport_) { RECT rect = {0, 0, (LONG)width, (LONG)height}; - WARN_HR(viewport_->SetViewportRect(&rect)); + WARN_IF_FAILED(viewport_->SetViewportRect(&rect)); } } void DirectManipulationOwner::Destroy() { if (handler_) { - handler_->window_ = nullptr; handler_->owner_ = nullptr; } if (viewport_) { - WARN_HR(viewport_->Disable()); - WARN_HR(viewport_->Disable()); - WARN_HR(viewport_->RemoveEventHandler(viewportHandlerCookie_)); - WARN_HR(viewport_->Abandon()); + WARN_IF_FAILED(viewport_->Disable()); + WARN_IF_FAILED(viewport_->Disable()); + WARN_IF_FAILED(viewport_->RemoveEventHandler(viewportHandlerCookie_)); + WARN_IF_FAILED(viewport_->Abandon()); } if (window_ && manager_) { - WARN_HR(manager_->Deactivate(window_->GetWindowHandle())); + WARN_IF_FAILED(manager_->Deactivate(window_->GetWindowHandle())); } handler_ = nullptr; diff --git a/shell/platform/windows/direct_manipulation.h b/shell/platform/windows/direct_manipulation.h index 36e0b2edd0b6a..bca747173e4f8 100644 --- a/shell/platform/windows/direct_manipulation.h +++ b/shell/platform/windows/direct_manipulation.h @@ -17,27 +17,50 @@ class WindowBindingHandlerDelegate; class DirectManipulationEventHandler; +// Owner for a DirectManipulation event handler, contains the link between +// DirectManipulation and WindowBindingHandlerDelegate. class DirectManipulationOwner { public: explicit DirectManipulationOwner(WindowWin32* window); + // Initialize a DirectManipulation viewport with specified width and height. + // These should match the width and height of the application window. int Init(unsigned int width, unsigned int height); + // Resize the DirectManipulation viewport. Should be called when the + // application window is resized. void ResizeViewport(unsigned int width, unsigned int height); + // Set the WindowBindingHandlerDelegate which will receive callbacks based on + // DirectManipulation updates. void SetBindingHandlerDelegate( WindowBindingHandlerDelegate* binding_handler_delegate); + // Called when DM_POINTERHITTEST occurs with an acceptable pointer type. Will + // start DirectManipulation for that interaction. void SetContact(UINT contactId); + // Called to get updates from DirectManipulation. Should be called frequently + // to provide smooth updates. void Update(); + // Release child event handler and OS resources. void Destroy(); + // The target that should be updated when DirectManipulation provides a new + // pan/zoom transformation. WindowBindingHandlerDelegate* binding_handler_delegate; private: + // The window gesture input is occuring on. WindowWin32* window_; + // Cookie needed to register child event handler with viewport. DWORD viewportHandlerCookie_; + // Object needed for operation of the DirectManipulation API. Microsoft::WRL::ComPtr manager_; + // Object needed for operation of the DirectManipulation API. Microsoft::WRL::ComPtr updateManager_; + // Object needed for operation of the DirectManipulation API. Microsoft::WRL::ComPtr viewport_; + // Child needed for operation of the DirectManipulation API. fml::RefPtr handler_; }; +// Implements DirectManipulation event handling interfaces, receives calls from +// system when gesture events occur. class DirectManipulationEventHandler : public fml::RefCountedThreadSafe, public IDirectManipulationViewportEventHandler, @@ -47,38 +70,45 @@ class DirectManipulationEventHandler FML_FRIEND_MAKE_REF_COUNTED(DirectManipulationEventHandler); public: - explicit DirectManipulationEventHandler(WindowWin32* window, - DirectManipulationOwner* owner) - : window_(window), owner_(owner) {} + explicit DirectManipulationEventHandler(DirectManipulationOwner* owner) + : owner_(owner) {} + // |IUnknown| STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override; + // |IUnknown| ULONG STDMETHODCALLTYPE AddRef() override; + + // |IUnknown| ULONG STDMETHODCALLTYPE Release() override; + // |IDirectManipulationViewportEventHandler| HRESULT STDMETHODCALLTYPE OnViewportStatusChanged(IDirectManipulationViewport* viewport, DIRECTMANIPULATION_STATUS current, DIRECTMANIPULATION_STATUS previous) override; + // |IDirectManipulationViewportEventHandler| HRESULT STDMETHODCALLTYPE OnViewportUpdated(IDirectManipulationViewport* viewport) override; + // |IDirectManipulationViewportEventHandler| HRESULT STDMETHODCALLTYPE OnContentUpdated(IDirectManipulationViewport* viewport, IDirectManipulationContent* content) override; + // |IDirectManipulationInteractionEventHandler| HRESULT STDMETHODCALLTYPE OnInteraction(IDirectManipulationViewport2* viewport, DIRECTMANIPULATION_INTERACTION_TYPE interaction) override; private: - WindowWin32* window_; + // Parent object, used to store the target for gesture event updates. DirectManipulationOwner* owner_; // We need to reset some parts of DirectManipulation after each gesture // A flag is needed to ensure that false events created as the reset occurs // are not sent to the flutter framework. - bool resetting_ = false; + bool during_synthesized_reset_ = false; }; } // namespace flutter diff --git a/shell/platform/windows/direct_manipulation_unittests.cc b/shell/platform/windows/direct_manipulation_unittests.cc index dd32ee97c9a22..57e84013cee1d 100644 --- a/shell/platform/windows/direct_manipulation_unittests.cc +++ b/shell/platform/windows/direct_manipulation_unittests.cc @@ -145,8 +145,7 @@ TEST(DirectManipulationTest, TestGesture) { MockIDirectManipulationContent content; MockWindowBindingHandlerDelegate delegate; MockIDirectManipulationViewport viewport; - const float scale = 1.1; - const float scale_rounded = 1.0999999046325684; + const float scale = 1.5; const float pan_x = 32.0; const float pan_y = 16.0; const int DISPLAY_WIDTH = 800; @@ -154,7 +153,7 @@ TEST(DirectManipulationTest, TestGesture) { auto owner = std::make_unique(nullptr); owner->SetBindingHandlerDelegate(&delegate); auto handler = - fml::MakeRefCounted(nullptr, owner.get()); + fml::MakeRefCounted(owner.get()); int32_t device_id = (int32_t) reinterpret_cast(handler.get()); EXPECT_CALL(delegate, OnPointerPanZoomStart(device_id)); handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport, @@ -168,8 +167,8 @@ TEST(DirectManipulationTest, TestGesture) { transform[5] = pan_y; return S_OK; })); - EXPECT_CALL(delegate, OnPointerPanZoomUpdate(device_id, pan_x, pan_y, - scale_rounded, 0)); + EXPECT_CALL(delegate, + OnPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, 0)); handler->OnContentUpdated((IDirectManipulationViewport*)&viewport, (IDirectManipulationContent*)&content); EXPECT_CALL(delegate, OnPointerPanZoomEnd(device_id)); diff --git a/shell/platform/windows/flutter_window_win32.h b/shell/platform/windows/flutter_window_win32.h index 26a7325f55456..b50b8090a98a9 100644 --- a/shell/platform/windows/flutter_window_win32.h +++ b/shell/platform/windows/flutter_window_win32.h @@ -134,6 +134,7 @@ class FlutterWindowWin32 : public WindowWin32, public WindowBindingHandler { size_t row_bytes, size_t height) override; + // |FlutterWindowBindingHandler| PointerLocation GetPrimaryPointerLocation() override; private: diff --git a/shell/platform/windows/flutter_windows_view.h b/shell/platform/windows/flutter_windows_view.h index 02cb1b2496486..2601dcc1e18f7 100644 --- a/shell/platform/windows/flutter_windows_view.h +++ b/shell/platform/windows/flutter_windows_view.h @@ -219,10 +219,10 @@ class FlutterWindowsView : public WindowBindingHandlerDelegate, // The currently pressed buttons, as represented in FlutterPointerEvent. uint64_t buttons = 0; - // The x position where the last pan/zoom started + // The x position where the last pan/zoom started. double pan_zoom_start_x = 0; - // The y position where the last pan/zoom started + // The y position where the last pan/zoom started. double pan_zoom_start_y = 0; }; diff --git a/shell/platform/windows/window_binding_handler.h b/shell/platform/windows/window_binding_handler.h index 7a4cc27424eef..594ef355a988d 100644 --- a/shell/platform/windows/window_binding_handler.h +++ b/shell/platform/windows/window_binding_handler.h @@ -24,7 +24,8 @@ struct PhysicalWindowBounds { size_t height; }; -// Structure containing the position of a mouse pointer +// Structure containing the position of a mouse pointer in the coordinate system +// specified by the function where it's used. struct PointerLocation { size_t x; size_t y; @@ -85,7 +86,8 @@ class WindowBindingHandler { // client is cleared. virtual void OnResetImeComposing() = 0; - // Returns the last known position of the primary pointer + // Returns the last known position of the primary pointer in window + // coordinates. virtual PointerLocation GetPrimaryPointerLocation() = 0; }; diff --git a/shell/platform/windows/window_binding_handler_delegate.h b/shell/platform/windows/window_binding_handler_delegate.h index 2411737fdef17..372e787370f80 100644 --- a/shell/platform/windows/window_binding_handler_delegate.h +++ b/shell/platform/windows/window_binding_handler_delegate.h @@ -50,12 +50,20 @@ class WindowBindingHandlerDelegate { virtual void OnPointerLeave(FlutterPointerDeviceKind device_kind, int32_t device_id) = 0; + // Notifies delegate that a pan/zoom gesture has started. + // Typically called by DirectManipulationEventHandler virtual void OnPointerPanZoomStart(int32_t device_id) = 0; + + // Notifies delegate that a pan/zoom gesture has updated. + // Typically called by DirectManipulationEventHandler virtual void OnPointerPanZoomUpdate(int32_t device_id, double pan_x, double pan_y, double scale, double rotation) = 0; + + // Notifies delegate that a pan/zoom gesture has ended. + // Typically called by DirectManipulationEventHandler virtual void OnPointerPanZoomEnd(int32_t device_id) = 0; // Notifies delegate that backing window has received text. diff --git a/shell/platform/windows/window_win32.cc b/shell/platform/windows/window_win32.cc index 446c77778393d..cf3054928eb04 100644 --- a/shell/platform/windows/window_win32.cc +++ b/shell/platform/windows/window_win32.cc @@ -99,11 +99,16 @@ void WindowWin32::InitializeChild(const char* title, ZeroMemory(&dmi, sizeof(dmi)); dmi.dmSize = sizeof(dmi); if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmi)) { - framerate_ = dmi.dmDisplayFrequency; - SetTimer(result, kVsyncTimer, 1000 / framerate_, nullptr); + directManipulationPollingRate_ = dmi.dmDisplayFrequency; } else { - OutputDebugString(L"Failed to get framerate"); + OutputDebugString( + L"Failed to get framerate, will use default of 60 Hz for gesture " + L"polling."); } + SetUserObjectInformationA(GetCurrentProcess(), + UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1); + SetTimer(result, kDirectManipulationTimer, + 1000 / directManipulationPollingRate_, nullptr); direct_manipulation_owner_ = std::make_unique(this); direct_manipulation_owner_->Init(width, height); } @@ -454,9 +459,10 @@ WindowWin32::HandleMessage(UINT const message, break; } case WM_TIMER: - if (wparam == kVsyncTimer) { + if (wparam == kDirectManipulationTimer) { direct_manipulation_owner_->Update(); - SetTimer(window_handle_, kVsyncTimer, 1000 / framerate_, nullptr); + SetTimer(window_handle_, kDirectManipulationTimer, + 1000 / directManipulationPollingRate_, nullptr); return 0; } break; diff --git a/shell/platform/windows/window_win32.h b/shell/platform/windows/window_win32.h index b16ab7de14065..7ee626f84d234 100644 --- a/shell/platform/windows/window_win32.h +++ b/shell/platform/windows/window_win32.h @@ -210,6 +210,8 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { // Returns the root view accessibility node, or nullptr if none. virtual gfx::NativeViewAccessible GetNativeViewAccessible() = 0; + // Handles running DirectManipulation on the window to receive trackpad + // gestures. std::unique_ptr direct_manipulation_owner_; private: @@ -258,8 +260,11 @@ class WindowWin32 : public KeyboardManagerWin32::WindowDelegate { // Generates touch point IDs for touch events. SequentialIdGenerator touch_id_generator_; - const static int kVsyncTimer = 1; - int framerate_ = 1; + // Timer identifier for DirectManipulation gesture polling. + const static int kDirectManipulationTimer = 1; + + // Frequency (Hz) to poll for DirectManipulation updates. + int directManipulationPollingRate_ = 60; }; } // namespace flutter From fbfe4d9c1a495a84ee26be9175ed2d75a2c89c6d Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Tue, 10 May 2022 23:26:16 -0400 Subject: [PATCH 3/4] Add scale rounding test --- .../windows/direct_manipulation_unittests.cc | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) diff --git a/shell/platform/windows/direct_manipulation_unittests.cc b/shell/platform/windows/direct_manipulation_unittests.cc index 57e84013cee1d..5975103e849b8 100644 --- a/shell/platform/windows/direct_manipulation_unittests.cc +++ b/shell/platform/windows/direct_manipulation_unittests.cc @@ -190,5 +190,74 @@ TEST(DirectManipulationTest, TestGesture) { DIRECTMANIPULATION_INERTIA); } +// Verify that scale mantissa rounding works as expected +TEST(DirectManipulationTest, TestRounding) { + MockIDirectManipulationContent content; + MockWindowBindingHandlerDelegate delegate; + MockIDirectManipulationViewport viewport; + const float scale = 1.5; + const int DISPLAY_WIDTH = 800; + const int DISPLAY_HEIGHT = 600; + auto owner = std::make_unique(nullptr); + owner->SetBindingHandlerDelegate(&delegate); + auto handler = + fml::MakeRefCounted(owner.get()); + int32_t device_id = (int32_t) reinterpret_cast(handler.get()); + EXPECT_CALL(delegate, OnPointerPanZoomStart(device_id)); + handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport, + DIRECTMANIPULATION_RUNNING, + DIRECTMANIPULATION_READY); + EXPECT_CALL(content, GetContentTransform(_, 6)) + .WillOnce(::testing::Invoke([scale](float* transform, DWORD size) { + transform[0] = 1.5000001f; + transform[4] = 4.0; + transform[5] = 0.0; + return S_OK; + })) + .RetiresOnSaturation(); + EXPECT_CALL(delegate, + OnPointerPanZoomUpdate(device_id, 4.0, 0, 1.5000001f, 0)) + .Times(0); + EXPECT_CALL(delegate, OnPointerPanZoomUpdate(device_id, 4.0, 0, 1.5f, 0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(content, GetContentTransform(_, 6)) + .WillOnce(::testing::Invoke([scale](float* transform, DWORD size) { + transform[0] = 1.50000065f; + transform[4] = 2.0; + transform[5] = 0.0; + return S_OK; + })) + .RetiresOnSaturation(); + EXPECT_CALL(delegate, + OnPointerPanZoomUpdate(device_id, 2.0, 0, 1.50000065f, 0)) + .Times(0); + EXPECT_CALL(delegate, + OnPointerPanZoomUpdate(device_id, 2.0, 0, 1.50000047f, 0)) + .Times(1) + .RetiresOnSaturation(); + EXPECT_CALL(delegate, OnPointerPanZoomEnd(device_id)); + EXPECT_CALL(viewport, GetViewportRect(_)) + .WillOnce(::testing::Invoke([DISPLAY_WIDTH, DISPLAY_HEIGHT](RECT* rect) { + rect->left = 0; + rect->top = 0; + rect->right = DISPLAY_WIDTH; + rect->bottom = DISPLAY_HEIGHT; + return S_OK; + })); + EXPECT_CALL(viewport, ZoomToRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, false)) + .WillOnce(::testing::Return(S_OK)); + handler->OnContentUpdated((IDirectManipulationViewport*)&viewport, + (IDirectManipulationContent*)&content); + handler->OnContentUpdated((IDirectManipulationViewport*)&viewport, + (IDirectManipulationContent*)&content); + handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport, + DIRECTMANIPULATION_INERTIA, + DIRECTMANIPULATION_RUNNING); + handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport, + DIRECTMANIPULATION_READY, + DIRECTMANIPULATION_INERTIA); +} + } // namespace testing } // namespace flutter From bb530c3bee3edf4f1910a4036a9bc4aea2b2475c Mon Sep 17 00:00:00 2001 From: Callum Moffat Date: Thu, 19 May 2022 22:48:16 -0400 Subject: [PATCH 4/4] Fix merge --- .../windows/flutter_window_win32_unittests.cc | 47 ------------------- .../mock_window_binding_handler_delegate.h | 3 +- 2 files changed, 2 insertions(+), 48 deletions(-) diff --git a/shell/platform/windows/flutter_window_win32_unittests.cc b/shell/platform/windows/flutter_window_win32_unittests.cc index ada618b26bfb3..063f51a77e96d 100644 --- a/shell/platform/windows/flutter_window_win32_unittests.cc +++ b/shell/platform/windows/flutter_window_win32_unittests.cc @@ -140,53 +140,6 @@ class MockFlutterWindowWin32 : public FlutterWindowWin32 { } }; -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_METHOD4(OnPointerMove, - void(double, double, FlutterPointerDeviceKind, int32_t)); - MOCK_METHOD5(OnPointerDown, - void(double, - double, - FlutterPointerDeviceKind, - int32_t, - FlutterPointerMouseButtons)); - MOCK_METHOD5(OnPointerUp, - void(double, - double, - FlutterPointerDeviceKind, - int32_t, - FlutterPointerMouseButtons)); - MOCK_METHOD4(OnPointerLeave, - void(double, double, FlutterPointerDeviceKind, int32_t)); - MOCK_METHOD1(OnText, void(const std::u16string&)); - MOCK_METHOD7(OnKey, - void(int, int, int, char32_t, bool, bool, KeyEventCallback)); - MOCK_METHOD0(OnComposeBegin, void()); - MOCK_METHOD0(OnComposeCommit, void()); - MOCK_METHOD0(OnComposeEnd, void()); - MOCK_METHOD2(OnComposeChange, void(const std::u16string&, int)); - MOCK_METHOD1(OnUpdateSemanticsEnabled, void(bool)); - MOCK_METHOD0(GetNativeViewAccessible, gfx::NativeViewAccessible()); - MOCK_METHOD7(OnScroll, - void(double, - double, - double, - double, - int, - FlutterPointerDeviceKind, - int32_t)); - MOCK_METHOD0(OnPlatformBrightnessChanged, void()); -}; - // A FlutterWindowsView that overrides the RegisterKeyboardHandlers function // to register the keyboard hook handlers that can be spied upon. class TestFlutterWindowsView : public FlutterWindowsView { diff --git a/shell/platform/windows/testing/mock_window_binding_handler_delegate.h b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h index 67bbac201c0ce..e4f962bad0375 100644 --- a/shell/platform/windows/testing/mock_window_binding_handler_delegate.h +++ b/shell/platform/windows/testing/mock_window_binding_handler_delegate.h @@ -36,7 +36,8 @@ class MockWindowBindingHandlerDelegate : public WindowBindingHandlerDelegate { FlutterPointerDeviceKind, int32_t, FlutterPointerMouseButtons)); - MOCK_METHOD2(OnPointerLeave, void(FlutterPointerDeviceKind, int32_t)); + MOCK_METHOD4(OnPointerLeave, + void(double, double, FlutterPointerDeviceKind, int32_t)); MOCK_METHOD1(OnPointerPanZoomStart, void(int32_t)); MOCK_METHOD5(OnPointerPanZoomUpdate, void(int32_t, double, double, double, double));