OLD | NEW |
1 // Copyright 2013 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "content/browser/renderer_host/input/synthetic_pinch_gesture.h" | 5 #include "content/browser/renderer_host/input/synthetic_pinch_gesture.h" |
6 | 6 |
7 #include <cmath> | 7 #include <cmath> |
8 | 8 |
9 #include "base/logging.h" | 9 #include "base/logging.h" |
10 #include "content/common/input/input_event.h" | 10 #include "content/common/input/input_event.h" |
11 #include "ui/events/latency_info.h" | 11 #include "ui/events/latency_info.h" |
12 | 12 |
13 namespace content { | 13 namespace content { |
14 | 14 |
15 SyntheticPinchGesture::SyntheticPinchGesture( | 15 SyntheticPinchGesture::SyntheticPinchGesture( |
16 const SyntheticPinchGestureParams& params) | 16 const SyntheticPinchGestureParams& params) |
17 : params_(params), | 17 : params_(params), |
18 current_y_0_(0.0f), | 18 start_y_0_(0.0f), |
19 current_y_1_(0.0f), | 19 start_y_1_(0.0f), |
20 target_y_0_(0.0f), | |
21 target_y_1_(0.0f), | |
22 gesture_source_type_(SyntheticGestureParams::DEFAULT_INPUT), | 20 gesture_source_type_(SyntheticGestureParams::DEFAULT_INPUT), |
23 state_(SETUP) { | 21 state_(SETUP) { |
24 DCHECK_GE(params_.total_num_pixels_covered, 0); | 22 DCHECK_GE(params_.total_num_pixels_covered, 0); |
25 } | 23 } |
26 | 24 |
27 SyntheticPinchGesture::~SyntheticPinchGesture() {} | 25 SyntheticPinchGesture::~SyntheticPinchGesture() {} |
28 | 26 |
29 SyntheticGesture::Result SyntheticPinchGesture::ForwardInputEvents( | 27 SyntheticGesture::Result SyntheticPinchGesture::ForwardInputEvents( |
30 const base::TimeDelta& interval, SyntheticGestureTarget* target) { | 28 const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { |
31 if (state_ == SETUP) { | 29 if (state_ == SETUP) { |
32 gesture_source_type_ = params_.gesture_source_type; | 30 gesture_source_type_ = params_.gesture_source_type; |
33 if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT) | 31 if (gesture_source_type_ == SyntheticGestureParams::DEFAULT_INPUT) |
34 gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType(); | 32 gesture_source_type_ = target->GetDefaultSyntheticGestureSourceType(); |
35 | 33 |
36 if (!target->SupportsSyntheticGestureSourceType(gesture_source_type_)) | 34 if (!target->SupportsSyntheticGestureSourceType(gesture_source_type_)) |
37 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM; | 35 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_SUPPORTED_BY_PLATFORM; |
38 | 36 |
39 state_ = STARTED; | 37 state_ = STARTED; |
| 38 start_time_ = timestamp; |
40 } | 39 } |
41 | 40 |
42 DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT); | 41 DCHECK_NE(gesture_source_type_, SyntheticGestureParams::DEFAULT_INPUT); |
43 if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) | 42 if (gesture_source_type_ == SyntheticGestureParams::TOUCH_INPUT) |
44 ForwardTouchInputEvents(interval, target); | 43 ForwardTouchInputEvents(timestamp, target); |
45 else | 44 else |
46 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; | 45 return SyntheticGesture::GESTURE_SOURCE_TYPE_NOT_IMPLEMENTED; |
47 | 46 |
48 return (state_ == DONE) ? SyntheticGesture::GESTURE_FINISHED | 47 return (state_ == DONE) ? SyntheticGesture::GESTURE_FINISHED |
49 : SyntheticGesture::GESTURE_RUNNING; | 48 : SyntheticGesture::GESTURE_RUNNING; |
50 } | 49 } |
51 | 50 |
52 void SyntheticPinchGesture::ForwardTouchInputEvents( | 51 void SyntheticPinchGesture::ForwardTouchInputEvents( |
53 const base::TimeDelta& interval, SyntheticGestureTarget* target) { | 52 const base::TimeTicks& timestamp, SyntheticGestureTarget* target) { |
54 switch (state_) { | 53 switch (state_) { |
55 case STARTED: | 54 case STARTED: |
56 // Check for an early finish. | 55 // Check for an early finish. |
57 if (params_.total_num_pixels_covered == 0) { | 56 if (params_.total_num_pixels_covered == 0) { |
58 state_ = DONE; | 57 state_ = DONE; |
59 break; | 58 break; |
60 } | 59 } |
61 SetupCoordinates(target); | 60 SetupCoordinatesAndStopTime(target); |
62 PressTouchPoints(target); | 61 PressTouchPoints(target, timestamp); |
63 state_ = MOVING; | 62 state_ = MOVING; |
64 break; | 63 break; |
65 case MOVING: | 64 case MOVING: { |
66 UpdateTouchPoints(interval); | 65 base::TimeTicks event_timestamp = ClampTimestamp(timestamp); |
67 MoveTouchPoints(target); | 66 float delta = GetDeltaForPointer0AtTime(event_timestamp); |
68 if (HasReachedTarget()) { | 67 MoveTouchPoints(target, delta, event_timestamp); |
69 ReleaseTouchPoints(target); | 68 if (HasReachedTarget(event_timestamp)) { |
| 69 ReleaseTouchPoints(target, event_timestamp); |
70 state_ = DONE; | 70 state_ = DONE; |
71 } | 71 } |
72 break; | 72 } break; |
73 case SETUP: | 73 case SETUP: |
74 NOTREACHED() << "State SETUP invalid for synthetic pinch."; | 74 NOTREACHED() << "State SETUP invalid for synthetic pinch."; |
75 case DONE: | 75 case DONE: |
76 NOTREACHED() << "State DONE invalid for synthetic pinch."; | 76 NOTREACHED() << "State DONE invalid for synthetic pinch."; |
77 } | 77 } |
78 } | 78 } |
79 | 79 |
80 void SyntheticPinchGesture::UpdateTouchPoints(base::TimeDelta interval) { | 80 void SyntheticPinchGesture::PressTouchPoints(SyntheticGestureTarget* target, |
81 // Compute the delta for the first pointer. The other one moves exactly | 81 const base::TimeTicks& timestamp) { |
82 // the same but in the opposite direction. | 82 touch_event_.PressPoint(params_.anchor.x(), start_y_0_); |
83 float delta = GetDeltaForPointer0(interval); | 83 touch_event_.PressPoint(params_.anchor.x(), start_y_1_); |
84 current_y_0_ += delta; | 84 ForwardTouchEvent(target, timestamp); |
85 current_y_1_ -= delta; | |
86 } | 85 } |
87 | 86 |
88 void SyntheticPinchGesture::PressTouchPoints(SyntheticGestureTarget* target) { | 87 void SyntheticPinchGesture::MoveTouchPoints(SyntheticGestureTarget* target, |
89 touch_event_.PressPoint(params_.anchor.x(), current_y_0_); | 88 float delta, |
90 touch_event_.PressPoint(params_.anchor.x(), current_y_1_); | 89 const base::TimeTicks& timestamp) { |
91 ForwardTouchEvent(target); | 90 // The two pointers move in opposite directions. |
92 } | 91 float current_y_0 = start_y_0_ + delta; |
| 92 float current_y_1 = start_y_1_ - delta; |
93 | 93 |
94 void SyntheticPinchGesture::MoveTouchPoints(SyntheticGestureTarget* target) { | |
95 // The current pointer positions are stored as float but the pointer | 94 // The current pointer positions are stored as float but the pointer |
96 // coordinates of the input event are integers. Floor both positions so that | 95 // coordinates of the input event are integers. Floor both positions so that |
97 // in case of an odd distance one of the pointers (the one whose position goes | 96 // in case of an odd distance one of the pointers (the one whose position goes |
98 // down) moves one pixel further than the other. The explicit flooring is only | 97 // down) moves one pixel further than the other. The explicit flooring is only |
99 // needed for negative values. | 98 // needed for negative values. |
100 touch_event_.MovePoint(0, params_.anchor.x(), floor(current_y_0_)); | 99 touch_event_.MovePoint(0, params_.anchor.x(), floor(current_y_0)); |
101 touch_event_.MovePoint(1, params_.anchor.x(), floor(current_y_1_)); | 100 touch_event_.MovePoint(1, params_.anchor.x(), floor(current_y_1)); |
102 ForwardTouchEvent(target); | 101 ForwardTouchEvent(target, timestamp); |
103 } | 102 } |
104 | 103 |
105 void SyntheticPinchGesture::ReleaseTouchPoints(SyntheticGestureTarget* target) { | 104 void SyntheticPinchGesture::ReleaseTouchPoints( |
| 105 SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { |
106 touch_event_.ReleasePoint(0); | 106 touch_event_.ReleasePoint(0); |
107 touch_event_.ReleasePoint(1); | 107 touch_event_.ReleasePoint(1); |
108 ForwardTouchEvent(target); | 108 ForwardTouchEvent(target, timestamp); |
109 } | 109 } |
110 | 110 |
111 | 111 void SyntheticPinchGesture::ForwardTouchEvent( |
112 void SyntheticPinchGesture::ForwardTouchEvent(SyntheticGestureTarget* target) | 112 SyntheticGestureTarget* target, const base::TimeTicks& timestamp) { |
113 const { | 113 touch_event_.timeStampSeconds = ConvertTimestampToSeconds(timestamp); |
114 target->DispatchInputEventToPlatform( | 114 target->DispatchInputEventToPlatform( |
115 InputEvent(touch_event_, ui::LatencyInfo(), false)); | 115 InputEvent(touch_event_, ui::LatencyInfo(), false)); |
116 } | 116 } |
117 | 117 |
118 void SyntheticPinchGesture::SetupCoordinates(SyntheticGestureTarget* target) { | 118 void SyntheticPinchGesture::SetupCoordinatesAndStopTime( |
119 const float kTouchSlopInDips = target->GetTouchSlopInDips(); | 119 SyntheticGestureTarget* target) { |
| 120 const int kTouchSlopInDips = target->GetTouchSlopInDips(); |
| 121 params_.total_num_pixels_covered += 2 * kTouchSlopInDips; |
120 float inner_distance_to_anchor = 2 * kTouchSlopInDips; | 122 float inner_distance_to_anchor = 2 * kTouchSlopInDips; |
121 float outer_distance_to_anchor = inner_distance_to_anchor + | 123 float outer_distance_to_anchor = |
122 params_.total_num_pixels_covered / 2.0f + | 124 inner_distance_to_anchor + params_.total_num_pixels_covered / 2.0f; |
123 kTouchSlopInDips; | |
124 | 125 |
125 // Move pointers away from each other to zoom in | 126 // Move pointers away from each other to zoom in |
126 // or towards each other to zoom out. | 127 // or towards each other to zoom out. |
127 if (params_.zoom_in) { | 128 if (params_.zoom_in) { |
128 current_y_0_ = params_.anchor.y() - inner_distance_to_anchor; | 129 start_y_0_ = params_.anchor.y() - inner_distance_to_anchor; |
129 current_y_1_ = params_.anchor.y() + inner_distance_to_anchor; | 130 start_y_1_ = params_.anchor.y() + inner_distance_to_anchor; |
130 target_y_0_ = params_.anchor.y() - outer_distance_to_anchor; | |
131 target_y_1_ = params_.anchor.y() + outer_distance_to_anchor; | |
132 } else { | 131 } else { |
133 current_y_0_ = params_.anchor.y() - outer_distance_to_anchor; | 132 start_y_0_ = params_.anchor.y() - outer_distance_to_anchor; |
134 current_y_1_ = params_.anchor.y() + outer_distance_to_anchor; | 133 start_y_1_ = params_.anchor.y() + outer_distance_to_anchor; |
135 target_y_0_ = params_.anchor.y() - inner_distance_to_anchor; | |
136 target_y_1_ = params_.anchor.y() + inner_distance_to_anchor; | |
137 } | 134 } |
| 135 |
| 136 int64 total_duration_in_us = static_cast<int64>( |
| 137 1e6 * (static_cast<double>(params_.total_num_pixels_covered) / |
| 138 params_.relative_pointer_speed_in_pixels_s)); |
| 139 DCHECK_GT(total_duration_in_us, 0); |
| 140 stop_time_ = |
| 141 start_time_ + base::TimeDelta::FromMicroseconds(total_duration_in_us); |
138 } | 142 } |
139 | 143 |
140 float SyntheticPinchGesture::GetDeltaForPointer0( | 144 float SyntheticPinchGesture::GetDeltaForPointer0AtTime( |
141 const base::TimeDelta& interval) const { | 145 const base::TimeTicks& timestamp) const { |
142 float total_abs_delta = | 146 float total_abs_delta; |
143 params_.relative_pointer_speed_in_pixels_s * interval.InSecondsF(); | |
144 | 147 |
145 // Make sure we're not moving too far in the final step. | 148 // Make sure the final delta is correct. Using the computation below can lead |
146 total_abs_delta = | 149 // to issues with floating point precision. |
147 std::min(total_abs_delta, ComputeAbsoluteRemainingDistance()); | 150 if (HasReachedTarget(timestamp)) |
| 151 total_abs_delta = params_.total_num_pixels_covered; |
| 152 else |
| 153 total_abs_delta = params_.relative_pointer_speed_in_pixels_s * |
| 154 (timestamp - start_time_).InSecondsF(); |
148 | 155 |
149 float abs_delta_pointer_0 = total_abs_delta / 2; | 156 float abs_delta_pointer_0 = total_abs_delta / 2.0f; |
150 return params_.zoom_in ? -abs_delta_pointer_0 : abs_delta_pointer_0; | 157 return params_.zoom_in ? -abs_delta_pointer_0 : abs_delta_pointer_0; |
151 } | 158 } |
152 | 159 |
153 float SyntheticPinchGesture::ComputeAbsoluteRemainingDistance() const { | 160 base::TimeTicks SyntheticPinchGesture::ClampTimestamp( |
154 float distance_0 = params_.zoom_in ? (current_y_0_ - target_y_0_) | 161 const base::TimeTicks& timestamp) const { |
155 : (target_y_0_ - current_y_0_); | 162 return std::min(timestamp, stop_time_); |
156 DCHECK_GE(distance_0, 0); | |
157 | |
158 // Both pointers move the same overall distance at the same speed. | |
159 return 2 * distance_0; | |
160 } | 163 } |
161 | 164 |
162 bool SyntheticPinchGesture::HasReachedTarget() const { | 165 bool SyntheticPinchGesture::HasReachedTarget(const base::TimeTicks& timestamp) |
163 return ComputeAbsoluteRemainingDistance() == 0; | 166 const { |
| 167 return timestamp >= stop_time_; |
164 } | 168 } |
165 | 169 |
166 } // namespace content | 170 } // namespace content |
OLD | NEW |