ui_events_helper.cc revision cedac228d2dd51db4b79ea1e72c7f249408ee061
1// Copyright (c) 2012 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.
4
5#include "content/browser/renderer_host/ui_events_helper.h"
6
7#include "content/common/input/web_touch_event_traits.h"
8#include "third_party/WebKit/public/web/WebInputEvent.h"
9#include "ui/events/event.h"
10#include "ui/events/event_constants.h"
11
12namespace {
13
14int WebModifiersToUIFlags(int modifiers) {
15  int flags = ui::EF_NONE;
16
17  if (modifiers & blink::WebInputEvent::ShiftKey)
18    flags |= ui::EF_SHIFT_DOWN;
19  if (modifiers & blink::WebInputEvent::ControlKey)
20    flags |= ui::EF_CONTROL_DOWN;
21  if (modifiers & blink::WebInputEvent::AltKey)
22    flags |= ui::EF_ALT_DOWN;
23
24  if (modifiers & blink::WebInputEvent::LeftButtonDown)
25    flags |= ui::EF_LEFT_MOUSE_BUTTON;
26  if (modifiers & blink::WebInputEvent::RightButtonDown)
27    flags |= ui::EF_RIGHT_MOUSE_BUTTON;
28  if (modifiers & blink::WebInputEvent::MiddleButtonDown)
29    flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
30
31  if (modifiers & blink::WebInputEvent::CapsLockOn)
32    flags |= ui::EF_CAPS_LOCK_DOWN;
33
34  return flags;
35}
36
37ui::EventType WebTouchPointStateToEventType(
38    blink::WebTouchPoint::State state) {
39  switch (state) {
40    case blink::WebTouchPoint::StateReleased:
41      return ui::ET_TOUCH_RELEASED;
42
43    case blink::WebTouchPoint::StatePressed:
44      return ui::ET_TOUCH_PRESSED;
45
46    case blink::WebTouchPoint::StateMoved:
47      return ui::ET_TOUCH_MOVED;
48
49    case blink::WebTouchPoint::StateCancelled:
50      return ui::ET_TOUCH_CANCELLED;
51
52    default:
53      return ui::ET_UNKNOWN;
54  }
55}
56
57blink::WebTouchPoint::State TouchPointStateFromEvent(
58    const ui::TouchEvent& event) {
59  switch (event.type()) {
60    case ui::ET_TOUCH_PRESSED:
61      return blink::WebTouchPoint::StatePressed;
62    case ui::ET_TOUCH_RELEASED:
63      return blink::WebTouchPoint::StateReleased;
64    case ui::ET_TOUCH_MOVED:
65      return blink::WebTouchPoint::StateMoved;
66    case ui::ET_TOUCH_CANCELLED:
67      return blink::WebTouchPoint::StateCancelled;
68    default:
69      return blink::WebTouchPoint::StateUndefined;
70  }
71}
72
73blink::WebInputEvent::Type TouchEventTypeFromEvent(
74    const ui::TouchEvent& event) {
75  switch (event.type()) {
76    case ui::ET_TOUCH_PRESSED:
77      return blink::WebInputEvent::TouchStart;
78    case ui::ET_TOUCH_RELEASED:
79      return blink::WebInputEvent::TouchEnd;
80    case ui::ET_TOUCH_MOVED:
81      return blink::WebInputEvent::TouchMove;
82    case ui::ET_TOUCH_CANCELLED:
83      return blink::WebInputEvent::TouchCancel;
84    default:
85      return blink::WebInputEvent::Undefined;
86  }
87}
88
89}  // namespace
90
91namespace content {
92
93bool MakeUITouchEventsFromWebTouchEvents(
94    const TouchEventWithLatencyInfo& touch_with_latency,
95    ScopedVector<ui::TouchEvent>* list,
96    TouchEventCoordinateSystem coordinate_system) {
97  const blink::WebTouchEvent& touch = touch_with_latency.event;
98  ui::EventType type = ui::ET_UNKNOWN;
99  switch (touch.type) {
100    case blink::WebInputEvent::TouchStart:
101      type = ui::ET_TOUCH_PRESSED;
102      break;
103    case blink::WebInputEvent::TouchEnd:
104      type = ui::ET_TOUCH_RELEASED;
105      break;
106    case blink::WebInputEvent::TouchMove:
107      type = ui::ET_TOUCH_MOVED;
108      break;
109    case blink::WebInputEvent::TouchCancel:
110      type = ui::ET_TOUCH_CANCELLED;
111      break;
112    default:
113      NOTREACHED();
114      return false;
115  }
116
117  int flags = WebModifiersToUIFlags(touch.modifiers);
118  base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
119      static_cast<int64>(touch.timeStampSeconds * 1000000));
120  for (unsigned i = 0; i < touch.touchesLength; ++i) {
121    const blink::WebTouchPoint& point = touch.touches[i];
122    if (WebTouchPointStateToEventType(point.state) != type)
123      continue;
124    // ui events start in the co-ordinate space of the EventDispatcher.
125    gfx::PointF location;
126    if (coordinate_system == LOCAL_COORDINATES)
127      location = point.position;
128    else
129      location = point.screenPosition;
130    ui::TouchEvent* uievent = new ui::TouchEvent(type,
131          location,
132          flags,
133          point.id,
134          timestamp,
135          point.radiusX,
136          point.radiusY,
137          point.rotationAngle,
138          point.force);
139    uievent->set_latency(touch_with_latency.latency);
140    list->push_back(uievent);
141  }
142  return true;
143}
144
145blink::WebGestureEvent MakeWebGestureEventFromUIEvent(
146    const ui::GestureEvent& event) {
147  blink::WebGestureEvent gesture_event;
148
149  switch (event.type()) {
150    case ui::ET_GESTURE_TAP:
151      gesture_event.type = blink::WebInputEvent::GestureTap;
152      gesture_event.data.tap.tapCount = event.details().tap_count();
153      gesture_event.data.tap.width = event.details().bounding_box().width();
154      gesture_event.data.tap.height = event.details().bounding_box().height();
155      break;
156    case ui::ET_GESTURE_TAP_DOWN:
157      gesture_event.type = blink::WebInputEvent::GestureTapDown;
158      gesture_event.data.tapDown.width =
159          event.details().bounding_box().width();
160      gesture_event.data.tapDown.height =
161          event.details().bounding_box().height();
162      break;
163    case ui::ET_GESTURE_SHOW_PRESS:
164      gesture_event.type = blink::WebInputEvent::GestureShowPress;
165      gesture_event.data.showPress.width =
166          event.details().bounding_box().width();
167      gesture_event.data.showPress.height =
168          event.details().bounding_box().height();
169      break;
170    case ui::ET_GESTURE_TAP_CANCEL:
171      gesture_event.type = blink::WebInputEvent::GestureTapCancel;
172      break;
173    case ui::ET_GESTURE_SCROLL_BEGIN:
174      gesture_event.type = blink::WebInputEvent::GestureScrollBegin;
175      gesture_event.data.scrollBegin.deltaXHint =
176          event.details().scroll_x_hint();
177      gesture_event.data.scrollBegin.deltaYHint =
178          event.details().scroll_y_hint();
179      break;
180    case ui::ET_GESTURE_SCROLL_UPDATE:
181      gesture_event.type = blink::WebInputEvent::GestureScrollUpdate;
182      gesture_event.data.scrollUpdate.deltaX = event.details().scroll_x();
183      gesture_event.data.scrollUpdate.deltaY = event.details().scroll_y();
184      break;
185    case ui::ET_GESTURE_SCROLL_END:
186      gesture_event.type = blink::WebInputEvent::GestureScrollEnd;
187      break;
188    case ui::ET_GESTURE_PINCH_BEGIN:
189      gesture_event.type = blink::WebInputEvent::GesturePinchBegin;
190      break;
191    case ui::ET_GESTURE_PINCH_UPDATE:
192      gesture_event.type = blink::WebInputEvent::GesturePinchUpdate;
193      gesture_event.data.pinchUpdate.scale = event.details().scale();
194      break;
195    case ui::ET_GESTURE_PINCH_END:
196      gesture_event.type = blink::WebInputEvent::GesturePinchEnd;
197      break;
198    case ui::ET_SCROLL_FLING_START:
199      gesture_event.type = blink::WebInputEvent::GestureFlingStart;
200      gesture_event.data.flingStart.velocityX = event.details().velocity_x();
201      gesture_event.data.flingStart.velocityY = event.details().velocity_y();
202      break;
203    case ui::ET_SCROLL_FLING_CANCEL:
204      gesture_event.type = blink::WebInputEvent::GestureFlingCancel;
205      break;
206    case ui::ET_GESTURE_LONG_PRESS:
207      gesture_event.type = blink::WebInputEvent::GestureLongPress;
208      gesture_event.data.longPress.width =
209          event.details().bounding_box().width();
210      gesture_event.data.longPress.height =
211          event.details().bounding_box().height();
212      break;
213    case ui::ET_GESTURE_LONG_TAP:
214      gesture_event.type = blink::WebInputEvent::GestureLongTap;
215      gesture_event.data.longPress.width =
216          event.details().bounding_box().width();
217      gesture_event.data.longPress.height =
218          event.details().bounding_box().height();
219      break;
220    case ui::ET_GESTURE_TWO_FINGER_TAP:
221      gesture_event.type = blink::WebInputEvent::GestureTwoFingerTap;
222      gesture_event.data.twoFingerTap.firstFingerWidth =
223          event.details().first_finger_width();
224      gesture_event.data.twoFingerTap.firstFingerHeight =
225          event.details().first_finger_height();
226      break;
227    case ui::ET_GESTURE_BEGIN:
228    case ui::ET_GESTURE_END:
229    case ui::ET_GESTURE_SWIPE:
230      gesture_event.type = blink::WebInputEvent::Undefined;
231      break;
232    default:
233      NOTREACHED() << "Unknown gesture type: " << event.type();
234  }
235
236  gesture_event.sourceDevice = blink::WebGestureEvent::Touchscreen;
237  gesture_event.modifiers = EventFlagsToWebEventModifiers(event.flags());
238  gesture_event.timeStampSeconds = event.time_stamp().InSecondsF();
239
240  return gesture_event;
241}
242
243int EventFlagsToWebEventModifiers(int flags) {
244  int modifiers = 0;
245
246  if (flags & ui::EF_SHIFT_DOWN)
247    modifiers |= blink::WebInputEvent::ShiftKey;
248  if (flags & ui::EF_CONTROL_DOWN)
249    modifiers |= blink::WebInputEvent::ControlKey;
250  if (flags & ui::EF_ALT_DOWN)
251    modifiers |= blink::WebInputEvent::AltKey;
252  // TODO(beng): MetaKey/META_MASK
253  if (flags & ui::EF_LEFT_MOUSE_BUTTON)
254    modifiers |= blink::WebInputEvent::LeftButtonDown;
255  if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
256    modifiers |= blink::WebInputEvent::MiddleButtonDown;
257  if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
258    modifiers |= blink::WebInputEvent::RightButtonDown;
259  if (flags & ui::EF_CAPS_LOCK_DOWN)
260    modifiers |= blink::WebInputEvent::CapsLockOn;
261  return modifiers;
262}
263
264blink::WebTouchPoint* UpdateWebTouchEventFromUIEvent(
265    const ui::TouchEvent& event,
266    blink::WebTouchEvent* web_event) {
267  blink::WebTouchPoint* point = NULL;
268  switch (event.type()) {
269    case ui::ET_TOUCH_PRESSED:
270      // Add a new touch point.
271      if (web_event->touchesLength < blink::WebTouchEvent::touchesLengthCap) {
272        point = &web_event->touches[web_event->touchesLength++];
273        point->id = event.touch_id();
274      }
275      break;
276    case ui::ET_TOUCH_RELEASED:
277    case ui::ET_TOUCH_CANCELLED:
278    case ui::ET_TOUCH_MOVED: {
279      // The touch point should have been added to the event from an earlier
280      // _PRESSED event. So find that.
281      // At the moment, only a maximum of 4 touch-points are allowed. So a
282      // simple loop should be sufficient.
283      for (unsigned i = 0; i < web_event->touchesLength; ++i) {
284        point = web_event->touches + i;
285        if (point->id == event.touch_id())
286          break;
287        point = NULL;
288      }
289      break;
290    }
291    default:
292      DLOG(WARNING) << "Unknown touch event " << event.type();
293      break;
294  }
295
296  if (!point)
297    return NULL;
298
299  // The spec requires the radii values to be positive (and 1 when unknown).
300  point->radiusX = std::max(1.f, event.radius_x());
301  point->radiusY = std::max(1.f, event.radius_y());
302  point->rotationAngle = event.rotation_angle();
303  point->force = event.force();
304
305  // Update the location and state of the point.
306  point->state = TouchPointStateFromEvent(event);
307  if (point->state == blink::WebTouchPoint::StateMoved) {
308    // It is possible for badly written touch drivers to emit Move events even
309    // when the touch location hasn't changed. In such cases, consume the event
310    // and pretend nothing happened.
311    if (point->position.x == event.x() && point->position.y == event.y())
312      return NULL;
313  }
314  point->position.x = event.x();
315  point->position.y = event.y();
316
317  const gfx::Point root_point = event.root_location();
318  point->screenPosition.x = root_point.x();
319  point->screenPosition.y = root_point.y();
320
321  // Mark the rest of the points as stationary.
322  for (unsigned i = 0; i < web_event->touchesLength; ++i) {
323    blink::WebTouchPoint* iter = web_event->touches + i;
324    if (iter != point)
325      iter->state = blink::WebTouchPoint::StateStationary;
326  }
327
328  // Update the type of the touch event.
329  WebTouchEventTraits::ResetType(TouchEventTypeFromEvent(event),
330                                 event.time_stamp().InSecondsF(),
331                                 web_event);
332  web_event->modifiers = EventFlagsToWebEventModifiers(event.flags());
333
334  return point;
335}
336
337}  // namespace content
338