Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit 216376c

Browse files
committed
Win32 trackpad gestures
1 parent 1932fb9 commit 216376c

18 files changed

Lines changed: 750 additions & 54 deletions

ci/licenses_golden/licenses_flutter

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1997,6 +1997,9 @@ FILE: ../../../flutter/shell/platform/windows/client_wrapper/include/flutter/plu
19971997
FILE: ../../../flutter/shell/platform/windows/client_wrapper/plugin_registrar_windows_unittests.cc
19981998
FILE: ../../../flutter/shell/platform/windows/cursor_handler.cc
19991999
FILE: ../../../flutter/shell/platform/windows/cursor_handler.h
2000+
FILE: ../../../flutter/shell/platform/windows/direct_manipulation.cc
2001+
FILE: ../../../flutter/shell/platform/windows/direct_manipulation.h
2002+
FILE: ../../../flutter/shell/platform/windows/direct_manipulation_unittests.cc
20002003
FILE: ../../../flutter/shell/platform/windows/display_helper_winuwp.cc
20012004
FILE: ../../../flutter/shell/platform/windows/display_helper_winuwp.h
20022005
FILE: ../../../flutter/shell/platform/windows/dpi_utils_win32.cc

shell/platform/windows/BUILD.gn

Lines changed: 10 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,8 @@ source_set("flutter_windows_source") {
115115
sources += [
116116
"accessibility_bridge_delegate_win32.cc",
117117
"accessibility_bridge_delegate_win32.h",
118+
"direct_manipulation.cc",
119+
"direct_manipulation.h",
118120
"dpi_utils_win32.cc",
119121
"dpi_utils_win32.h",
120122
"flutter_platform_node_delegate_win32.cc",
@@ -155,14 +157,10 @@ source_set("flutter_windows_source") {
155157

156158
public_configs = [ ":relative_angle_headers" ]
157159

158-
if (target_os == "winuwp") {
159-
defines = [
160-
"_SILENCE_CLANG_COROUTINE_MESSAGE",
161-
"FLUTTER_ENGINE_NO_PROTOTYPES",
162-
]
163-
} else {
164-
defines = [ "FLUTTER_ENGINE_NO_PROTOTYPES" ]
165-
}
160+
defines = [
161+
"_SILENCE_CLANG_COROUTINE_MESSAGE",
162+
"FLUTTER_ENGINE_NO_PROTOTYPES",
163+
]
166164

167165
public_deps = [
168166
"//flutter/fml:string_conversion",
@@ -171,6 +169,7 @@ source_set("flutter_windows_source") {
171169

172170
deps = [
173171
":flutter_windows_headers",
172+
"//flutter/fml:fml",
174173
"//flutter/shell/platform/common:common_cpp",
175174
"//flutter/shell/platform/common:common_cpp_input",
176175
"//flutter/shell/platform/common:common_cpp_switches",
@@ -241,6 +240,7 @@ executable("flutter_windows_unittests") {
241240
sources += [
242241
# TODO move first two tests to common once above TODO's unblocked.
243242
"accessibility_bridge_delegate_win32_unittests.cc",
243+
"direct_manipulation_unittests.cc",
244244
"dpi_utils_win32_unittests.cc",
245245
"flutter_project_bundle_unittests.cc",
246246
"flutter_window_win32_unittests.cc",
@@ -273,6 +273,8 @@ executable("flutter_windows_unittests") {
273273

274274
public_configs = [ "//flutter:config" ]
275275

276+
defines = [ "_SILENCE_CLANG_COROUTINE_MESSAGE" ]
277+
276278
deps = [
277279
":flutter_windows_fixtures",
278280
":flutter_windows_headers",
Lines changed: 225 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,225 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/fml/logging.h"
6+
7+
#include "flutter/shell/platform/windows/direct_manipulation.h"
8+
#include "flutter/shell/platform/windows/window_binding_handler_delegate.h"
9+
#include "flutter/shell/platform/windows/window_win32.h"
10+
11+
#define VERIFY_HR(operation) \
12+
if (FAILED(operation)) { \
13+
FML_LOG(ERROR) << #operation << " failed"; \
14+
manager_ = nullptr; \
15+
updateManager_ = nullptr; \
16+
viewport_ = nullptr; \
17+
return -1; \
18+
}
19+
20+
#define WARN_HR(operation) \
21+
if (FAILED(operation)) { \
22+
FML_LOG(ERROR) << #operation << " failed"; \
23+
}
24+
25+
namespace flutter {
26+
27+
STDMETHODIMP DirectManipulationEventHandler::QueryInterface(REFIID iid,
28+
void** ppv) {
29+
if ((iid == IID_IUnknown) ||
30+
(iid == IID_IDirectManipulationViewportEventHandler)) {
31+
*ppv = static_cast<IDirectManipulationViewportEventHandler*>(this);
32+
AddRef();
33+
return S_OK;
34+
} else if (iid == IID_IDirectManipulationInteractionEventHandler) {
35+
*ppv = static_cast<IDirectManipulationInteractionEventHandler*>(this);
36+
AddRef();
37+
return S_OK;
38+
}
39+
return E_NOINTERFACE;
40+
}
41+
42+
HRESULT DirectManipulationEventHandler::OnViewportStatusChanged(
43+
IDirectManipulationViewport* viewport,
44+
DIRECTMANIPULATION_STATUS current,
45+
DIRECTMANIPULATION_STATUS previous) {
46+
if (current == DIRECTMANIPULATION_RUNNING) {
47+
if (!resetting_) {
48+
// Not a false event
49+
if (owner_->binding_handler_delegate) {
50+
owner_->binding_handler_delegate->OnPointerPanZoomStart(
51+
(int32_t) reinterpret_cast<int64_t>(this));
52+
}
53+
}
54+
} else if (previous == DIRECTMANIPULATION_RUNNING) {
55+
if (resetting_) {
56+
// The resetting transition has concluded
57+
resetting_ = false;
58+
} else {
59+
if (owner_->binding_handler_delegate) {
60+
owner_->binding_handler_delegate->OnPointerPanZoomEnd(
61+
(int32_t) reinterpret_cast<int64_t>(this));
62+
}
63+
// Need to reset the content transform to its original position
64+
// so that we are ready for the next gesture
65+
// Use resetting_ flag to prevent sending reset also to the framework
66+
resetting_ = true;
67+
RECT rect;
68+
HRESULT hr = viewport->GetViewportRect(&rect);
69+
if (FAILED(hr)) {
70+
FML_LOG(ERROR) << "Failed to get the current viewport rect";
71+
return E_FAIL;
72+
}
73+
hr = viewport->ZoomToRect(rect.left, rect.top, rect.right, rect.bottom,
74+
false);
75+
if (FAILED(hr)) {
76+
FML_LOG(ERROR) << "Failed to reset the gesture using ZoomToRect";
77+
return E_FAIL;
78+
}
79+
}
80+
}
81+
return S_OK;
82+
}
83+
84+
HRESULT DirectManipulationEventHandler::OnViewportUpdated(
85+
IDirectManipulationViewport* viewport) {
86+
return S_OK;
87+
}
88+
89+
HRESULT DirectManipulationEventHandler::OnContentUpdated(
90+
IDirectManipulationViewport* viewport,
91+
IDirectManipulationContent* content) {
92+
float transform[6];
93+
HRESULT hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
94+
if (FAILED(hr)) {
95+
FML_LOG(ERROR) << "GetContentTransform failed";
96+
return S_OK;
97+
}
98+
if (!resetting_) {
99+
// DirectManipulation provides updates with very high precision. If the user
100+
// holds their fingers steady on a trackpad, DirectManipulation sends
101+
// jittery updates. This calculation will reduce the precision of the scale
102+
// value of the event to avoid jitter
103+
const int mantissa_bits_chop = 2;
104+
const float factor = (1 << mantissa_bits_chop) + 1;
105+
float c = factor * transform[0];
106+
float scale = c - (c - transform[0]);
107+
float pan_x = transform[4];
108+
float pan_y = transform[5];
109+
if (owner_->binding_handler_delegate) {
110+
owner_->binding_handler_delegate->OnPointerPanZoomUpdate(
111+
(int32_t) reinterpret_cast<int64_t>(this), pan_x, pan_y, scale, 0);
112+
}
113+
}
114+
return S_OK;
115+
}
116+
117+
HRESULT DirectManipulationEventHandler::OnInteraction(
118+
IDirectManipulationViewport2* viewport,
119+
DIRECTMANIPULATION_INTERACTION_TYPE interaction) {
120+
return S_OK;
121+
}
122+
123+
ULONG STDMETHODCALLTYPE DirectManipulationEventHandler::AddRef() {
124+
RefCountedThreadSafe::AddRef();
125+
return 0;
126+
}
127+
128+
ULONG STDMETHODCALLTYPE DirectManipulationEventHandler::Release() {
129+
RefCountedThreadSafe::Release();
130+
return 0;
131+
}
132+
133+
DirectManipulationOwner::DirectManipulationOwner(WindowWin32* window)
134+
: window_(window) {}
135+
136+
int DirectManipulationOwner::Init(unsigned int width, unsigned int height) {
137+
VERIFY_HR(CoCreateInstance(CLSID_DirectManipulationManager, nullptr,
138+
CLSCTX_INPROC_SERVER,
139+
IID_IDirectManipulationManager, &manager_));
140+
VERIFY_HR(manager_->GetUpdateManager(IID_IDirectManipulationUpdateManager,
141+
&updateManager_));
142+
VERIFY_HR(manager_->CreateViewport(nullptr, window_->GetWindowHandle(),
143+
IID_IDirectManipulationViewport,
144+
&viewport_));
145+
DIRECTMANIPULATION_CONFIGURATION configuration =
146+
DIRECTMANIPULATION_CONFIGURATION_INTERACTION |
147+
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_X |
148+
DIRECTMANIPULATION_CONFIGURATION_TRANSLATION_Y |
149+
DIRECTMANIPULATION_CONFIGURATION_SCALING;
150+
VERIFY_HR(viewport_->ActivateConfiguration(configuration));
151+
VERIFY_HR(viewport_->SetViewportOptions(
152+
DIRECTMANIPULATION_VIEWPORT_OPTIONS_MANUALUPDATE));
153+
handler_ = fml::MakeRefCounted<DirectManipulationEventHandler>(window_, this);
154+
VERIFY_HR(viewport_->AddEventHandler(
155+
window_->GetWindowHandle(), handler_.get(), &viewportHandlerCookie_));
156+
RECT rect = {0, 0, (LONG)width, (LONG)height};
157+
VERIFY_HR(viewport_->SetViewportRect(&rect));
158+
VERIFY_HR(manager_->Activate(window_->GetWindowHandle()));
159+
VERIFY_HR(viewport_->Enable());
160+
VERIFY_HR(updateManager_->Update(nullptr));
161+
return 0;
162+
}
163+
164+
void DirectManipulationOwner::ResizeViewport(unsigned int width,
165+
unsigned int height) {
166+
if (viewport_) {
167+
RECT rect = {0, 0, (LONG)width, (LONG)height};
168+
WARN_HR(viewport_->SetViewportRect(&rect));
169+
}
170+
}
171+
172+
void DirectManipulationOwner::Destroy() {
173+
if (handler_) {
174+
handler_->window_ = nullptr;
175+
handler_->owner_ = nullptr;
176+
}
177+
178+
if (viewport_) {
179+
WARN_HR(viewport_->Disable());
180+
WARN_HR(viewport_->Disable());
181+
WARN_HR(viewport_->RemoveEventHandler(viewportHandlerCookie_));
182+
WARN_HR(viewport_->Abandon());
183+
}
184+
185+
if (window_ && manager_) {
186+
WARN_HR(manager_->Deactivate(window_->GetWindowHandle()));
187+
}
188+
189+
handler_ = nullptr;
190+
viewport_ = nullptr;
191+
updateManager_ = nullptr;
192+
manager_ = nullptr;
193+
window_ = nullptr;
194+
}
195+
196+
void DirectManipulationOwner::SetContact(UINT contactId) {
197+
if (viewport_) {
198+
viewport_->SetContact(contactId);
199+
}
200+
}
201+
202+
void DirectManipulationOwner::SetBindingHandlerDelegate(
203+
WindowBindingHandlerDelegate* delegate) {
204+
binding_handler_delegate = delegate;
205+
}
206+
207+
void DirectManipulationOwner::Update() {
208+
if (updateManager_) {
209+
HRESULT hr = updateManager_->Update(nullptr);
210+
if (FAILED(hr)) {
211+
FML_LOG(ERROR) << "updateManager_->Update failed";
212+
auto error = GetLastError();
213+
FML_LOG(ERROR) << error;
214+
LPWSTR message = nullptr;
215+
size_t size = FormatMessageW(
216+
FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM |
217+
FORMAT_MESSAGE_IGNORE_INSERTS,
218+
NULL, error, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
219+
reinterpret_cast<LPWSTR>(&message), 0, NULL);
220+
FML_LOG(ERROR) << message;
221+
}
222+
}
223+
}
224+
225+
} // namespace flutter
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_SHELL_PLATFORM_WINDOWS_DIRECT_MANIPULATION_H_
6+
#define FLUTTER_SHELL_PLATFORM_WINDOWS_DIRECT_MANIPULATION_H_
7+
8+
#include "flutter/fml/memory/ref_counted.h"
9+
10+
#include <wrl/client.h>
11+
#include "directmanipulation.h"
12+
13+
namespace flutter {
14+
15+
class WindowWin32;
16+
class WindowBindingHandlerDelegate;
17+
18+
class DirectManipulationEventHandler;
19+
20+
class DirectManipulationOwner {
21+
public:
22+
explicit DirectManipulationOwner(WindowWin32* window);
23+
int Init(unsigned int width, unsigned int height);
24+
void ResizeViewport(unsigned int width, unsigned int height);
25+
void SetBindingHandlerDelegate(
26+
WindowBindingHandlerDelegate* binding_handler_delegate);
27+
void SetContact(UINT contactId);
28+
void Update();
29+
void Destroy();
30+
WindowBindingHandlerDelegate* binding_handler_delegate;
31+
32+
private:
33+
WindowWin32* window_;
34+
DWORD viewportHandlerCookie_;
35+
Microsoft::WRL::ComPtr<IDirectManipulationManager> manager_;
36+
Microsoft::WRL::ComPtr<IDirectManipulationUpdateManager> updateManager_;
37+
Microsoft::WRL::ComPtr<IDirectManipulationViewport> viewport_;
38+
fml::RefPtr<DirectManipulationEventHandler> handler_;
39+
};
40+
41+
class DirectManipulationEventHandler
42+
: public fml::RefCountedThreadSafe<DirectManipulationEventHandler>,
43+
public IDirectManipulationViewportEventHandler,
44+
public IDirectManipulationInteractionEventHandler {
45+
friend class DirectManipulationOwner;
46+
FML_FRIEND_REF_COUNTED_THREAD_SAFE(DirectManipulationEventHandler);
47+
FML_FRIEND_MAKE_REF_COUNTED(DirectManipulationEventHandler);
48+
49+
public:
50+
explicit DirectManipulationEventHandler(WindowWin32* window,
51+
DirectManipulationOwner* owner)
52+
: window_(window), owner_(owner) {}
53+
54+
STDMETHODIMP QueryInterface(REFIID iid, void** ppv) override;
55+
56+
ULONG STDMETHODCALLTYPE AddRef() override;
57+
ULONG STDMETHODCALLTYPE Release() override;
58+
59+
HRESULT STDMETHODCALLTYPE
60+
OnViewportStatusChanged(IDirectManipulationViewport* viewport,
61+
DIRECTMANIPULATION_STATUS current,
62+
DIRECTMANIPULATION_STATUS previous) override;
63+
64+
HRESULT STDMETHODCALLTYPE
65+
OnViewportUpdated(IDirectManipulationViewport* viewport) override;
66+
67+
HRESULT STDMETHODCALLTYPE
68+
OnContentUpdated(IDirectManipulationViewport* viewport,
69+
IDirectManipulationContent* content) override;
70+
71+
HRESULT STDMETHODCALLTYPE
72+
OnInteraction(IDirectManipulationViewport2* viewport,
73+
DIRECTMANIPULATION_INTERACTION_TYPE interaction) override;
74+
75+
private:
76+
WindowWin32* window_;
77+
DirectManipulationOwner* owner_;
78+
// We need to reset some parts of DirectManipulation after each gesture
79+
// A flag is needed to ensure that false events created as the reset occurs
80+
// are not sent to the flutter framework.
81+
bool resetting_ = false;
82+
};
83+
84+
} // namespace flutter
85+
86+
#endif // FLUTTER_SHELL_PLATFORM_WINDOWS_DIRECT_MANIPULATION_H_

0 commit comments

Comments
 (0)