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