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 // MSVC++ requires this to be set before any other includes to get M_PI.
6 #define _USE_MATH_DEFINES
8 #include "content/browser/renderer_host/input/web_input_event_util.h"
12 #include "base/strings/string_util.h"
13 #include "content/common/input/web_touch_event_traits.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"
18 using blink::WebGestureEvent;
19 using blink::WebInputEvent;
20 using blink::WebTouchEvent;
21 using blink::WebTouchPoint;
22 using ui::MotionEvent;
26 const char* GetKeyIdentifier(ui::KeyboardCode key_code) {
30 case ui::VKEY_CONTROL:
34 case ui::VKEY_CAPITAL:
47 case ui::VKEY_EXECUTE:
101 case ui::VKEY_INSERT:
111 case ui::VKEY_SNAPSHOT:
112 return "PrintScreen";
115 case ui::VKEY_SCROLL:
117 case ui::VKEY_SELECT:
121 case ui::VKEY_DELETE:
122 return "U+007F"; // Standard says that DEL becomes U+007F.
123 case ui::VKEY_MEDIA_NEXT_TRACK:
124 return "MediaNextTrack";
125 case ui::VKEY_MEDIA_PREV_TRACK:
126 return "MediaPreviousTrack";
127 case ui::VKEY_MEDIA_STOP:
129 case ui::VKEY_MEDIA_PLAY_PAUSE:
130 return "MediaPlayPause";
131 case ui::VKEY_VOLUME_MUTE:
133 case ui::VKEY_VOLUME_DOWN:
135 case ui::VKEY_VOLUME_UP:
142 WebInputEvent::Type ToWebInputEventType(MotionEvent::Action action) {
144 case MotionEvent::ACTION_DOWN:
145 return WebInputEvent::TouchStart;
146 case MotionEvent::ACTION_MOVE:
147 return WebInputEvent::TouchMove;
148 case MotionEvent::ACTION_UP:
149 return WebInputEvent::TouchEnd;
150 case MotionEvent::ACTION_CANCEL:
151 return WebInputEvent::TouchCancel;
152 case MotionEvent::ACTION_POINTER_DOWN:
153 return WebInputEvent::TouchStart;
154 case MotionEvent::ACTION_POINTER_UP:
155 return WebInputEvent::TouchEnd;
157 NOTREACHED() << "Invalid MotionEvent::Action.";
158 return WebInputEvent::Undefined;
161 // Note that |is_action_pointer| is meaningful only in the context of
162 // |ACTION_POINTER_UP| and |ACTION_POINTER_DOWN|; other actions map directly to
163 // WebTouchPoint::State.
164 WebTouchPoint::State ToWebTouchPointState(MotionEvent::Action action,
165 bool is_action_pointer) {
167 case MotionEvent::ACTION_DOWN:
168 return WebTouchPoint::StatePressed;
169 case MotionEvent::ACTION_MOVE:
170 return WebTouchPoint::StateMoved;
171 case MotionEvent::ACTION_UP:
172 return WebTouchPoint::StateReleased;
173 case MotionEvent::ACTION_CANCEL:
174 return WebTouchPoint::StateCancelled;
175 case MotionEvent::ACTION_POINTER_DOWN:
176 return is_action_pointer ? WebTouchPoint::StatePressed
177 : WebTouchPoint::StateStationary;
178 case MotionEvent::ACTION_POINTER_UP:
179 return is_action_pointer ? WebTouchPoint::StateReleased
180 : WebTouchPoint::StateStationary;
182 NOTREACHED() << "Invalid MotionEvent::Action.";
183 return WebTouchPoint::StateUndefined;
186 WebTouchPoint CreateWebTouchPoint(const MotionEvent& event,
187 size_t pointer_index) {
189 touch.id = event.GetPointerId(pointer_index);
190 touch.state = ToWebTouchPointState(
192 static_cast<int>(pointer_index) == event.GetActionIndex());
193 touch.position.x = event.GetX(pointer_index);
194 touch.position.y = event.GetY(pointer_index);
195 touch.screenPosition.x = event.GetRawX(pointer_index);
196 touch.screenPosition.y = event.GetRawY(pointer_index);
198 // A note on touch ellipse specifications:
200 // Android MotionEvent provides the major and minor axes of the touch ellipse,
201 // as well as the orientation of the major axis clockwise from vertical, in
203 // http://developer.android.com/reference/android/view/MotionEvent.html
205 // The proposed extension to W3C Touch Events specifies the touch ellipse
206 // using two radii along x- & y-axes and a positive acute rotation angle in
208 // http://dvcs.w3.org/hg/webevents/raw-file/default/touchevents.html
210 float major_radius = event.GetTouchMajor(pointer_index) / 2.f;
211 float minor_radius = event.GetTouchMinor(pointer_index) / 2.f;
212 float orientation_deg = event.GetOrientation(pointer_index) * 180.f / M_PI;
213 DCHECK_GE(major_radius, 0);
214 DCHECK_GE(minor_radius, 0);
215 DCHECK_GE(major_radius, minor_radius);
216 // Allow a small bound tolerance to account for floating point conversion.
217 DCHECK_GT(orientation_deg, -90.01f);
218 DCHECK_LT(orientation_deg, 90.01f);
219 if (orientation_deg >= 0) {
220 // The case orientation_deg == 0 is handled here on purpose: although the
221 // 'else' block is equivalent in this case, we want to pass the 0 value
222 // unchanged (and 0 is the default value for many devices that don't
223 // report elliptical touches).
224 touch.radiusX = minor_radius;
225 touch.radiusY = major_radius;
226 touch.rotationAngle = orientation_deg;
228 touch.radiusX = major_radius;
229 touch.radiusY = minor_radius;
230 touch.rotationAngle = orientation_deg + 90;
233 touch.force = event.GetPressure(pointer_index);
242 void UpdateWindowsKeyCodeAndKeyIdentifier(blink::WebKeyboardEvent* event,
243 ui::KeyboardCode windows_key_code) {
244 event->windowsKeyCode = windows_key_code;
246 const char* id = GetKeyIdentifier(windows_key_code);
248 base::strlcpy(event->keyIdentifier, id, sizeof(event->keyIdentifier) - 1);
250 base::snprintf(event->keyIdentifier,
251 sizeof(event->keyIdentifier),
253 base::ToUpperASCII(static_cast<int>(windows_key_code)));
257 blink::WebTouchEvent CreateWebTouchEventFromMotionEvent(
258 const ui::MotionEvent& event) {
259 COMPILE_ASSERT(static_cast<int>(MotionEvent::MAX_TOUCH_POINT_COUNT) ==
260 static_cast<int>(blink::WebTouchEvent::touchesLengthCap),
261 inconsistent_maximum_number_of_active_touch_points);
263 blink::WebTouchEvent result;
265 WebTouchEventTraits::ResetType(
266 ToWebInputEventType(event.GetAction()),
267 (event.GetEventTime() - base::TimeTicks()).InSecondsF(),
270 result.modifiers = EventFlagsToWebEventModifiers(event.GetFlags());
271 result.touchesLength =
272 std::min(event.GetPointerCount(),
273 static_cast<size_t>(WebTouchEvent::touchesLengthCap));
274 DCHECK_GT(result.touchesLength, 0U);
276 for (size_t i = 0; i < result.touchesLength; ++i)
277 result.touches[i] = CreateWebTouchPoint(event, i);
282 WebGestureEvent CreateWebGestureEventFromGestureEventData(
283 const ui::GestureEventData& data) {
284 WebGestureEvent gesture;
285 gesture.modifiers = EventFlagsToWebEventModifiers(data.flags);
288 gesture.globalX = data.raw_x;
289 gesture.globalY = data.raw_y;
290 gesture.timeStampSeconds = (data.time - base::TimeTicks()).InSecondsF();
291 gesture.sourceDevice = blink::WebGestureDeviceTouchscreen;
293 switch (data.type()) {
294 case ui::ET_GESTURE_SHOW_PRESS:
295 gesture.type = WebInputEvent::GestureShowPress;
296 gesture.data.showPress.width = data.details.bounding_box_f().width();
297 gesture.data.showPress.height = data.details.bounding_box_f().height();
299 case ui::ET_GESTURE_DOUBLE_TAP:
300 gesture.type = WebInputEvent::GestureDoubleTap;
301 DCHECK_EQ(1, data.details.tap_count());
302 gesture.data.tap.tapCount = data.details.tap_count();
303 gesture.data.tap.width = data.details.bounding_box_f().width();
304 gesture.data.tap.height = data.details.bounding_box_f().height();
306 case ui::ET_GESTURE_TAP:
307 gesture.type = WebInputEvent::GestureTap;
308 DCHECK_EQ(1, data.details.tap_count());
309 gesture.data.tap.tapCount = data.details.tap_count();
310 gesture.data.tap.width = data.details.bounding_box_f().width();
311 gesture.data.tap.height = data.details.bounding_box_f().height();
313 case ui::ET_GESTURE_TAP_UNCONFIRMED:
314 gesture.type = WebInputEvent::GestureTapUnconfirmed;
315 DCHECK_EQ(1, data.details.tap_count());
316 gesture.data.tap.tapCount = data.details.tap_count();
317 gesture.data.tap.width = data.details.bounding_box_f().width();
318 gesture.data.tap.height = data.details.bounding_box_f().height();
320 case ui::ET_GESTURE_LONG_PRESS:
321 gesture.type = WebInputEvent::GestureLongPress;
322 gesture.data.longPress.width = data.details.bounding_box_f().width();
323 gesture.data.longPress.height = data.details.bounding_box_f().height();
325 case ui::ET_GESTURE_LONG_TAP:
326 gesture.type = WebInputEvent::GestureLongTap;
327 gesture.data.longPress.width = data.details.bounding_box_f().width();
328 gesture.data.longPress.height = data.details.bounding_box_f().height();
330 case ui::ET_GESTURE_SCROLL_BEGIN:
331 gesture.type = WebInputEvent::GestureScrollBegin;
332 gesture.data.scrollBegin.deltaXHint = data.details.scroll_x_hint();
333 gesture.data.scrollBegin.deltaYHint = data.details.scroll_y_hint();
335 case ui::ET_GESTURE_SCROLL_UPDATE:
336 gesture.type = WebInputEvent::GestureScrollUpdate;
337 gesture.data.scrollUpdate.deltaX = data.details.scroll_x();
338 gesture.data.scrollUpdate.deltaY = data.details.scroll_y();
340 case ui::ET_GESTURE_SCROLL_END:
341 gesture.type = WebInputEvent::GestureScrollEnd;
343 case ui::ET_SCROLL_FLING_START:
344 gesture.type = WebInputEvent::GestureFlingStart;
345 gesture.data.flingStart.velocityX = data.details.velocity_x();
346 gesture.data.flingStart.velocityY = data.details.velocity_y();
348 case ui::ET_SCROLL_FLING_CANCEL:
349 gesture.type = WebInputEvent::GestureFlingCancel;
351 case ui::ET_GESTURE_PINCH_BEGIN:
352 gesture.type = WebInputEvent::GesturePinchBegin;
354 case ui::ET_GESTURE_PINCH_UPDATE:
355 gesture.type = WebInputEvent::GesturePinchUpdate;
356 gesture.data.pinchUpdate.scale = data.details.scale();
358 case ui::ET_GESTURE_PINCH_END:
359 gesture.type = WebInputEvent::GesturePinchEnd;
361 case ui::ET_GESTURE_TAP_CANCEL:
362 gesture.type = WebInputEvent::GestureTapCancel;
364 case ui::ET_GESTURE_TAP_DOWN:
365 gesture.type = WebInputEvent::GestureTapDown;
366 gesture.data.tapDown.width = data.details.bounding_box_f().width();
367 gesture.data.tapDown.height = data.details.bounding_box_f().height();
369 case ui::ET_GESTURE_BEGIN:
370 case ui::ET_GESTURE_END:
371 NOTREACHED() << "ET_GESTURE_BEGIN and ET_GESTURE_END are only produced "
372 << "in Aura, and should never end up here.";
375 NOTREACHED() << "ui::EventType provided wasn't a valid gesture event.";
382 int EventFlagsToWebEventModifiers(int flags) {
385 if (flags & ui::EF_SHIFT_DOWN)
386 modifiers |= blink::WebInputEvent::ShiftKey;
387 if (flags & ui::EF_CONTROL_DOWN)
388 modifiers |= blink::WebInputEvent::ControlKey;
389 if (flags & ui::EF_ALT_DOWN)
390 modifiers |= blink::WebInputEvent::AltKey;
391 if (flags & ui::EF_COMMAND_DOWN)
392 modifiers |= blink::WebInputEvent::MetaKey;
394 if (flags & ui::EF_LEFT_MOUSE_BUTTON)
395 modifiers |= blink::WebInputEvent::LeftButtonDown;
396 if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
397 modifiers |= blink::WebInputEvent::MiddleButtonDown;
398 if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
399 modifiers |= blink::WebInputEvent::RightButtonDown;
400 if (flags & ui::EF_CAPS_LOCK_DOWN)
401 modifiers |= blink::WebInputEvent::CapsLockOn;
402 if (flags & ui::EF_IS_REPEAT)
403 modifiers |= blink::WebInputEvent::IsAutoRepeat;
404 if (flags & ui::EF_NUMPAD_KEY)
405 modifiers |= blink::WebInputEvent::IsKeyPad;
410 int WebEventModifiersToEventFlags(int modifiers) {
413 if (modifiers & blink::WebInputEvent::ShiftKey)
414 flags |= ui::EF_SHIFT_DOWN;
415 if (modifiers & blink::WebInputEvent::ControlKey)
416 flags |= ui::EF_CONTROL_DOWN;
417 if (modifiers & blink::WebInputEvent::AltKey)
418 flags |= ui::EF_ALT_DOWN;
419 if (modifiers & blink::WebInputEvent::MetaKey)
420 flags |= ui::EF_COMMAND_DOWN;
422 if (modifiers & blink::WebInputEvent::LeftButtonDown)
423 flags |= ui::EF_LEFT_MOUSE_BUTTON;
424 if (modifiers & blink::WebInputEvent::MiddleButtonDown)
425 flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
426 if (modifiers & blink::WebInputEvent::RightButtonDown)
427 flags |= ui::EF_RIGHT_MOUSE_BUTTON;
428 if (modifiers & blink::WebInputEvent::CapsLockOn)
429 flags |= ui::EF_CAPS_LOCK_DOWN;
430 if (modifiers & blink::WebInputEvent::IsAutoRepeat)
431 flags |= ui::EF_IS_REPEAT;
432 if (modifiers & blink::WebInputEvent::IsKeyPad)
433 flags |= ui::EF_NUMPAD_KEY;
438 } // namespace content