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

Commit af1a2da

Browse files
authored
Increase Win32 trackpad polling rate, fix repeated swipes issue (#37154)
* Windows trackpad improvements * Address feedback, remove dynamic polling rate * Adjust comment
1 parent e32bff4 commit af1a2da

File tree

5 files changed

+152
-36
lines changed

5 files changed

+152
-36
lines changed

shell/platform/windows/direct_manipulation.cc

Lines changed: 40 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -45,22 +45,49 @@ STDMETHODIMP DirectManipulationEventHandler::QueryInterface(REFIID iid,
4545
return E_NOINTERFACE;
4646
}
4747

48+
DirectManipulationEventHandler::GestureData
49+
DirectManipulationEventHandler::ConvertToGestureData(float transform[6]) {
50+
// DirectManipulation provides updates with very high precision. If the user
51+
// holds their fingers steady on a trackpad, DirectManipulation sends
52+
// jittery updates. This calculation will reduce the precision of the scale
53+
// value of the event to avoid jitter.
54+
const int mantissa_bits_chop = 2;
55+
const float factor = (1 << mantissa_bits_chop) + 1;
56+
float c = factor * transform[0];
57+
return GestureData{
58+
c - (c - transform[0]), // scale
59+
transform[4], // pan_x
60+
transform[5], // pan_y
61+
};
62+
}
63+
4864
HRESULT DirectManipulationEventHandler::OnViewportStatusChanged(
4965
IDirectManipulationViewport* viewport,
5066
DIRECTMANIPULATION_STATUS current,
5167
DIRECTMANIPULATION_STATUS previous) {
68+
if (during_synthesized_reset_) {
69+
during_synthesized_reset_ = current != DIRECTMANIPULATION_READY;
70+
return S_OK;
71+
}
5272
during_inertia_ = current == DIRECTMANIPULATION_INERTIA;
53-
if (during_synthesized_reset_ && previous == DIRECTMANIPULATION_RUNNING) {
54-
during_synthesized_reset_ = false;
55-
} else if (current == DIRECTMANIPULATION_RUNNING) {
56-
if (!during_synthesized_reset_) {
57-
// Not a false event.
58-
if (owner_->binding_handler_delegate) {
59-
owner_->binding_handler_delegate->OnPointerPanZoomStart(GetDeviceId());
73+
if (current == DIRECTMANIPULATION_RUNNING) {
74+
IDirectManipulationContent* content;
75+
HRESULT hr = viewport->GetPrimaryContent(IID_PPV_ARGS(&content));
76+
if (SUCCEEDED(hr)) {
77+
float transform[6];
78+
hr = content->GetContentTransform(transform, ARRAYSIZE(transform));
79+
if (SUCCEEDED(hr)) {
80+
initial_gesture_data_ = ConvertToGestureData(transform);
81+
} else {
82+
FML_LOG(ERROR) << "GetContentTransform failed";
6083
}
84+
} else {
85+
FML_LOG(ERROR) << "GetPrimaryContent failed";
6186
}
62-
}
63-
if (previous == DIRECTMANIPULATION_RUNNING) {
87+
if (owner_->binding_handler_delegate) {
88+
owner_->binding_handler_delegate->OnPointerPanZoomStart(GetDeviceId());
89+
}
90+
} else if (previous == DIRECTMANIPULATION_RUNNING) {
6491
// Reset deltas to ensure only inertia values will be compared later.
6592
last_pan_delta_x_ = 0.0;
6693
last_pan_delta_y_ = 0.0;
@@ -113,16 +140,10 @@ HRESULT DirectManipulationEventHandler::OnContentUpdated(
113140
return S_OK;
114141
}
115142
if (!during_synthesized_reset_) {
116-
// DirectManipulation provides updates with very high precision. If the user
117-
// holds their fingers steady on a trackpad, DirectManipulation sends
118-
// jittery updates. This calculation will reduce the precision of the scale
119-
// value of the event to avoid jitter.
120-
const int mantissa_bits_chop = 2;
121-
const float factor = (1 << mantissa_bits_chop) + 1;
122-
float c = factor * transform[0];
123-
float scale = c - (c - transform[0]);
124-
float pan_x = transform[4];
125-
float pan_y = transform[5];
143+
GestureData data = ConvertToGestureData(transform);
144+
float scale = data.scale / initial_gesture_data_.scale;
145+
float pan_x = data.pan_x - initial_gesture_data_.pan_x;
146+
float pan_y = data.pan_y - initial_gesture_data_.pan_y;
126147
last_pan_delta_x_ = pan_x - last_pan_x_;
127148
last_pan_delta_y_ = pan_y - last_pan_y_;
128149
last_pan_x_ = pan_x;

shell/platform/windows/direct_manipulation.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,13 @@ class DirectManipulationEventHandler
106106
DIRECTMANIPULATION_INTERACTION_TYPE interaction) override;
107107

108108
private:
109+
struct GestureData {
110+
float scale;
111+
float pan_x;
112+
float pan_y;
113+
};
114+
// Convert transform array to Flutter-usable values.
115+
GestureData ConvertToGestureData(float transform[6]);
109116
// Unique identifier to associate with all gesture event updates.
110117
int32_t GetDeviceId();
111118
// Parent object, used to store the target for gesture event updates.
@@ -117,6 +124,13 @@ class DirectManipulationEventHandler
117124
// Store whether current events are from synthetic inertia rather than user
118125
// input.
119126
bool during_inertia_ = false;
127+
// The transform might not be able to be reset before the next gesture, so
128+
// the initial state needs to be stored for reference.
129+
GestureData initial_gesture_data_ = {
130+
1, // scale
131+
0, // pan_x
132+
0, // pan_y
133+
};
120134
// Store the difference between the last pan offsets to determine if inertia
121135
// has been cancelled in the middle of an animation.
122136
float last_pan_x_ = 0.0;

shell/platform/windows/direct_manipulation_unittests.cc

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,20 @@ TEST(DirectManipulationTest, TestGesture) {
155155
auto handler =
156156
fml::MakeRefCounted<DirectManipulationEventHandler>(owner.get());
157157
int32_t device_id = (int32_t) reinterpret_cast<int64_t>(handler.get());
158+
EXPECT_CALL(viewport, GetPrimaryContent(_, _))
159+
.WillOnce(::testing::Invoke([&content](REFIID in, void** out) {
160+
*out = &content;
161+
return S_OK;
162+
}))
163+
.RetiresOnSaturation();
164+
EXPECT_CALL(content, GetContentTransform(_, 6))
165+
.WillOnce(::testing::Invoke([scale](float* transform, DWORD size) {
166+
transform[0] = 1.0f;
167+
transform[4] = 0.0;
168+
transform[5] = 0.0;
169+
return S_OK;
170+
}))
171+
.RetiresOnSaturation();
158172
EXPECT_CALL(delegate, OnPointerPanZoomStart(device_id));
159173
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
160174
DIRECTMANIPULATION_RUNNING,
@@ -203,6 +217,20 @@ TEST(DirectManipulationTest, TestRounding) {
203217
auto handler =
204218
fml::MakeRefCounted<DirectManipulationEventHandler>(owner.get());
205219
int32_t device_id = (int32_t) reinterpret_cast<int64_t>(handler.get());
220+
EXPECT_CALL(viewport, GetPrimaryContent(_, _))
221+
.WillOnce(::testing::Invoke([&content](REFIID in, void** out) {
222+
*out = &content;
223+
return S_OK;
224+
}))
225+
.RetiresOnSaturation();
226+
EXPECT_CALL(content, GetContentTransform(_, 6))
227+
.WillOnce(::testing::Invoke([scale](float* transform, DWORD size) {
228+
transform[0] = 1.0f;
229+
transform[4] = 0.0;
230+
transform[5] = 0.0;
231+
return S_OK;
232+
}))
233+
.RetiresOnSaturation();
206234
EXPECT_CALL(delegate, OnPointerPanZoomStart(device_id));
207235
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
208236
DIRECTMANIPULATION_RUNNING,
@@ -364,5 +392,70 @@ TEST(DirectManipulationTest, TestInertiaCamcelNotSentAtInertiaEnd) {
364392
DIRECTMANIPULATION_INERTIA);
365393
}
366394

395+
// Have some initial values in the matrix, only the differences should be
396+
// reported.
397+
TEST(DirectManipulationTest, TestGestureWithInitialData) {
398+
MockIDirectManipulationContent content;
399+
MockWindowBindingHandlerDelegate delegate;
400+
MockIDirectManipulationViewport viewport;
401+
const float scale = 1.5;
402+
const float pan_x = 32.0;
403+
const float pan_y = 16.0;
404+
const int DISPLAY_WIDTH = 800;
405+
const int DISPLAY_HEIGHT = 600;
406+
auto owner = std::make_unique<DirectManipulationOwner>(nullptr);
407+
owner->SetBindingHandlerDelegate(&delegate);
408+
auto handler =
409+
fml::MakeRefCounted<DirectManipulationEventHandler>(owner.get());
410+
int32_t device_id = (int32_t) reinterpret_cast<int64_t>(handler.get());
411+
EXPECT_CALL(viewport, GetPrimaryContent(_, _))
412+
.WillOnce(::testing::Invoke([&content](REFIID in, void** out) {
413+
*out = &content;
414+
return S_OK;
415+
}))
416+
.RetiresOnSaturation();
417+
EXPECT_CALL(content, GetContentTransform(_, 6))
418+
.WillOnce(::testing::Invoke([scale](float* transform, DWORD size) {
419+
transform[0] = 2.0f;
420+
transform[4] = 234.0;
421+
transform[5] = 345.0;
422+
return S_OK;
423+
}))
424+
.RetiresOnSaturation();
425+
EXPECT_CALL(delegate, OnPointerPanZoomStart(device_id));
426+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
427+
DIRECTMANIPULATION_RUNNING,
428+
DIRECTMANIPULATION_READY);
429+
EXPECT_CALL(content, GetContentTransform(_, 6))
430+
.WillOnce(::testing::Invoke(
431+
[scale, pan_x, pan_y](float* transform, DWORD size) {
432+
transform[0] = 2.0f * scale;
433+
transform[4] = 234.0 + pan_x;
434+
transform[5] = 345.0 + pan_y;
435+
return S_OK;
436+
}));
437+
EXPECT_CALL(delegate,
438+
OnPointerPanZoomUpdate(device_id, pan_x, pan_y, scale, 0));
439+
handler->OnContentUpdated((IDirectManipulationViewport*)&viewport,
440+
(IDirectManipulationContent*)&content);
441+
EXPECT_CALL(delegate, OnPointerPanZoomEnd(device_id));
442+
EXPECT_CALL(viewport, GetViewportRect(_))
443+
.WillOnce(::testing::Invoke([DISPLAY_WIDTH, DISPLAY_HEIGHT](RECT* rect) {
444+
rect->left = 0;
445+
rect->top = 0;
446+
rect->right = DISPLAY_WIDTH;
447+
rect->bottom = DISPLAY_HEIGHT;
448+
return S_OK;
449+
}));
450+
EXPECT_CALL(viewport, ZoomToRect(0, 0, DISPLAY_WIDTH, DISPLAY_HEIGHT, false))
451+
.WillOnce(::testing::Return(S_OK));
452+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
453+
DIRECTMANIPULATION_INERTIA,
454+
DIRECTMANIPULATION_RUNNING);
455+
handler->OnViewportStatusChanged((IDirectManipulationViewport*)&viewport,
456+
DIRECTMANIPULATION_READY,
457+
DIRECTMANIPULATION_INERTIA);
458+
}
459+
367460
} // namespace testing
368461
} // namespace flutter

shell/platform/windows/window.cc

Lines changed: 5 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -107,20 +107,13 @@ void Window::InitializeChild(const char* title,
107107
OutputDebugString(message);
108108
LocalFree(message);
109109
}
110-
DEVMODE dmi;
111-
ZeroMemory(&dmi, sizeof(dmi));
112-
dmi.dmSize = sizeof(dmi);
113-
if (EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &dmi)) {
114-
directManipulationPollingRate_ = dmi.dmDisplayFrequency;
115-
} else {
116-
OutputDebugString(
117-
L"Failed to get framerate, will use default of 60 Hz for gesture "
118-
L"polling.");
119-
}
120110
SetUserObjectInformationA(GetCurrentProcess(),
121111
UOI_TIMERPROC_EXCEPTION_SUPPRESSION, FALSE, 1);
122-
SetTimer(result, kDirectManipulationTimer,
123-
1000 / directManipulationPollingRate_, nullptr);
112+
// SetTimer is not precise, if a 16 ms interval is requested, it will instead
113+
// often fire in an interval of 32 ms. Providing a value of 14 will ensure it
114+
// runs every 16 ms, which will allow for 60 Hz trackpad gesture events, which
115+
// is the maximal frequency supported by SetTimer.
116+
SetTimer(result, kDirectManipulationTimer, 14, nullptr);
124117
direct_manipulation_owner_ = std::make_unique<DirectManipulationOwner>(this);
125118
direct_manipulation_owner_->Init(width, height);
126119
}
@@ -487,8 +480,6 @@ Window::HandleMessage(UINT const message,
487480
case WM_TIMER:
488481
if (wparam == kDirectManipulationTimer) {
489482
direct_manipulation_owner_->Update();
490-
SetTimer(window_handle_, kDirectManipulationTimer,
491-
1000 / directManipulationPollingRate_, nullptr);
492483
return 0;
493484
}
494485
break;

shell/platform/windows/window.h

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -299,9 +299,6 @@ class Window : public KeyboardManager::WindowDelegate {
299299

300300
// Timer identifier for DirectManipulation gesture polling.
301301
const static int kDirectManipulationTimer = 1;
302-
303-
// Frequency (Hz) to poll for DirectManipulation updates.
304-
int directManipulationPollingRate_ = 60;
305302
};
306303

307304
} // namespace flutter

0 commit comments

Comments
 (0)