1 // Copyright 2015 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 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
8 #include "ui/events/blink/blink_event_util.h"
12 #include "base/time/time.h"
13 #include "third_party/WebKit/public/web/WebInputEvent.h"
14 #include "ui/events/event_constants.h"
15 #include "ui/events/gesture_detection/gesture_event_data.h"
16 #include "ui/events/gesture_detection/motion_event.h"
17 #include "ui/events/gesture_event_details.h"
18 #include "ui/gfx/geometry/safe_integer_conversions.h"
20 using blink::WebGestureEvent
;
21 using blink::WebInputEvent
;
22 using blink::WebTouchEvent
;
23 using blink::WebTouchPoint
;
28 WebInputEvent::Type
ToWebInputEventType(MotionEvent::Action action
) {
30 case MotionEvent::ACTION_DOWN
:
31 return WebInputEvent::TouchStart
;
32 case MotionEvent::ACTION_MOVE
:
33 return WebInputEvent::TouchMove
;
34 case MotionEvent::ACTION_UP
:
35 return WebInputEvent::TouchEnd
;
36 case MotionEvent::ACTION_CANCEL
:
37 return WebInputEvent::TouchCancel
;
38 case MotionEvent::ACTION_POINTER_DOWN
:
39 return WebInputEvent::TouchStart
;
40 case MotionEvent::ACTION_POINTER_UP
:
41 return WebInputEvent::TouchEnd
;
43 NOTREACHED() << "Invalid MotionEvent::Action.";
44 return WebInputEvent::Undefined
;
47 // Note that |is_action_pointer| is meaningful only in the context of
48 // |ACTION_POINTER_UP| and |ACTION_POINTER_DOWN|; other actions map directly to
49 // WebTouchPoint::State.
50 WebTouchPoint::State
ToWebTouchPointState(const MotionEvent
& event
,
51 size_t pointer_index
) {
52 switch (event
.GetAction()) {
53 case MotionEvent::ACTION_DOWN
:
54 return WebTouchPoint::StatePressed
;
55 case MotionEvent::ACTION_MOVE
:
56 return WebTouchPoint::StateMoved
;
57 case MotionEvent::ACTION_UP
:
58 return WebTouchPoint::StateReleased
;
59 case MotionEvent::ACTION_CANCEL
:
60 return WebTouchPoint::StateCancelled
;
61 case MotionEvent::ACTION_POINTER_DOWN
:
62 return static_cast<int>(pointer_index
) == event
.GetActionIndex()
63 ? WebTouchPoint::StatePressed
64 : WebTouchPoint::StateStationary
;
65 case MotionEvent::ACTION_POINTER_UP
:
66 return static_cast<int>(pointer_index
) == event
.GetActionIndex()
67 ? WebTouchPoint::StateReleased
68 : WebTouchPoint::StateStationary
;
70 NOTREACHED() << "Invalid MotionEvent::Action.";
71 return WebTouchPoint::StateUndefined
;
74 WebTouchPoint::PointerType
ToWebTouchPointPointerType(const MotionEvent
& event
,
75 size_t pointer_index
) {
76 switch (event
.GetToolType(pointer_index
)) {
77 case MotionEvent::TOOL_TYPE_UNKNOWN
:
78 return WebTouchPoint::PointerTypeUnknown
;
79 case MotionEvent::TOOL_TYPE_FINGER
:
80 return WebTouchPoint::PointerTypeTouch
;
81 case MotionEvent::TOOL_TYPE_STYLUS
:
82 return WebTouchPoint::PointerTypePen
;
83 case MotionEvent::TOOL_TYPE_MOUSE
:
84 return WebTouchPoint::PointerTypeMouse
;
85 case MotionEvent::TOOL_TYPE_ERASER
:
86 return WebTouchPoint::PointerTypeUnknown
;
88 NOTREACHED() << "Invalid MotionEvent::ToolType = "
89 << event
.GetToolType(pointer_index
);
90 return WebTouchPoint::PointerTypeUnknown
;
93 WebTouchPoint
CreateWebTouchPoint(const MotionEvent
& event
,
94 size_t pointer_index
) {
96 touch
.id
= event
.GetPointerId(pointer_index
);
97 touch
.pointerType
= ToWebTouchPointPointerType(event
, pointer_index
);
98 touch
.state
= ToWebTouchPointState(event
, pointer_index
);
99 touch
.position
.x
= event
.GetX(pointer_index
);
100 touch
.position
.y
= event
.GetY(pointer_index
);
101 touch
.screenPosition
.x
= event
.GetRawX(pointer_index
);
102 touch
.screenPosition
.y
= event
.GetRawY(pointer_index
);
104 // A note on touch ellipse specifications:
106 // Android MotionEvent provides the major and minor axes of the touch ellipse,
107 // as well as the orientation of the major axis clockwise from vertical, in
109 // http://developer.android.com/reference/android/view/MotionEvent.html
111 // The proposed extension to W3C Touch Events specifies the touch ellipse
112 // using two radii along x- & y-axes and a positive acute rotation angle in
114 // http://dvcs.w3.org/hg/webevents/raw-file/default/touchevents.html
116 float major_radius
= event
.GetTouchMajor(pointer_index
) / 2.f
;
117 float minor_radius
= event
.GetTouchMinor(pointer_index
) / 2.f
;
119 DCHECK_LE(minor_radius
, major_radius
);
120 DCHECK_IMPLIES(major_radius
, minor_radius
);
122 float orientation_deg
= event
.GetOrientation(pointer_index
) * 180.f
/ M_PI
;
123 DCHECK_GE(major_radius
, 0);
124 DCHECK_GE(minor_radius
, 0);
125 DCHECK_GE(major_radius
, minor_radius
);
126 if (event
.GetToolType(pointer_index
) == MotionEvent::TOOL_TYPE_STYLUS
) {
127 // Orientation lies in [-180, 180] for a stylus. Normalise to [-90, 90).
128 // Allow a small bound tolerance to account for floating point conversion.
129 // TODO(e_hakkinen): crbug.com/493728: Pass also unaltered orientation
130 // to touch in order not to lose quadrant information.
131 DCHECK_GT(orientation_deg
, -180.01f
);
132 DCHECK_LT(orientation_deg
, 180.01f
);
133 if (orientation_deg
>= 90.f
)
134 orientation_deg
-= 180.f
;
135 else if (orientation_deg
< -90.f
)
136 orientation_deg
+= 180.f
;
138 // Orientation lies in [-90, 90] for a touch. Normalise to [-90, 90).
139 // Allow a small bound tolerance to account for floating point conversion.
140 DCHECK_GT(orientation_deg
, -90.01f
);
141 DCHECK_LT(orientation_deg
, 90.01f
);
142 if (orientation_deg
>= 90.f
)
143 orientation_deg
-= 180.f
;
145 if (orientation_deg
>= 0) {
146 // The case orientation_deg == 0 is handled here on purpose: although the
147 // 'else' block is equivalent in this case, we want to pass the 0 value
148 // unchanged (and 0 is the default value for many devices that don't
149 // report elliptical touches).
150 touch
.radiusX
= minor_radius
;
151 touch
.radiusY
= major_radius
;
152 touch
.rotationAngle
= orientation_deg
;
154 touch
.radiusX
= major_radius
;
155 touch
.radiusY
= minor_radius
;
156 touch
.rotationAngle
= orientation_deg
+ 90;
159 touch
.force
= event
.GetPressure(pointer_index
);
166 blink::WebTouchEvent
CreateWebTouchEventFromMotionEvent(
167 const MotionEvent
& event
,
168 bool may_cause_scrolling
) {
169 static_assert(static_cast<int>(MotionEvent::MAX_TOUCH_POINT_COUNT
) ==
170 static_cast<int>(blink::WebTouchEvent::touchesLengthCap
),
171 "inconsistent maximum number of active touch points");
173 blink::WebTouchEvent result
;
175 result
.type
= ToWebInputEventType(event
.GetAction());
176 result
.cancelable
= (result
.type
!= WebInputEvent::TouchCancel
);
177 result
.timeStampSeconds
=
178 (event
.GetEventTime() - base::TimeTicks()).InSecondsF(),
179 result
.causesScrollingIfUncanceled
= may_cause_scrolling
;
180 result
.modifiers
= EventFlagsToWebEventModifiers(event
.GetFlags());
181 DCHECK_NE(event
.GetUniqueEventId(), 0U);
182 result
.uniqueTouchEventId
= event
.GetUniqueEventId();
183 result
.touchesLength
=
184 std::min(static_cast<unsigned>(event
.GetPointerCount()),
185 static_cast<unsigned>(WebTouchEvent::touchesLengthCap
));
186 DCHECK_GT(result
.touchesLength
, 0U);
188 for (size_t i
= 0; i
< result
.touchesLength
; ++i
)
189 result
.touches
[i
] = CreateWebTouchPoint(event
, i
);
194 int EventFlagsToWebEventModifiers(int flags
) {
197 if (flags
& EF_SHIFT_DOWN
)
198 modifiers
|= blink::WebInputEvent::ShiftKey
;
199 if (flags
& EF_CONTROL_DOWN
)
200 modifiers
|= blink::WebInputEvent::ControlKey
;
201 if (flags
& EF_ALT_DOWN
)
202 modifiers
|= blink::WebInputEvent::AltKey
;
203 if (flags
& EF_COMMAND_DOWN
)
204 modifiers
|= blink::WebInputEvent::MetaKey
;
206 if (flags
& EF_LEFT_MOUSE_BUTTON
)
207 modifiers
|= blink::WebInputEvent::LeftButtonDown
;
208 if (flags
& EF_MIDDLE_MOUSE_BUTTON
)
209 modifiers
|= blink::WebInputEvent::MiddleButtonDown
;
210 if (flags
& EF_RIGHT_MOUSE_BUTTON
)
211 modifiers
|= blink::WebInputEvent::RightButtonDown
;
212 if (flags
& EF_CAPS_LOCK_DOWN
)
213 modifiers
|= blink::WebInputEvent::CapsLockOn
;
214 if (flags
& EF_IS_REPEAT
)
215 modifiers
|= blink::WebInputEvent::IsAutoRepeat
;
220 WebGestureEvent
CreateWebGestureEvent(const GestureEventDetails
& details
,
221 base::TimeDelta timestamp
,
222 const gfx::PointF
& location
,
223 const gfx::PointF
& raw_location
,
225 WebGestureEvent gesture
;
226 gesture
.timeStampSeconds
= timestamp
.InSecondsF();
227 gesture
.x
= gfx::ToFlooredInt(location
.x());
228 gesture
.y
= gfx::ToFlooredInt(location
.y());
229 gesture
.globalX
= gfx::ToFlooredInt(raw_location
.x());
230 gesture
.globalY
= gfx::ToFlooredInt(raw_location
.y());
231 gesture
.modifiers
= EventFlagsToWebEventModifiers(flags
);
232 gesture
.sourceDevice
= blink::WebGestureDeviceTouchscreen
;
234 switch (details
.type()) {
235 case ET_GESTURE_SHOW_PRESS
:
236 gesture
.type
= WebInputEvent::GestureShowPress
;
237 gesture
.data
.showPress
.width
= details
.bounding_box_f().width();
238 gesture
.data
.showPress
.height
= details
.bounding_box_f().height();
240 case ET_GESTURE_DOUBLE_TAP
:
241 gesture
.type
= WebInputEvent::GestureDoubleTap
;
242 DCHECK_EQ(1, details
.tap_count());
243 gesture
.data
.tap
.tapCount
= details
.tap_count();
244 gesture
.data
.tap
.width
= details
.bounding_box_f().width();
245 gesture
.data
.tap
.height
= details
.bounding_box_f().height();
248 gesture
.type
= WebInputEvent::GestureTap
;
249 DCHECK_GE(details
.tap_count(), 1);
250 gesture
.data
.tap
.tapCount
= details
.tap_count();
251 gesture
.data
.tap
.width
= details
.bounding_box_f().width();
252 gesture
.data
.tap
.height
= details
.bounding_box_f().height();
254 case ET_GESTURE_TAP_UNCONFIRMED
:
255 gesture
.type
= WebInputEvent::GestureTapUnconfirmed
;
256 DCHECK_EQ(1, details
.tap_count());
257 gesture
.data
.tap
.tapCount
= details
.tap_count();
258 gesture
.data
.tap
.width
= details
.bounding_box_f().width();
259 gesture
.data
.tap
.height
= details
.bounding_box_f().height();
261 case ET_GESTURE_LONG_PRESS
:
262 gesture
.type
= WebInputEvent::GestureLongPress
;
263 gesture
.data
.longPress
.width
= details
.bounding_box_f().width();
264 gesture
.data
.longPress
.height
= details
.bounding_box_f().height();
266 case ET_GESTURE_LONG_TAP
:
267 gesture
.type
= WebInputEvent::GestureLongTap
;
268 gesture
.data
.longPress
.width
= details
.bounding_box_f().width();
269 gesture
.data
.longPress
.height
= details
.bounding_box_f().height();
271 case ET_GESTURE_TWO_FINGER_TAP
:
272 gesture
.type
= blink::WebInputEvent::GestureTwoFingerTap
;
273 gesture
.data
.twoFingerTap
.firstFingerWidth
= details
.first_finger_width();
274 gesture
.data
.twoFingerTap
.firstFingerHeight
=
275 details
.first_finger_height();
277 case ET_GESTURE_SCROLL_BEGIN
:
278 gesture
.type
= WebInputEvent::GestureScrollBegin
;
279 gesture
.data
.scrollBegin
.deltaXHint
= details
.scroll_x_hint();
280 gesture
.data
.scrollBegin
.deltaYHint
= details
.scroll_y_hint();
282 case ET_GESTURE_SCROLL_UPDATE
:
283 gesture
.type
= WebInputEvent::GestureScrollUpdate
;
284 gesture
.data
.scrollUpdate
.deltaX
= details
.scroll_x();
285 gesture
.data
.scrollUpdate
.deltaY
= details
.scroll_y();
286 gesture
.data
.scrollUpdate
.previousUpdateInSequencePrevented
=
287 details
.previous_scroll_update_in_sequence_prevented();
289 case ET_GESTURE_SCROLL_END
:
290 gesture
.type
= WebInputEvent::GestureScrollEnd
;
292 case ET_SCROLL_FLING_START
:
293 gesture
.type
= WebInputEvent::GestureFlingStart
;
294 gesture
.data
.flingStart
.velocityX
= details
.velocity_x();
295 gesture
.data
.flingStart
.velocityY
= details
.velocity_y();
297 case ET_SCROLL_FLING_CANCEL
:
298 gesture
.type
= WebInputEvent::GestureFlingCancel
;
300 case ET_GESTURE_PINCH_BEGIN
:
301 gesture
.type
= WebInputEvent::GesturePinchBegin
;
303 case ET_GESTURE_PINCH_UPDATE
:
304 gesture
.type
= WebInputEvent::GesturePinchUpdate
;
305 gesture
.data
.pinchUpdate
.scale
= details
.scale();
307 case ET_GESTURE_PINCH_END
:
308 gesture
.type
= WebInputEvent::GesturePinchEnd
;
310 case ET_GESTURE_TAP_CANCEL
:
311 gesture
.type
= WebInputEvent::GestureTapCancel
;
313 case ET_GESTURE_TAP_DOWN
:
314 gesture
.type
= WebInputEvent::GestureTapDown
;
315 gesture
.data
.tapDown
.width
= details
.bounding_box_f().width();
316 gesture
.data
.tapDown
.height
= details
.bounding_box_f().height();
318 case ET_GESTURE_BEGIN
:
320 case ET_GESTURE_SWIPE
:
321 // The caller is responsible for discarding these gestures appropriately.
322 gesture
.type
= WebInputEvent::Undefined
;
325 NOTREACHED() << "EventType provided wasn't a valid gesture event: "
332 WebGestureEvent
CreateWebGestureEventFromGestureEventData(
333 const GestureEventData
& data
) {
334 return CreateWebGestureEvent(data
.details
, data
.time
- base::TimeTicks(),
335 gfx::PointF(data
.x
, data
.y
),
336 gfx::PointF(data
.raw_x
, data
.raw_y
), data
.flags
);