1 // Copyright 2013 The Chromium 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.
5 #include "content/browser/renderer_host/input/synthetic_smooth_move_gesture.h"
7 #include "base/logging.h"
8 #include "ui/gfx/geometry/point_f.h"
13 gfx::Vector2d
FloorTowardZero(const gfx::Vector2dF
& vector
) {
14 int x
= vector
.x() > 0 ? floor(vector
.x()) : ceil(vector
.x());
15 int y
= vector
.y() > 0 ? floor(vector
.y()) : ceil(vector
.y());
16 return gfx::Vector2d(x
, y
);
19 gfx::Vector2d
CeilFromZero(const gfx::Vector2dF
& vector
) {
20 int x
= vector
.x() > 0 ? ceil(vector
.x()) : floor(vector
.x());
21 int y
= vector
.y() > 0 ? ceil(vector
.y()) : floor(vector
.y());
22 return gfx::Vector2d(x
, y
);
25 gfx::Vector2dF
ProjectScalarOntoVector(float scalar
,
26 const gfx::Vector2dF
& vector
) {
27 return gfx::ScaleVector2d(vector
, scalar
/ vector
.Length());
30 const int kDefaultSpeedInPixelsPerSec
= 800;
34 SyntheticSmoothMoveGestureParams::SyntheticSmoothMoveGestureParams()
35 : speed_in_pixels_s(kDefaultSpeedInPixelsPerSec
),
39 SyntheticSmoothMoveGestureParams::~SyntheticSmoothMoveGestureParams() {}
41 SyntheticSmoothMoveGesture::SyntheticSmoothMoveGesture(
42 SyntheticSmoothMoveGestureParams params
)
44 current_move_segment_start_position_(params
.start_point
),
48 SyntheticSmoothMoveGesture::~SyntheticSmoothMoveGesture() {}
50 SyntheticGesture::Result
SyntheticSmoothMoveGesture::ForwardInputEvents(
51 const base::TimeTicks
& timestamp
,
52 SyntheticGestureTarget
* target
) {
53 if (state_
== SETUP
) {
55 current_move_segment_
= -1;
56 current_move_segment_stop_time_
= timestamp
;
59 switch (params_
.input_type
) {
60 case SyntheticSmoothMoveGestureParams::TOUCH_INPUT
:
61 ForwardTouchInputEvents(timestamp
, target
);
63 case SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT
:
64 ForwardMouseClickInputEvents(timestamp
, target
);
66 case SyntheticSmoothMoveGestureParams::MOUSE_WHEEL_INPUT
:
67 ForwardMouseWheelInputEvents(timestamp
, target
);
70 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED
;
72 return (state_
== DONE
) ? SyntheticGesture::GESTURE_FINISHED
73 : SyntheticGesture::GESTURE_RUNNING
;
76 // TODO(ssid): Clean up the switch statements by adding functions instead of
77 // large code, in the Forward*Events functions. Move the actions for all input
78 // types to different class (SyntheticInputDevice) which generates input events
79 // for all input types. The gesture class can use instance of device actions.
80 // Refer: crbug.com/461825
82 void SyntheticSmoothMoveGesture::ForwardTouchInputEvents(
83 const base::TimeTicks
& timestamp
,
84 SyntheticGestureTarget
* target
) {
85 base::TimeTicks event_timestamp
= timestamp
;
93 AddTouchSlopToFirstDistance(target
);
94 ComputeNextMoveSegment();
95 PressTouchPoint(target
, event_timestamp
);
99 event_timestamp
= ClampTimestamp(timestamp
);
100 gfx::Vector2dF delta
= GetPositionDeltaAtTime(event_timestamp
);
101 MoveTouchPoint(target
, delta
, event_timestamp
);
103 if (FinishedCurrentMoveSegment(event_timestamp
)) {
104 if (!IsLastMoveSegment()) {
105 current_move_segment_start_position_
+=
106 params_
.distances
[current_move_segment_
];
107 ComputeNextMoveSegment();
108 } else if (params_
.prevent_fling
) {
111 ReleaseTouchPoint(target
, event_timestamp
);
117 if (timestamp
- current_move_segment_stop_time_
>=
118 target
->PointerAssumedStoppedTime()) {
119 event_timestamp
= current_move_segment_stop_time_
+
120 target
->PointerAssumedStoppedTime();
121 ReleaseTouchPoint(target
, event_timestamp
);
127 << "State SETUP invalid for synthetic scroll using touch input.";
130 << "State DONE invalid for synthetic scroll using touch input.";
134 void SyntheticSmoothMoveGesture::ForwardMouseWheelInputEvents(
135 const base::TimeTicks
& timestamp
,
136 SyntheticGestureTarget
* target
) {
143 ComputeNextMoveSegment();
145 // Fall through to forward the first event.
147 // Even though WebMouseWheelEvents take floating point deltas,
148 // internally the scroll position is stored as an integer. We therefore
149 // keep track of the discrete delta which is consistent with the
150 // internal scrolling state. This ensures that when the gesture has
151 // finished we've scrolled exactly the specified distance.
152 base::TimeTicks event_timestamp
= ClampTimestamp(timestamp
);
153 gfx::Vector2dF current_move_segment_total_delta
=
154 GetPositionDeltaAtTime(event_timestamp
);
155 gfx::Vector2d delta_discrete
=
156 FloorTowardZero(current_move_segment_total_delta
-
157 current_move_segment_total_delta_discrete_
);
158 ForwardMouseWheelEvent(target
, delta_discrete
, event_timestamp
);
159 current_move_segment_total_delta_discrete_
+= delta_discrete
;
161 if (FinishedCurrentMoveSegment(event_timestamp
)) {
162 if (!IsLastMoveSegment()) {
163 current_move_segment_total_delta_discrete_
= gfx::Vector2d();
164 ComputeNextMoveSegment();
165 ForwardMouseWheelInputEvents(timestamp
, target
);
172 NOTREACHED() << "State SETUP invalid for synthetic scroll using mouse "
175 NOTREACHED() << "State STOPPING invalid for synthetic scroll using mouse "
179 << "State DONE invalid for synthetic scroll using mouse wheel input.";
183 void SyntheticSmoothMoveGesture::ForwardMouseClickInputEvents(
184 const base::TimeTicks
& timestamp
,
185 SyntheticGestureTarget
* target
) {
186 base::TimeTicks event_timestamp
= timestamp
;
193 ComputeNextMoveSegment();
194 PressMousePoint(target
, event_timestamp
);
198 base::TimeTicks event_timestamp
= ClampTimestamp(timestamp
);
199 gfx::Vector2dF delta
= GetPositionDeltaAtTime(event_timestamp
);
200 MoveMousePoint(target
, delta
, event_timestamp
);
202 if (FinishedCurrentMoveSegment(event_timestamp
)) {
203 if (!IsLastMoveSegment()) {
204 current_move_segment_start_position_
+=
205 params_
.distances
[current_move_segment_
];
206 ComputeNextMoveSegment();
208 ReleaseMousePoint(target
, event_timestamp
);
215 << "State STOPPING invalid for synthetic drag using mouse input.";
218 << "State SETUP invalid for synthetic drag using mouse input.";
221 << "State DONE invalid for synthetic drag using mouse input.";
225 void SyntheticSmoothMoveGesture::ForwardTouchEvent(
226 SyntheticGestureTarget
* target
,
227 const base::TimeTicks
& timestamp
) {
228 touch_event_
.timeStampSeconds
= ConvertTimestampToSeconds(timestamp
);
230 target
->DispatchInputEventToPlatform(touch_event_
);
233 void SyntheticSmoothMoveGesture::ForwardMouseWheelEvent(
234 SyntheticGestureTarget
* target
,
235 const gfx::Vector2dF
& delta
,
236 const base::TimeTicks
& timestamp
) const {
237 blink::WebMouseWheelEvent mouse_wheel_event
=
238 SyntheticWebMouseWheelEventBuilder::Build(delta
.x(), delta
.y(), 0, false);
240 mouse_wheel_event
.x
= current_move_segment_start_position_
.x();
241 mouse_wheel_event
.y
= current_move_segment_start_position_
.y();
243 mouse_wheel_event
.timeStampSeconds
= ConvertTimestampToSeconds(timestamp
);
245 target
->DispatchInputEventToPlatform(mouse_wheel_event
);
248 void SyntheticSmoothMoveGesture::PressTouchPoint(
249 SyntheticGestureTarget
* target
,
250 const base::TimeTicks
& timestamp
) {
251 DCHECK_EQ(current_move_segment_
, 0);
252 touch_event_
.PressPoint(current_move_segment_start_position_
.x(),
253 current_move_segment_start_position_
.y());
254 ForwardTouchEvent(target
, timestamp
);
257 void SyntheticSmoothMoveGesture::MoveTouchPoint(
258 SyntheticGestureTarget
* target
,
259 const gfx::Vector2dF
& delta
,
260 const base::TimeTicks
& timestamp
) {
261 DCHECK_GE(current_move_segment_
, 0);
262 DCHECK_LT(current_move_segment_
, static_cast<int>(params_
.distances
.size()));
263 gfx::PointF touch_position
= current_move_segment_start_position_
+ delta
;
264 touch_event_
.MovePoint(0, touch_position
.x(), touch_position
.y());
265 ForwardTouchEvent(target
, timestamp
);
268 void SyntheticSmoothMoveGesture::ReleaseTouchPoint(
269 SyntheticGestureTarget
* target
,
270 const base::TimeTicks
& timestamp
) {
271 DCHECK_EQ(current_move_segment_
,
272 static_cast<int>(params_
.distances
.size()) - 1);
273 touch_event_
.ReleasePoint(0);
274 ForwardTouchEvent(target
, timestamp
);
277 void SyntheticSmoothMoveGesture::PressMousePoint(
278 SyntheticGestureTarget
* target
,
279 const base::TimeTicks
& timestamp
) {
280 DCHECK_EQ(params_
.input_type
,
281 SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT
);
282 blink::WebMouseEvent mouse_event
= SyntheticWebMouseEventBuilder::Build(
283 blink::WebInputEvent::MouseDown
, current_move_segment_start_position_
.x(),
284 current_move_segment_start_position_
.y(), 0);
285 mouse_event
.clickCount
= 1;
286 mouse_event
.timeStampSeconds
= ConvertTimestampToSeconds(timestamp
);
287 target
->DispatchInputEventToPlatform(mouse_event
);
290 void SyntheticSmoothMoveGesture::ReleaseMousePoint(
291 SyntheticGestureTarget
* target
,
292 const base::TimeTicks
& timestamp
) {
293 DCHECK_EQ(params_
.input_type
,
294 SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT
);
295 gfx::PointF mouse_position
=
296 current_move_segment_start_position_
+ GetPositionDeltaAtTime(timestamp
);
297 blink::WebMouseEvent mouse_event
= SyntheticWebMouseEventBuilder::Build(
298 blink::WebInputEvent::MouseUp
, mouse_position
.x(), mouse_position
.y(), 0);
299 mouse_event
.timeStampSeconds
= ConvertTimestampToSeconds(timestamp
);
300 target
->DispatchInputEventToPlatform(mouse_event
);
303 void SyntheticSmoothMoveGesture::MoveMousePoint(
304 SyntheticGestureTarget
* target
,
305 const gfx::Vector2dF
& delta
,
306 const base::TimeTicks
& timestamp
) {
307 gfx::PointF mouse_position
= current_move_segment_start_position_
+ delta
;
308 DCHECK_EQ(params_
.input_type
,
309 SyntheticSmoothMoveGestureParams::MOUSE_DRAG_INPUT
);
310 blink::WebMouseEvent mouse_event
= SyntheticWebMouseEventBuilder::Build(
311 blink::WebInputEvent::MouseMove
, mouse_position
.x(), mouse_position
.y(),
313 mouse_event
.button
= blink::WebMouseEvent::ButtonLeft
;
314 mouse_event
.timeStampSeconds
= ConvertTimestampToSeconds(timestamp
);
315 target
->DispatchInputEventToPlatform(mouse_event
);
318 void SyntheticSmoothMoveGesture::AddTouchSlopToFirstDistance(
319 SyntheticGestureTarget
* target
) {
320 DCHECK_GE(params_
.distances
.size(), 1ul);
321 gfx::Vector2dF
& first_move_distance
= params_
.distances
[0];
322 DCHECK_GT(first_move_distance
.Length(), 0);
323 first_move_distance
+= CeilFromZero(ProjectScalarOntoVector(
324 target
->GetTouchSlopInDips(), first_move_distance
));
327 gfx::Vector2dF
SyntheticSmoothMoveGesture::GetPositionDeltaAtTime(
328 const base::TimeTicks
& timestamp
) const {
329 // Make sure the final delta is correct. Using the computation below can lead
330 // to issues with floating point precision.
331 if (FinishedCurrentMoveSegment(timestamp
))
332 return params_
.distances
[current_move_segment_
];
335 params_
.speed_in_pixels_s
*
336 (timestamp
- current_move_segment_start_time_
).InSecondsF();
337 return ProjectScalarOntoVector(delta_length
,
338 params_
.distances
[current_move_segment_
]);
341 void SyntheticSmoothMoveGesture::ComputeNextMoveSegment() {
342 current_move_segment_
++;
343 DCHECK_LT(current_move_segment_
, static_cast<int>(params_
.distances
.size()));
344 int64 total_duration_in_us
= static_cast<int64
>(
345 1e6
* (params_
.distances
[current_move_segment_
].Length() /
346 params_
.speed_in_pixels_s
));
347 DCHECK_GT(total_duration_in_us
, 0);
348 current_move_segment_start_time_
= current_move_segment_stop_time_
;
349 current_move_segment_stop_time_
=
350 current_move_segment_start_time_
+
351 base::TimeDelta::FromMicroseconds(total_duration_in_us
);
354 base::TimeTicks
SyntheticSmoothMoveGesture::ClampTimestamp(
355 const base::TimeTicks
& timestamp
) const {
356 return std::min(timestamp
, current_move_segment_stop_time_
);
359 bool SyntheticSmoothMoveGesture::FinishedCurrentMoveSegment(
360 const base::TimeTicks
& timestamp
) const {
361 return timestamp
>= current_move_segment_stop_time_
;
364 bool SyntheticSmoothMoveGesture::IsLastMoveSegment() const {
365 DCHECK_LT(current_move_segment_
, static_cast<int>(params_
.distances
.size()));
366 return current_move_segment_
==
367 static_cast<int>(params_
.distances
.size()) - 1;
370 bool SyntheticSmoothMoveGesture::MoveIsNoOp() const {
371 return params_
.distances
.size() == 0 || params_
.distances
[0].IsZero();
374 } // namespace content