event.cc revision 1e9bf3e0803691d0a228da41fc608347b6db4340
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 "ui/events/event.h"
6
7#if defined(USE_X11)
8#include <X11/Xlib.h>
9#endif
10
11#include <cmath>
12#include <cstring>
13
14#include "base/metrics/histogram.h"
15#include "base/strings/stringprintf.h"
16#include "ui/events/event_utils.h"
17#include "ui/events/keycodes/keyboard_code_conversion.h"
18#include "ui/gfx/point3_f.h"
19#include "ui/gfx/point_conversions.h"
20#include "ui/gfx/transform.h"
21#include "ui/gfx/transform_util.h"
22
23#if defined(USE_X11)
24#include "ui/events/keycodes/keyboard_code_conversion_x.h"
25#endif
26
27namespace {
28
29base::NativeEvent CopyNativeEvent(const base::NativeEvent& event) {
30#if defined(USE_X11)
31  if (!event || event->type == GenericEvent)
32    return NULL;
33  XEvent* copy = new XEvent;
34  *copy = *event;
35  return copy;
36#elif defined(OS_WIN)
37  return event;
38#elif defined(USE_OZONE)
39  return NULL;
40#else
41  NOTREACHED() <<
42      "Don't know how to copy base::NativeEvent for this platform";
43  return NULL;
44#endif
45}
46
47gfx::Point CalibratePoint(const gfx::Point& point,
48                          const gfx::Size& from,
49                          const gfx::Size& to) {
50  float calibrated_x =
51      static_cast<float>(point.x()) * to.width() / from.width();
52  float calibrated_y =
53      static_cast<float>(point.y()) * to.height() / from.height();
54  return gfx::Point(static_cast<int>(floorf(calibrated_x + 0.5f)),
55                    static_cast<int>(floorf(calibrated_y + 0.5f)));
56}
57
58std::string EventTypeName(ui::EventType type) {
59#define RETURN_IF_TYPE(t) if (type == ui::t)  return #t
60#define CASE_TYPE(t) case ui::t:  return #t
61  switch (type) {
62    CASE_TYPE(ET_UNKNOWN);
63    CASE_TYPE(ET_MOUSE_PRESSED);
64    CASE_TYPE(ET_MOUSE_DRAGGED);
65    CASE_TYPE(ET_MOUSE_RELEASED);
66    CASE_TYPE(ET_MOUSE_MOVED);
67    CASE_TYPE(ET_MOUSE_ENTERED);
68    CASE_TYPE(ET_MOUSE_EXITED);
69    CASE_TYPE(ET_KEY_PRESSED);
70    CASE_TYPE(ET_KEY_RELEASED);
71    CASE_TYPE(ET_MOUSEWHEEL);
72    CASE_TYPE(ET_MOUSE_CAPTURE_CHANGED);
73    CASE_TYPE(ET_TOUCH_RELEASED);
74    CASE_TYPE(ET_TOUCH_PRESSED);
75    CASE_TYPE(ET_TOUCH_MOVED);
76    CASE_TYPE(ET_TOUCH_STATIONARY);
77    CASE_TYPE(ET_TOUCH_CANCELLED);
78    CASE_TYPE(ET_DROP_TARGET_EVENT);
79    CASE_TYPE(ET_TRANSLATED_KEY_PRESS);
80    CASE_TYPE(ET_TRANSLATED_KEY_RELEASE);
81    CASE_TYPE(ET_GESTURE_SCROLL_BEGIN);
82    CASE_TYPE(ET_GESTURE_SCROLL_END);
83    CASE_TYPE(ET_GESTURE_SCROLL_UPDATE);
84    CASE_TYPE(ET_GESTURE_SHOW_PRESS);
85    CASE_TYPE(ET_GESTURE_TAP);
86    CASE_TYPE(ET_GESTURE_TAP_DOWN);
87    CASE_TYPE(ET_GESTURE_TAP_CANCEL);
88    CASE_TYPE(ET_GESTURE_BEGIN);
89    CASE_TYPE(ET_GESTURE_END);
90    CASE_TYPE(ET_GESTURE_TWO_FINGER_TAP);
91    CASE_TYPE(ET_GESTURE_PINCH_BEGIN);
92    CASE_TYPE(ET_GESTURE_PINCH_END);
93    CASE_TYPE(ET_GESTURE_PINCH_UPDATE);
94    CASE_TYPE(ET_GESTURE_LONG_PRESS);
95    CASE_TYPE(ET_GESTURE_LONG_TAP);
96    CASE_TYPE(ET_GESTURE_MULTIFINGER_SWIPE);
97    CASE_TYPE(ET_SCROLL);
98    CASE_TYPE(ET_SCROLL_FLING_START);
99    CASE_TYPE(ET_SCROLL_FLING_CANCEL);
100    CASE_TYPE(ET_CANCEL_MODE);
101    CASE_TYPE(ET_UMA_DATA);
102    case ui::ET_LAST: NOTREACHED(); return std::string();
103    // Don't include default, so that we get an error when new type is added.
104  }
105#undef CASE_TYPE
106
107  NOTREACHED();
108  return std::string();
109}
110
111bool IsX11SendEventTrue(const base::NativeEvent& event) {
112#if defined(USE_X11)
113  if (event && event->xany.send_event)
114    return true;
115#endif
116  return false;
117}
118
119}  // namespace
120
121namespace ui {
122
123////////////////////////////////////////////////////////////////////////////////
124// Event
125
126Event::~Event() {
127#if defined(USE_X11)
128  if (delete_native_event_)
129    delete native_event_;
130#endif
131}
132
133bool Event::HasNativeEvent() const {
134  base::NativeEvent null_event;
135  std::memset(&null_event, 0, sizeof(null_event));
136  return !!std::memcmp(&native_event_, &null_event, sizeof(null_event));
137}
138
139void Event::StopPropagation() {
140  // TODO(sad): Re-enable these checks once View uses dispatcher to dispatch
141  // events.
142  // CHECK(phase_ != EP_PREDISPATCH && phase_ != EP_POSTDISPATCH);
143  CHECK(cancelable_);
144  result_ = static_cast<EventResult>(result_ | ER_CONSUMED);
145}
146
147void Event::SetHandled() {
148  // TODO(sad): Re-enable these checks once View uses dispatcher to dispatch
149  // events.
150  // CHECK(phase_ != EP_PREDISPATCH && phase_ != EP_POSTDISPATCH);
151  CHECK(cancelable_);
152  result_ = static_cast<EventResult>(result_ | ER_HANDLED);
153}
154
155Event::Event(EventType type, base::TimeDelta time_stamp, int flags)
156    : type_(type),
157      time_stamp_(time_stamp),
158      flags_(flags),
159      dispatch_to_hidden_targets_(false),
160#if defined(USE_X11)
161      native_event_(NULL),
162#endif
163      delete_native_event_(false),
164      cancelable_(true),
165      target_(NULL),
166      phase_(EP_PREDISPATCH),
167      result_(ER_UNHANDLED) {
168  if (type_ < ET_LAST)
169    name_ = EventTypeName(type_);
170  Init();
171}
172
173Event::Event(const base::NativeEvent& native_event,
174             EventType type,
175             int flags)
176    : type_(type),
177      time_stamp_(EventTimeFromNative(native_event)),
178      flags_(flags),
179      dispatch_to_hidden_targets_(false),
180      delete_native_event_(false),
181      cancelable_(true),
182      target_(NULL),
183      phase_(EP_PREDISPATCH),
184      result_(ER_UNHANDLED) {
185  base::TimeDelta delta = EventTimeForNow() - time_stamp_;
186  if (type_ < ET_LAST)
187    name_ = EventTypeName(type_);
188  UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.Browser",
189                              delta.InMicroseconds(), 0, 1000000, 100);
190  std::string name_for_event =
191      base::StringPrintf("Event.Latency.Browser.%s", name_.c_str());
192  base::HistogramBase* counter_for_type =
193      base::Histogram::FactoryGet(
194          name_for_event,
195          0,
196          1000000,
197          100,
198          base::HistogramBase::kUmaTargetedHistogramFlag);
199  counter_for_type->Add(delta.InMicroseconds());
200  InitWithNativeEvent(native_event);
201}
202
203Event::Event(const Event& copy)
204    : type_(copy.type_),
205      time_stamp_(copy.time_stamp_),
206      latency_(copy.latency_),
207      flags_(copy.flags_),
208      dispatch_to_hidden_targets_(false),
209      native_event_(::CopyNativeEvent(copy.native_event_)),
210      delete_native_event_(false),
211      cancelable_(true),
212      target_(NULL),
213      phase_(EP_PREDISPATCH),
214      result_(ER_UNHANDLED) {
215  if (type_ < ET_LAST)
216    name_ = EventTypeName(type_);
217#if defined(USE_X11)
218  if (native_event_)
219    delete_native_event_ = true;
220#endif
221}
222
223void Event::SetType(EventType type) {
224  if (type_ < ET_LAST)
225    name_ = std::string();
226  type_ = type;
227  if (type_ < ET_LAST)
228    name_ = EventTypeName(type_);
229}
230
231void Event::Init() {
232  std::memset(&native_event_, 0, sizeof(native_event_));
233}
234
235void Event::InitWithNativeEvent(const base::NativeEvent& native_event) {
236  native_event_ = native_event;
237}
238
239void Event::InitLatencyInfo() {
240  latency_.AddLatencyNumberWithTimestamp(INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT,
241                                         0,
242                                         0,
243                                         base::TimeTicks::FromInternalValue(
244                                             time_stamp_.ToInternalValue()),
245                                         1,
246                                         true);
247  latency_.AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0);
248}
249
250////////////////////////////////////////////////////////////////////////////////
251// CancelModeEvent
252
253CancelModeEvent::CancelModeEvent()
254    : Event(ET_CANCEL_MODE, base::TimeDelta(), 0) {
255  set_cancelable(false);
256}
257
258CancelModeEvent::~CancelModeEvent() {
259}
260
261////////////////////////////////////////////////////////////////////////////////
262// LocatedEvent
263
264LocatedEvent::~LocatedEvent() {
265}
266
267LocatedEvent::LocatedEvent(const base::NativeEvent& native_event)
268    : Event(native_event,
269            EventTypeFromNative(native_event),
270            EventFlagsFromNative(native_event)),
271      location_(EventLocationFromNative(native_event)),
272      root_location_(location_) {
273}
274
275LocatedEvent::LocatedEvent(EventType type,
276                           const gfx::Point& location,
277                           const gfx::Point& root_location,
278                           base::TimeDelta time_stamp,
279                           int flags)
280    : Event(type, time_stamp, flags),
281      location_(location),
282      root_location_(root_location) {
283}
284
285void LocatedEvent::UpdateForRootTransform(
286    const gfx::Transform& reversed_root_transform) {
287  // Transform has to be done at root level.
288  gfx::Point3F p(location_);
289  reversed_root_transform.TransformPoint(&p);
290  root_location_ = location_ = gfx::ToFlooredPoint(p.AsPointF());
291}
292
293////////////////////////////////////////////////////////////////////////////////
294// MouseEvent
295
296MouseEvent::MouseEvent(const base::NativeEvent& native_event)
297    : LocatedEvent(native_event),
298      changed_button_flags_(
299          GetChangedMouseButtonFlagsFromNative(native_event)) {
300  if (type() == ET_MOUSE_PRESSED || type() == ET_MOUSE_RELEASED)
301    SetClickCount(GetRepeatCount(*this));
302}
303
304MouseEvent::MouseEvent(EventType type,
305                       const gfx::Point& location,
306                       const gfx::Point& root_location,
307                       int flags)
308    : LocatedEvent(type, location, root_location, EventTimeForNow(), flags),
309      changed_button_flags_(0) {
310  if (this->type() == ET_MOUSE_MOVED && IsAnyButton())
311    SetType(ET_MOUSE_DRAGGED);
312}
313
314// static
315bool MouseEvent::IsRepeatedClickEvent(
316    const MouseEvent& event1,
317    const MouseEvent& event2) {
318  // These values match the Windows defaults.
319  static const int kDoubleClickTimeMS = 500;
320  static const int kDoubleClickWidth = 4;
321  static const int kDoubleClickHeight = 4;
322
323  if (event1.type() != ET_MOUSE_PRESSED ||
324      event2.type() != ET_MOUSE_PRESSED)
325    return false;
326
327  // Compare flags, but ignore EF_IS_DOUBLE_CLICK to allow triple clicks.
328  if ((event1.flags() & ~EF_IS_DOUBLE_CLICK) !=
329      (event2.flags() & ~EF_IS_DOUBLE_CLICK))
330    return false;
331
332  base::TimeDelta time_difference = event2.time_stamp() - event1.time_stamp();
333
334  if (time_difference.InMilliseconds() > kDoubleClickTimeMS)
335    return false;
336
337  if (abs(event2.x() - event1.x()) > kDoubleClickWidth / 2)
338    return false;
339
340  if (abs(event2.y() - event1.y()) > kDoubleClickHeight / 2)
341    return false;
342
343  return true;
344}
345
346// static
347int MouseEvent::GetRepeatCount(const MouseEvent& event) {
348  int click_count = 1;
349  if (last_click_event_) {
350    if (event.type() == ui::ET_MOUSE_RELEASED)
351      return last_click_event_->GetClickCount();
352    if (IsX11SendEventTrue(event.native_event()))
353      click_count = last_click_event_->GetClickCount();
354    else if (IsRepeatedClickEvent(*last_click_event_, event))
355      click_count = last_click_event_->GetClickCount() + 1;
356    delete last_click_event_;
357  }
358  last_click_event_ = new MouseEvent(event);
359  if (click_count > 3)
360    click_count = 3;
361  last_click_event_->SetClickCount(click_count);
362  return click_count;
363}
364
365// static
366MouseEvent* MouseEvent::last_click_event_ = NULL;
367
368int MouseEvent::GetClickCount() const {
369  if (type() != ET_MOUSE_PRESSED && type() != ET_MOUSE_RELEASED)
370    return 0;
371
372  if (flags() & EF_IS_TRIPLE_CLICK)
373    return 3;
374  else if (flags() & EF_IS_DOUBLE_CLICK)
375    return 2;
376  else
377    return 1;
378}
379
380void MouseEvent::SetClickCount(int click_count) {
381  if (type() != ET_MOUSE_PRESSED && type() != ET_MOUSE_RELEASED)
382    return;
383
384  DCHECK(click_count > 0);
385  DCHECK(click_count <= 3);
386
387  int f = flags();
388  switch (click_count) {
389    case 1:
390      f &= ~EF_IS_DOUBLE_CLICK;
391      f &= ~EF_IS_TRIPLE_CLICK;
392      break;
393    case 2:
394      f |= EF_IS_DOUBLE_CLICK;
395      f &= ~EF_IS_TRIPLE_CLICK;
396      break;
397    case 3:
398      f &= ~EF_IS_DOUBLE_CLICK;
399      f |= EF_IS_TRIPLE_CLICK;
400      break;
401  }
402  set_flags(f);
403}
404
405////////////////////////////////////////////////////////////////////////////////
406// MouseWheelEvent
407
408MouseWheelEvent::MouseWheelEvent(const base::NativeEvent& native_event)
409    : MouseEvent(native_event),
410      offset_(GetMouseWheelOffset(native_event)) {
411}
412
413MouseWheelEvent::MouseWheelEvent(const ScrollEvent& scroll_event)
414    : MouseEvent(scroll_event),
415      offset_(scroll_event.x_offset(), scroll_event.y_offset()){
416  SetType(ET_MOUSEWHEEL);
417}
418
419MouseWheelEvent::MouseWheelEvent(const MouseEvent& mouse_event,
420                                 int x_offset,
421                                 int y_offset)
422    : MouseEvent(mouse_event), offset_(x_offset, y_offset) {
423  DCHECK(type() == ET_MOUSEWHEEL);
424}
425
426MouseWheelEvent::MouseWheelEvent(const MouseWheelEvent& mouse_wheel_event)
427    : MouseEvent(mouse_wheel_event),
428      offset_(mouse_wheel_event.offset()) {
429  DCHECK(type() == ET_MOUSEWHEEL);
430}
431
432#if defined(OS_WIN)
433// This value matches windows WHEEL_DELTA.
434// static
435const int MouseWheelEvent::kWheelDelta = 120;
436#else
437// This value matches GTK+ wheel scroll amount.
438const int MouseWheelEvent::kWheelDelta = 53;
439#endif
440
441void MouseWheelEvent::UpdateForRootTransform(
442    const gfx::Transform& inverted_root_transform) {
443  LocatedEvent::UpdateForRootTransform(inverted_root_transform);
444  gfx::DecomposedTransform decomp;
445  bool success = gfx::DecomposeTransform(&decomp, inverted_root_transform);
446  DCHECK(success);
447  if (decomp.scale[0])
448    offset_.set_x(offset_.x() * decomp.scale[0]);
449  if (decomp.scale[1])
450    offset_.set_y(offset_.y() * decomp.scale[1]);
451}
452
453////////////////////////////////////////////////////////////////////////////////
454// TouchEvent
455
456TouchEvent::TouchEvent(const base::NativeEvent& native_event)
457    : LocatedEvent(native_event),
458      touch_id_(GetTouchId(native_event)),
459      radius_x_(GetTouchRadiusX(native_event)),
460      radius_y_(GetTouchRadiusY(native_event)),
461      rotation_angle_(GetTouchAngle(native_event)),
462      force_(GetTouchForce(native_event)) {
463  InitLatencyInfo();
464}
465
466TouchEvent::TouchEvent(EventType type,
467                       const gfx::Point& location,
468                       int touch_id,
469                       base::TimeDelta time_stamp)
470    : LocatedEvent(type, location, location, time_stamp, 0),
471      touch_id_(touch_id),
472      radius_x_(0.0f),
473      radius_y_(0.0f),
474      rotation_angle_(0.0f),
475      force_(0.0f) {
476  InitLatencyInfo();
477}
478
479TouchEvent::TouchEvent(EventType type,
480                       const gfx::Point& location,
481                       int flags,
482                       int touch_id,
483                       base::TimeDelta time_stamp,
484                       float radius_x,
485                       float radius_y,
486                       float angle,
487                       float force)
488    : LocatedEvent(type, location, location, time_stamp, flags),
489      touch_id_(touch_id),
490      radius_x_(radius_x),
491      radius_y_(radius_y),
492      rotation_angle_(angle),
493      force_(force) {
494  InitLatencyInfo();
495}
496
497TouchEvent::~TouchEvent() {
498  // In ctor TouchEvent(native_event) we call GetTouchId() which in X11
499  // platform setups the tracking_id to slot mapping. So in dtor here,
500  // if this touch event is a release event, we clear the mapping accordingly.
501  if (HasNativeEvent())
502    ClearTouchIdIfReleased(native_event());
503}
504
505void TouchEvent::Relocate(const gfx::Point& origin) {
506  location_ -= origin.OffsetFromOrigin();
507  root_location_ -= origin.OffsetFromOrigin();
508}
509
510void TouchEvent::UpdateForRootTransform(
511    const gfx::Transform& inverted_root_transform) {
512  LocatedEvent::UpdateForRootTransform(inverted_root_transform);
513  gfx::DecomposedTransform decomp;
514  bool success = gfx::DecomposeTransform(&decomp, inverted_root_transform);
515  DCHECK(success);
516  if (decomp.scale[0])
517    radius_x_ *= decomp.scale[0];
518  if (decomp.scale[1])
519    radius_y_ *= decomp.scale[1];
520}
521
522////////////////////////////////////////////////////////////////////////////////
523// KeyEvent
524
525KeyEvent::KeyEvent(const base::NativeEvent& native_event, bool is_char)
526    : Event(native_event,
527            EventTypeFromNative(native_event),
528            EventFlagsFromNative(native_event)),
529      key_code_(KeyboardCodeFromNative(native_event)),
530      is_char_(is_char),
531      character_(0) {
532#if defined(USE_X11)
533  NormalizeFlags();
534#endif
535}
536
537KeyEvent::KeyEvent(EventType type,
538                   KeyboardCode key_code,
539                   int flags,
540                   bool is_char)
541    : Event(type, EventTimeForNow(), flags),
542      key_code_(key_code),
543      is_char_(is_char),
544      character_(GetCharacterFromKeyCode(key_code, flags)) {
545}
546
547uint16 KeyEvent::GetCharacter() const {
548  if (character_)
549    return character_;
550
551#if defined(OS_WIN)
552  return (native_event().message == WM_CHAR) ? key_code_ :
553      GetCharacterFromKeyCode(key_code_, flags());
554#elif defined(USE_X11)
555  if (!native_event())
556    return GetCharacterFromKeyCode(key_code_, flags());
557
558  DCHECK(native_event()->type == KeyPress ||
559         native_event()->type == KeyRelease);
560
561  uint16 ch = 0;
562  if (!IsControlDown())
563    ch = GetCharacterFromXEvent(native_event());
564  return ch ? ch : GetCharacterFromKeyCode(key_code_, flags());
565#else
566  NOTIMPLEMENTED();
567  return 0;
568#endif
569}
570
571KeyEvent* KeyEvent::Copy() const {
572#if defined(USE_OZONE)
573  KeyEvent* copy = new KeyEvent(*this);
574#else
575  KeyEvent* copy = HasNativeEvent() ?
576      new KeyEvent(::CopyNativeEvent(native_event()), is_char()) :
577      new KeyEvent(*this);
578#endif
579#if defined(USE_X11)
580  copy->set_delete_native_event(true);
581#endif
582  return copy;
583}
584
585bool KeyEvent::IsUnicodeKeyCode() const {
586  if (!IsAltDown())
587    return false;
588  const int key = key_code();
589  if (key >= VKEY_NUMPAD0 && key <= VKEY_NUMPAD9)
590    return true;
591  // Check whether the user is using the numeric keypad with num-lock off.
592  // In that case, EF_EXTENDED will not be set; if it is set, the key event
593  // originated from the relevant non-numpad dedicated key, e.g. [Insert].
594  return (!(flags() & EF_EXTENDED) &&
595          (key == VKEY_INSERT || key == VKEY_END  || key == VKEY_DOWN ||
596           key == VKEY_NEXT   || key == VKEY_LEFT || key == VKEY_CLEAR ||
597           key == VKEY_RIGHT  || key == VKEY_HOME || key == VKEY_UP ||
598           key == VKEY_PRIOR));
599}
600
601void KeyEvent::NormalizeFlags() {
602  int mask = 0;
603  switch (key_code()) {
604    case VKEY_CONTROL:
605      mask = EF_CONTROL_DOWN;
606      break;
607    case VKEY_SHIFT:
608      mask = EF_SHIFT_DOWN;
609      break;
610    case VKEY_MENU:
611      mask = EF_ALT_DOWN;
612      break;
613    case VKEY_CAPITAL:
614      mask = EF_CAPS_LOCK_DOWN;
615      break;
616    default:
617      return;
618  }
619  if (type() == ET_KEY_PRESSED)
620    set_flags(flags() | mask);
621  else
622    set_flags(flags() & ~mask);
623}
624
625////////////////////////////////////////////////////////////////////////////////
626// TranslatedKeyEvent
627
628TranslatedKeyEvent::TranslatedKeyEvent(const base::NativeEvent& native_event,
629                                       bool is_char)
630    : KeyEvent(native_event, is_char) {
631  SetType(type() == ET_KEY_PRESSED ?
632          ET_TRANSLATED_KEY_PRESS : ET_TRANSLATED_KEY_RELEASE);
633}
634
635TranslatedKeyEvent::TranslatedKeyEvent(bool is_press,
636                                       KeyboardCode key_code,
637                                       int flags)
638    : KeyEvent((is_press ? ET_TRANSLATED_KEY_PRESS : ET_TRANSLATED_KEY_RELEASE),
639               key_code,
640               flags,
641               false) {
642}
643
644void TranslatedKeyEvent::ConvertToKeyEvent() {
645  SetType(type() == ET_TRANSLATED_KEY_PRESS ?
646          ET_KEY_PRESSED : ET_KEY_RELEASED);
647}
648
649////////////////////////////////////////////////////////////////////////////////
650// ScrollEvent
651
652ScrollEvent::ScrollEvent(const base::NativeEvent& native_event)
653    : MouseEvent(native_event) {
654  if (type() == ET_SCROLL) {
655    GetScrollOffsets(native_event,
656                     &x_offset_, &y_offset_,
657                     &x_offset_ordinal_, &y_offset_ordinal_,
658                     &finger_count_);
659  } else if (type() == ET_SCROLL_FLING_START ||
660             type() == ET_SCROLL_FLING_CANCEL) {
661    GetFlingData(native_event,
662                 &x_offset_, &y_offset_,
663                 &x_offset_ordinal_, &y_offset_ordinal_,
664                 NULL);
665  } else {
666    NOTREACHED() << "Unexpected event type " << type()
667        << " when constructing a ScrollEvent.";
668  }
669}
670
671ScrollEvent::ScrollEvent(EventType type,
672                         const gfx::Point& location,
673                         base::TimeDelta time_stamp,
674                         int flags,
675                         float x_offset,
676                         float y_offset,
677                         float x_offset_ordinal,
678                         float y_offset_ordinal,
679                         int finger_count)
680    : MouseEvent(type, location, location, flags),
681      x_offset_(x_offset),
682      y_offset_(y_offset),
683      x_offset_ordinal_(x_offset_ordinal),
684      y_offset_ordinal_(y_offset_ordinal),
685      finger_count_(finger_count) {
686  set_time_stamp(time_stamp);
687  CHECK(IsScrollEvent());
688}
689
690void ScrollEvent::Scale(const float factor) {
691  x_offset_ *= factor;
692  y_offset_ *= factor;
693  x_offset_ordinal_ *= factor;
694  y_offset_ordinal_ *= factor;
695}
696
697////////////////////////////////////////////////////////////////////////////////
698// GestureEvent
699
700GestureEvent::GestureEvent(EventType type,
701                           int x,
702                           int y,
703                           int flags,
704                           base::TimeDelta time_stamp,
705                           const GestureEventDetails& details,
706                           unsigned int touch_ids_bitfield)
707    : LocatedEvent(type,
708                   gfx::Point(x, y),
709                   gfx::Point(x, y),
710                   time_stamp,
711                   flags | EF_FROM_TOUCH),
712      details_(details),
713      touch_ids_bitfield_(touch_ids_bitfield) {
714}
715
716GestureEvent::~GestureEvent() {
717}
718
719int GestureEvent::GetLowestTouchId() const {
720  if (touch_ids_bitfield_ == 0)
721    return -1;
722  int i = -1;
723  // Find the index of the least significant 1 bit
724  while (!(1 << ++i & touch_ids_bitfield_));
725  return i;
726}
727
728}  // namespace ui
729