blob: 7753a8af74eac39b3dbc73dafb8510df33a8949d [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ui/events/event.h"
#include <utility>
#include "base/memory/ptr_util.h"
#if defined(USE_X11)
#include <X11/extensions/XInput2.h>
#include <X11/keysym.h>
#include <X11/Xlib.h>
#endif
#include <cmath>
#include <cstring>
#include "base/metrics/histogram.h"
#include "base/strings/stringprintf.h"
#include "build/build_config.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/dom_code.h"
#include "ui/events/keycodes/dom/dom_key.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/gfx/geometry/point3_f.h"
#include "ui/gfx/geometry/point_conversions.h"
#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/transform.h"
#include "ui/gfx/transform_util.h"
#if defined(USE_X11)
#include "ui/events/keycodes/keyboard_code_conversion_x.h" // nogncheck
#elif defined(USE_OZONE)
#include "ui/events/ozone/layout/keyboard_layout_engine.h" // nogncheck
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" // nogncheck
#endif
#if defined(OS_WIN)
#include "ui/events/keycodes/platform_key_map_win.h"
#endif
// Support a collection of histograms, perhaps one for each entry in an
// enumeration. This macro manages a block of pointers, adding to a specific
// one by its index.
//
// A typical instantiation looks something like this:
// STATIC_HISTOGRAM_POINTER_GROUP(
// GetHistogramNameForIndex(histogram_index),
// histogram_index, MAXIMUM_HISTOGRAM_INDEX, Add(some_delta),
// base::Histogram::FactoryGet(
// GetHistogramNameForType(histogram_index),
// MINIMUM_SAMPLE, MAXIMUM_SAMPLE, BUCKET_COUNT,
// base::HistogramBase::kUmaTargetedHistogramFlag));
//
// Though it seems inefficient to generate the name twice, the first
// instance will be used only for DCHECK builds and the second will
// execute only during the first access to the given index, after which
// the pointer is cached and the name never needed again.
//
// This is defined in this way so that it can be moved unchanged into
// base/metrics/histogram_macros.h if it is useful in other files.
#define STATIC_HISTOGRAM_POINTER_GROUP(constant_histogram_name, index, \
constant_maximum, \
histogram_add_method_invocation, \
histogram_factory_get_invocation) \
do { \
static base::subtle::AtomicWord atomic_histograms[constant_maximum]; \
DCHECK_LE(0, index); \
DCHECK_LT(index, constant_maximum); \
HISTOGRAM_POINTER_USE(&atomic_histograms[index], constant_histogram_name, \
histogram_add_method_invocation, \
histogram_factory_get_invocation); \
} while (0)
namespace {
std::string EventTypeName(ui::EventType type) {
#define RETURN_IF_TYPE(t) if (type == ui::t) return #t
#define CASE_TYPE(t) case ui::t: return #t
switch (type) {
CASE_TYPE(ET_UNKNOWN);
CASE_TYPE(ET_MOUSE_PRESSED);
CASE_TYPE(ET_MOUSE_DRAGGED);
CASE_TYPE(ET_MOUSE_RELEASED);
CASE_TYPE(ET_MOUSE_MOVED);
CASE_TYPE(ET_MOUSE_ENTERED);
CASE_TYPE(ET_MOUSE_EXITED);
CASE_TYPE(ET_KEY_PRESSED);
CASE_TYPE(ET_KEY_RELEASED);
CASE_TYPE(ET_MOUSEWHEEL);
CASE_TYPE(ET_MOUSE_CAPTURE_CHANGED);
CASE_TYPE(ET_TOUCH_RELEASED);
CASE_TYPE(ET_TOUCH_PRESSED);
CASE_TYPE(ET_TOUCH_MOVED);
CASE_TYPE(ET_TOUCH_CANCELLED);
CASE_TYPE(ET_DROP_TARGET_EVENT);
CASE_TYPE(ET_POINTER_DOWN);
CASE_TYPE(ET_POINTER_MOVED);
CASE_TYPE(ET_POINTER_UP);
CASE_TYPE(ET_POINTER_CANCELLED);
CASE_TYPE(ET_POINTER_ENTERED);
CASE_TYPE(ET_POINTER_EXITED);
CASE_TYPE(ET_GESTURE_SCROLL_BEGIN);
CASE_TYPE(ET_GESTURE_SCROLL_END);
CASE_TYPE(ET_GESTURE_SCROLL_UPDATE);
CASE_TYPE(ET_GESTURE_SHOW_PRESS);
CASE_TYPE(ET_GESTURE_WIN8_EDGE_SWIPE);
CASE_TYPE(ET_GESTURE_TAP);
CASE_TYPE(ET_GESTURE_TAP_DOWN);
CASE_TYPE(ET_GESTURE_TAP_CANCEL);
CASE_TYPE(ET_GESTURE_BEGIN);
CASE_TYPE(ET_GESTURE_END);
CASE_TYPE(ET_GESTURE_TWO_FINGER_TAP);
CASE_TYPE(ET_GESTURE_PINCH_BEGIN);
CASE_TYPE(ET_GESTURE_PINCH_END);
CASE_TYPE(ET_GESTURE_PINCH_UPDATE);
CASE_TYPE(ET_GESTURE_LONG_PRESS);
CASE_TYPE(ET_GESTURE_LONG_TAP);
CASE_TYPE(ET_GESTURE_SWIPE);
CASE_TYPE(ET_GESTURE_TAP_UNCONFIRMED);
CASE_TYPE(ET_GESTURE_DOUBLE_TAP);
CASE_TYPE(ET_SCROLL);
CASE_TYPE(ET_SCROLL_FLING_START);
CASE_TYPE(ET_SCROLL_FLING_CANCEL);
CASE_TYPE(ET_CANCEL_MODE);
CASE_TYPE(ET_UMA_DATA);
case ui::ET_LAST: NOTREACHED(); return std::string();
// Don't include default, so that we get an error when new type is added.
}
#undef CASE_TYPE
NOTREACHED();
return std::string();
}
bool IsX11SendEventTrue(const base::NativeEvent& event) {
#if defined(USE_X11)
return event && event->xany.send_event;
#else
return false;
#endif
}
bool X11EventHasNonStandardState(const base::NativeEvent& event) {
#if defined(USE_X11)
const unsigned int kAllStateMask =
Button1Mask | Button2Mask | Button3Mask | Button4Mask | Button5Mask |
Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask | ShiftMask |
LockMask | ControlMask | AnyModifier;
return event && (event->xkey.state & ~kAllStateMask) != 0;
#else
return false;
#endif
}
} // namespace
namespace ui {
////////////////////////////////////////////////////////////////////////////////
// Event
// static
std::unique_ptr<Event> Event::Clone(const Event& event) {
if (event.IsKeyEvent()) {
return base::WrapUnique(new KeyEvent(static_cast<const KeyEvent&>(event)));
}
if (event.IsMouseEvent()) {
if (event.IsMouseWheelEvent()) {
return base::WrapUnique(
new MouseWheelEvent(static_cast<const MouseWheelEvent&>(event)));
}
return base::WrapUnique(
new MouseEvent(static_cast<const MouseEvent&>(event)));
}
if (event.IsTouchEvent()) {
return base::WrapUnique(
new TouchEvent(static_cast<const TouchEvent&>(event)));
}
if (event.IsGestureEvent()) {
return base::WrapUnique(
new GestureEvent(static_cast<const GestureEvent&>(event)));
}
if (event.IsPointerEvent()) {
return base::WrapUnique(
new PointerEvent(static_cast<const PointerEvent&>(event)));
}
if (event.IsScrollEvent()) {
return base::WrapUnique(
new ScrollEvent(static_cast<const ScrollEvent&>(event)));
}
return base::WrapUnique(new Event(event));
}
Event::~Event() {
if (delete_native_event_)
ReleaseCopiedNativeEvent(native_event_);
}
bool Event::IsMousePointerEvent() const {
return IsPointerEvent() &&
AsPointerEvent()->pointer_details().pointer_type ==
EventPointerType::POINTER_TYPE_MOUSE;
}
bool Event::IsTouchPointerEvent() const {
return IsPointerEvent() &&
AsPointerEvent()->pointer_details().pointer_type ==
EventPointerType::POINTER_TYPE_TOUCH;
}
GestureEvent* Event::AsGestureEvent() {
CHECK(IsGestureEvent());
return static_cast<GestureEvent*>(this);
}
const GestureEvent* Event::AsGestureEvent() const {
CHECK(IsGestureEvent());
return static_cast<const GestureEvent*>(this);
}
KeyEvent* Event::AsKeyEvent() {
CHECK(IsKeyEvent());
return static_cast<KeyEvent*>(this);
}
const KeyEvent* Event::AsKeyEvent() const {
CHECK(IsKeyEvent());
return static_cast<const KeyEvent*>(this);
}
LocatedEvent* Event::AsLocatedEvent() {
CHECK(IsLocatedEvent());
return static_cast<LocatedEvent*>(this);
}
const LocatedEvent* Event::AsLocatedEvent() const {
CHECK(IsLocatedEvent());
return static_cast<const LocatedEvent*>(this);
}
MouseEvent* Event::AsMouseEvent() {
CHECK(IsMouseEvent());
return static_cast<MouseEvent*>(this);
}
const MouseEvent* Event::AsMouseEvent() const {
CHECK(IsMouseEvent());
return static_cast<const MouseEvent*>(this);
}
MouseWheelEvent* Event::AsMouseWheelEvent() {
CHECK(IsMouseWheelEvent());
return static_cast<MouseWheelEvent*>(this);
}
const MouseWheelEvent* Event::AsMouseWheelEvent() const {
CHECK(IsMouseWheelEvent());
return static_cast<const MouseWheelEvent*>(this);
}
PointerEvent* Event::AsPointerEvent() {
CHECK(IsPointerEvent());
return static_cast<PointerEvent*>(this);
}
const PointerEvent* Event::AsPointerEvent() const {
CHECK(IsPointerEvent());
return static_cast<const PointerEvent*>(this);
}
ScrollEvent* Event::AsScrollEvent() {
CHECK(IsScrollEvent());
return static_cast<ScrollEvent*>(this);
}
const ScrollEvent* Event::AsScrollEvent() const {
CHECK(IsScrollEvent());
return static_cast<const ScrollEvent*>(this);
}
TouchEvent* Event::AsTouchEvent() {
CHECK(IsTouchEvent());
return static_cast<TouchEvent*>(this);
}
const TouchEvent* Event::AsTouchEvent() const {
CHECK(IsTouchEvent());
return static_cast<const TouchEvent*>(this);
}
bool Event::HasNativeEvent() const {
base::NativeEvent null_event;
std::memset(&null_event, 0, sizeof(null_event));
return !!std::memcmp(&native_event_, &null_event, sizeof(null_event));
}
void Event::StopPropagation() {
// TODO(sad): Re-enable these checks once View uses dispatcher to dispatch
// events.
// CHECK(phase_ != EP_PREDISPATCH && phase_ != EP_POSTDISPATCH);
CHECK(cancelable_);
result_ = static_cast<EventResult>(result_ | ER_CONSUMED);
}
void Event::SetHandled() {
// TODO(sad): Re-enable these checks once View uses dispatcher to dispatch
// events.
// CHECK(phase_ != EP_PREDISPATCH && phase_ != EP_POSTDISPATCH);
CHECK(cancelable_);
result_ = static_cast<EventResult>(result_ | ER_HANDLED);
}
Event::Event(EventType type, base::TimeDelta time_stamp, int flags)
: type_(type),
time_stamp_(time_stamp),
flags_(flags),
native_event_(base::NativeEvent()),
delete_native_event_(false),
cancelable_(true),
target_(NULL),
phase_(EP_PREDISPATCH),
result_(ER_UNHANDLED),
source_device_id_(ED_UNKNOWN_DEVICE) {
if (type_ < ET_LAST)
name_ = EventTypeName(type_);
}
Event::Event(const base::NativeEvent& native_event,
EventType type,
int flags)
: type_(type),
time_stamp_(EventTimeFromNative(native_event)),
flags_(flags),
native_event_(native_event),
delete_native_event_(false),
cancelable_(true),
target_(NULL),
phase_(EP_PREDISPATCH),
result_(ER_UNHANDLED),
source_device_id_(ED_UNKNOWN_DEVICE) {
base::TimeDelta delta = EventTimeForNow() - time_stamp_;
if (type_ < ET_LAST)
name_ = EventTypeName(type_);
base::HistogramBase::Sample delta_sample =
static_cast<base::HistogramBase::Sample>(delta.InMicroseconds());
UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.Browser", delta_sample, 1, 1000000,
100);
// Though it seems inefficient to generate the string twice, the first
// instance will be used only for DCHECK builds and the second won't be
// executed at all if the histogram was previously accessed here.
STATIC_HISTOGRAM_POINTER_GROUP(
base::StringPrintf("Event.Latency.Browser.%s", name_.c_str()),
type_, ET_LAST, Add(delta_sample),
base::Histogram::FactoryGet(
base::StringPrintf("Event.Latency.Browser.%s", name_.c_str()),
1, 1000000, 100,
base::HistogramBase::kUmaTargetedHistogramFlag));
#if defined(USE_X11)
if (native_event->type == GenericEvent) {
XIDeviceEvent* xiev =
static_cast<XIDeviceEvent*>(native_event->xcookie.data);
source_device_id_ = xiev->sourceid;
}
#endif
#if defined(USE_OZONE)
source_device_id_ =
static_cast<const Event*>(native_event)->source_device_id();
#endif
}
Event::Event(const Event& copy)
: type_(copy.type_),
time_stamp_(copy.time_stamp_),
latency_(copy.latency_),
flags_(copy.flags_),
native_event_(CopyNativeEvent(copy.native_event_)),
delete_native_event_(true),
cancelable_(true),
target_(NULL),
phase_(EP_PREDISPATCH),
result_(ER_UNHANDLED),
source_device_id_(copy.source_device_id_) {
if (type_ < ET_LAST)
name_ = EventTypeName(type_);
}
void Event::SetType(EventType type) {
if (type_ < ET_LAST)
name_ = std::string();
type_ = type;
if (type_ < ET_LAST)
name_ = EventTypeName(type_);
}
////////////////////////////////////////////////////////////////////////////////
// CancelModeEvent
CancelModeEvent::CancelModeEvent()
: Event(ET_CANCEL_MODE, base::TimeDelta(), 0) {
set_cancelable(false);
}
CancelModeEvent::~CancelModeEvent() {
}
////////////////////////////////////////////////////////////////////////////////
// LocatedEvent
LocatedEvent::~LocatedEvent() {
}
LocatedEvent::LocatedEvent(const base::NativeEvent& native_event)
: Event(native_event,
EventTypeFromNative(native_event),
EventFlagsFromNative(native_event)),
location_(EventLocationFromNative(native_event)),
root_location_(location_) {
}
LocatedEvent::LocatedEvent(EventType type,
const gfx::PointF& location,
const gfx::PointF& root_location,
base::TimeDelta time_stamp,
int flags)
: Event(type, time_stamp, flags),
location_(location),
root_location_(root_location) {
}
void LocatedEvent::UpdateForRootTransform(
const gfx::Transform& reversed_root_transform) {
// Transform has to be done at root level.
gfx::Point3F p(location_);
reversed_root_transform.TransformPoint(&p);
location_ = p.AsPointF();
root_location_ = location_;
}
////////////////////////////////////////////////////////////////////////////////
// MouseEvent
MouseEvent::MouseEvent(const base::NativeEvent& native_event)
: LocatedEvent(native_event),
changed_button_flags_(GetChangedMouseButtonFlagsFromNative(native_event)),
pointer_details_(GetMousePointerDetailsFromNative(native_event)) {
if (type() == ET_MOUSE_PRESSED || type() == ET_MOUSE_RELEASED)
SetClickCount(GetRepeatCount(*this));
}
MouseEvent::MouseEvent(EventType type,
const gfx::Point& location,
const gfx::Point& root_location,
base::TimeDelta time_stamp,
int flags,
int changed_button_flags)
: LocatedEvent(type,
gfx::PointF(location),
gfx::PointF(root_location),
time_stamp,
flags),
changed_button_flags_(changed_button_flags),
pointer_details_(PointerDetails(EventPointerType::POINTER_TYPE_MOUSE)) {
if (this->type() == ET_MOUSE_MOVED && IsAnyButton())
SetType(ET_MOUSE_DRAGGED);
}
// static
bool MouseEvent::IsRepeatedClickEvent(
const MouseEvent& event1,
const MouseEvent& event2) {
// These values match the Windows defaults.
static const int kDoubleClickTimeMS = 500;
static const int kDoubleClickWidth = 4;
static const int kDoubleClickHeight = 4;
if (event1.type() != ET_MOUSE_PRESSED ||
event2.type() != ET_MOUSE_PRESSED)
return false;
// Compare flags, but ignore EF_IS_DOUBLE_CLICK to allow triple clicks.
if ((event1.flags() & ~EF_IS_DOUBLE_CLICK) !=
(event2.flags() & ~EF_IS_DOUBLE_CLICK))
return false;
// The new event has been created from the same native event.
if (event1.time_stamp() == event2.time_stamp())
return false;
base::TimeDelta time_difference = event2.time_stamp() - event1.time_stamp();
if (time_difference.InMilliseconds() > kDoubleClickTimeMS)
return false;
if (std::abs(event2.x() - event1.x()) > kDoubleClickWidth / 2)
return false;
if (std::abs(event2.y() - event1.y()) > kDoubleClickHeight / 2)
return false;
return true;
}
// static
int MouseEvent::GetRepeatCount(const MouseEvent& event) {
int click_count = 1;
if (last_click_event_) {
if (event.type() == ui::ET_MOUSE_RELEASED) {
if (event.changed_button_flags() ==
last_click_event_->changed_button_flags()) {
last_click_complete_ = true;
return last_click_event_->GetClickCount();
} else {
// If last_click_event_ has changed since this button was pressed
// return a click count of 1.
return click_count;
}
}
if (event.time_stamp() != last_click_event_->time_stamp())
last_click_complete_ = true;
if (!last_click_complete_ ||
IsX11SendEventTrue(event.native_event())) {
click_count = last_click_event_->GetClickCount();
} else if (IsRepeatedClickEvent(*last_click_event_, event)) {
click_count = last_click_event_->GetClickCount() + 1;
}
delete last_click_event_;
}
last_click_event_ = new MouseEvent(event);
last_click_complete_ = false;
if (click_count > 3)
click_count = 3;
last_click_event_->SetClickCount(click_count);
return click_count;
}
void MouseEvent::ResetLastClickForTest() {
if (last_click_event_) {
delete last_click_event_;
last_click_event_ = NULL;
last_click_complete_ = false;
}
}
// static
MouseEvent* MouseEvent::last_click_event_ = NULL;
bool MouseEvent::last_click_complete_ = false;
int MouseEvent::GetClickCount() const {
if (type() != ET_MOUSE_PRESSED && type() != ET_MOUSE_RELEASED)
return 0;
if (flags() & EF_IS_TRIPLE_CLICK)
return 3;
else if (flags() & EF_IS_DOUBLE_CLICK)
return 2;
else
return 1;
}
void MouseEvent::SetClickCount(int click_count) {
if (type() != ET_MOUSE_PRESSED && type() != ET_MOUSE_RELEASED)
return;
DCHECK(click_count > 0);
DCHECK(click_count <= 3);
int f = flags();
switch (click_count) {
case 1:
f &= ~EF_IS_DOUBLE_CLICK;
f &= ~EF_IS_TRIPLE_CLICK;
break;
case 2:
f |= EF_IS_DOUBLE_CLICK;
f &= ~EF_IS_TRIPLE_CLICK;
break;
case 3:
f &= ~EF_IS_DOUBLE_CLICK;
f |= EF_IS_TRIPLE_CLICK;
break;
}
set_flags(f);
}
////////////////////////////////////////////////////////////////////////////////
// MouseWheelEvent
MouseWheelEvent::MouseWheelEvent(const base::NativeEvent& native_event)
: MouseEvent(native_event),
offset_(GetMouseWheelOffset(native_event)) {
}
MouseWheelEvent::MouseWheelEvent(const ScrollEvent& scroll_event)
: MouseEvent(scroll_event),
offset_(gfx::ToRoundedInt(scroll_event.x_offset()),
gfx::ToRoundedInt(scroll_event.y_offset())) {
SetType(ET_MOUSEWHEEL);
}
MouseWheelEvent::MouseWheelEvent(const MouseEvent& mouse_event,
int x_offset,
int y_offset)
: MouseEvent(mouse_event), offset_(x_offset, y_offset) {
DCHECK(type() == ET_MOUSEWHEEL);
}
MouseWheelEvent::MouseWheelEvent(const MouseWheelEvent& mouse_wheel_event)
: MouseEvent(mouse_wheel_event),
offset_(mouse_wheel_event.offset()) {
DCHECK(type() == ET_MOUSEWHEEL);
}
MouseWheelEvent::MouseWheelEvent(const gfx::Vector2d& offset,
const gfx::Point& location,
const gfx::Point& root_location,
base::TimeDelta time_stamp,
int flags,
int changed_button_flags)
: MouseEvent(ui::ET_MOUSEWHEEL,
location,
root_location,
time_stamp,
flags,
changed_button_flags),
offset_(offset) {}
#if defined(OS_WIN)
// This value matches windows WHEEL_DELTA.
// static
const int MouseWheelEvent::kWheelDelta = 120;
#else
// This value matches GTK+ wheel scroll amount.
const int MouseWheelEvent::kWheelDelta = 53;
#endif
////////////////////////////////////////////////////////////////////////////////
// TouchEvent
TouchEvent::TouchEvent(const base::NativeEvent& native_event)
: LocatedEvent(native_event),
touch_id_(GetTouchId(native_event)),
unique_event_id_(ui::GetNextTouchEventId()),
rotation_angle_(GetTouchAngle(native_event)),
may_cause_scrolling_(false),
should_remove_native_touch_id_mapping_(false),
pointer_details_(GetTouchPointerDetailsFromNative(native_event)) {
latency()->AddLatencyNumberWithTimestamp(
INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, 0, 0,
base::TimeTicks::FromInternalValue(time_stamp().ToInternalValue()), 1);
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0);
FixRotationAngle();
if (type() == ET_TOUCH_RELEASED || type() == ET_TOUCH_CANCELLED)
should_remove_native_touch_id_mapping_ = true;
}
TouchEvent::TouchEvent(EventType type,
const gfx::Point& location,
int touch_id,
base::TimeDelta time_stamp)
: LocatedEvent(type,
gfx::PointF(location),
gfx::PointF(location),
time_stamp,
0),
touch_id_(touch_id),
unique_event_id_(ui::GetNextTouchEventId()),
rotation_angle_(0.0f),
may_cause_scrolling_(false),
should_remove_native_touch_id_mapping_(false),
pointer_details_(PointerDetails(EventPointerType::POINTER_TYPE_TOUCH)) {
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0);
}
TouchEvent::TouchEvent(EventType type,
const gfx::Point& location,
int flags,
int touch_id,
base::TimeDelta time_stamp,
float radius_x,
float radius_y,
float angle,
float force)
: LocatedEvent(type,
gfx::PointF(location),
gfx::PointF(location),
time_stamp,
flags),
touch_id_(touch_id),
unique_event_id_(ui::GetNextTouchEventId()),
rotation_angle_(angle),
may_cause_scrolling_(false),
should_remove_native_touch_id_mapping_(false),
pointer_details_(PointerDetails(EventPointerType::POINTER_TYPE_TOUCH,
radius_x,
radius_y,
force,
/* tilt_x */ 0.0f,
/* tilt_y */ 0.0f)) {
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0);
FixRotationAngle();
}
TouchEvent::TouchEvent(const TouchEvent& copy)
: LocatedEvent(copy),
touch_id_(copy.touch_id_),
unique_event_id_(copy.unique_event_id_),
rotation_angle_(copy.rotation_angle_),
may_cause_scrolling_(copy.may_cause_scrolling_),
should_remove_native_touch_id_mapping_(false),
pointer_details_(copy.pointer_details_) {
// Copied events should not remove touch id mapping, as this either causes the
// mapping to be lost before the initial event has finished dispatching, or
// the copy to attempt to remove the mapping from a null |native_event_|.
}
TouchEvent::~TouchEvent() {
// In ctor TouchEvent(native_event) we call GetTouchId() which in X11
// platform setups the tracking_id to slot mapping. So in dtor here,
// if this touch event is a release event, we clear the mapping accordingly.
if (should_remove_native_touch_id_mapping_) {
DCHECK(type() == ET_TOUCH_RELEASED || type() == ET_TOUCH_CANCELLED);
if (type() == ET_TOUCH_RELEASED || type() == ET_TOUCH_CANCELLED)
ClearTouchIdIfReleased(native_event());
}
}
void TouchEvent::UpdateForRootTransform(
const gfx::Transform& inverted_root_transform) {
LocatedEvent::UpdateForRootTransform(inverted_root_transform);
gfx::DecomposedTransform decomp;
bool success = gfx::DecomposeTransform(&decomp, inverted_root_transform);
DCHECK(success);
if (decomp.scale[0])
pointer_details_.radius_x *= decomp.scale[0];
if (decomp.scale[1])
pointer_details_.radius_y *= decomp.scale[1];
}
void TouchEvent::DisableSynchronousHandling() {
DispatcherApi dispatcher_api(this);
dispatcher_api.set_result(
static_cast<EventResult>(result() | ER_DISABLE_SYNC_HANDLING));
}
void TouchEvent::FixRotationAngle() {
while (rotation_angle_ < 0)
rotation_angle_ += 180;
while (rotation_angle_ >= 180)
rotation_angle_ -= 180;
}
////////////////////////////////////////////////////////////////////////////////
// PointerEvent
bool PointerEvent::CanConvertFrom(const Event& event) {
switch (event.type()) {
case ET_MOUSE_PRESSED:
case ET_MOUSE_DRAGGED:
case ET_MOUSE_MOVED:
case ET_MOUSE_ENTERED:
case ET_MOUSE_EXITED:
case ET_MOUSE_RELEASED:
case ET_TOUCH_PRESSED:
case ET_TOUCH_MOVED:
case ET_TOUCH_RELEASED:
case ET_TOUCH_CANCELLED:
return true;
default:
return false;
}
}
PointerEvent::PointerEvent(const PointerEvent& pointer_event)
: LocatedEvent(pointer_event),
pointer_id_(pointer_event.pointer_id()),
details_(pointer_event.pointer_details()) {}
PointerEvent::PointerEvent(const MouseEvent& mouse_event)
: LocatedEvent(mouse_event),
pointer_id_(kMousePointerId),
details_(mouse_event.pointer_details()) {
DCHECK(CanConvertFrom(mouse_event));
switch (mouse_event.type()) {
case ET_MOUSE_PRESSED:
SetType(ET_POINTER_DOWN);
break;
case ET_MOUSE_DRAGGED:
case ET_MOUSE_MOVED:
SetType(ET_POINTER_MOVED);
break;
case ET_MOUSE_ENTERED:
SetType(ET_POINTER_ENTERED);
break;
case ET_MOUSE_EXITED:
SetType(ET_POINTER_EXITED);
break;
case ET_MOUSE_RELEASED:
SetType(ET_POINTER_UP);
break;
default:
NOTREACHED();
}
}
PointerEvent::PointerEvent(const TouchEvent& touch_event)
: LocatedEvent(touch_event),
pointer_id_(touch_event.touch_id()),
details_(touch_event.pointer_details()) {
DCHECK(CanConvertFrom(touch_event));
switch (touch_event.type()) {
case ET_TOUCH_PRESSED:
SetType(ET_POINTER_DOWN);
break;
case ET_TOUCH_MOVED:
SetType(ET_POINTER_MOVED);
break;
case ET_TOUCH_RELEASED:
SetType(ET_POINTER_UP);
break;
case ET_TOUCH_CANCELLED:
SetType(ET_POINTER_CANCELLED);
break;
default:
NOTREACHED();
}
}
PointerEvent::PointerEvent(EventType type,
EventPointerType pointer_type,
const gfx::Point& location,
const gfx::Point& root_location,
int flags,
int pointer_id,
base::TimeDelta time_stamp)
: LocatedEvent(type,
gfx::PointF(location),
gfx::PointF(root_location),
time_stamp,
flags),
pointer_id_(pointer_id),
details_(PointerDetails(pointer_type)) {}
const int PointerEvent::kMousePointerId = std::numeric_limits<int32_t>::max();
////////////////////////////////////////////////////////////////////////////////
// KeyEvent
// static
KeyEvent* KeyEvent::last_key_event_ = NULL;
// static
bool KeyEvent::IsRepeated(const KeyEvent& event) {
// A safe guard in case if there were continous key pressed events that are
// not auto repeat.
const int kMaxAutoRepeatTimeMs = 2000;
// Ignore key events that have non standard state masks as it may be
// reposted by an IME. IBUS-GTK uses this field to detect the
// re-posted event for example. crbug.com/385873.
if (X11EventHasNonStandardState(event.native_event()))
return false;
if (event.is_char())
return false;
if (event.type() == ui::ET_KEY_RELEASED) {
delete last_key_event_;
last_key_event_ = NULL;
return false;
}
CHECK_EQ(ui::ET_KEY_PRESSED, event.type());
if (!last_key_event_) {
last_key_event_ = new KeyEvent(event);
return false;
} else if (event.time_stamp() == last_key_event_->time_stamp()) {
// The KeyEvent is created from the same native event.
return (last_key_event_->flags() & ui::EF_IS_REPEAT) != 0;
}
if (event.key_code() == last_key_event_->key_code() &&
event.flags() == (last_key_event_->flags() & ~ui::EF_IS_REPEAT) &&
(event.time_stamp() - last_key_event_->time_stamp()).InMilliseconds() <
kMaxAutoRepeatTimeMs) {
last_key_event_->set_time_stamp(event.time_stamp());
last_key_event_->set_flags(last_key_event_->flags() | ui::EF_IS_REPEAT);
return true;
}
delete last_key_event_;
last_key_event_ = new KeyEvent(event);
return false;
}
KeyEvent::KeyEvent(EventType type, base::TimeDelta time_stamp, int flags)
: Event(type, time_stamp, flags) {}
KeyEvent::KeyEvent(const base::NativeEvent& native_event)
: Event(native_event,
EventTypeFromNative(native_event),
EventFlagsFromNative(native_event)),
key_code_(KeyboardCodeFromNative(native_event)),
code_(CodeFromNative(native_event)),
is_char_(IsCharFromNative(native_event)) {
if (IsRepeated(*this))
set_flags(flags() | ui::EF_IS_REPEAT);
#if defined(USE_X11)
NormalizeFlags();
#endif
#if defined(OS_WIN)
// Only Windows has native character events.
if (is_char_)
key_ = DomKey::FromCharacter(native_event.wParam);
else
key_ = PlatformKeyMap::DomKeyFromNative(native_event);
#endif
}
KeyEvent::KeyEvent(EventType type,
KeyboardCode key_code,
int flags)
: Event(type, EventTimeForNow(), flags),
key_code_(key_code),
code_(UsLayoutKeyboardCodeToDomCode(key_code)) {
}
KeyEvent::KeyEvent(EventType type,
KeyboardCode key_code,
DomCode code,
int flags)
: Event(type, EventTimeForNow(), flags),
key_code_(key_code),
code_(code) {
}
KeyEvent::KeyEvent(EventType type,
KeyboardCode key_code,
DomCode code,
int flags,
DomKey key,
base::TimeDelta time_stamp)
: Event(type, time_stamp, flags),
key_code_(key_code),
code_(code),
key_(key) {
}
KeyEvent::KeyEvent(base::char16 character, KeyboardCode key_code, int flags)
: Event(ET_KEY_PRESSED, EventTimeForNow(), flags),
key_code_(key_code),
code_(DomCode::NONE),
is_char_(true),
key_(DomKey::FromCharacter(character)) {
}
KeyEvent::KeyEvent(const KeyEvent& rhs)
: Event(rhs),
key_code_(rhs.key_code_),
code_(rhs.code_),
is_char_(rhs.is_char_),
key_(rhs.key_) {
if (rhs.extended_key_event_data_)
extended_key_event_data_.reset(rhs.extended_key_event_data_->Clone());
}
KeyEvent& KeyEvent::operator=(const KeyEvent& rhs) {
if (this != &rhs) {
Event::operator=(rhs);
key_code_ = rhs.key_code_;
code_ = rhs.code_;
key_ = rhs.key_;
is_char_ = rhs.is_char_;
if (rhs.extended_key_event_data_)
extended_key_event_data_.reset(rhs.extended_key_event_data_->Clone());
}
return *this;
}
KeyEvent::~KeyEvent() {}
void KeyEvent::SetExtendedKeyEventData(
std::unique_ptr<ExtendedKeyEventData> data) {
extended_key_event_data_ = std::move(data);
}
void KeyEvent::ApplyLayout() const {
ui::DomCode code = code_;
if (code == DomCode::NONE) {
// Catch old code that tries to do layout without a physical key, and try
// to recover using the KeyboardCode. Once key events are fully defined
// on construction (see TODO in event.h) this will go away.
VLOG(2) << "DomCode::NONE keycode=" << key_code_;
code = UsLayoutKeyboardCodeToDomCode(key_code_);
if (code == DomCode::NONE) {
key_ = DomKey::UNIDENTIFIED;
return;
}
}
KeyboardCode dummy_key_code;
#if defined(OS_WIN)
// Native Windows character events always have is_char_ == true,
// so this is a synthetic or native keystroke event.
// Therefore, perform only the fallback action.
#elif defined(USE_X11)
// When a control key is held, prefer ASCII characters to non ASCII
// characters in order to use it for shortcut keys. GetCharacterFromKeyCode
// returns 'a' for VKEY_A even if the key is actually bound to 'à' in X11.
// GetCharacterFromXEvent returns 'à' in that case.
if (!IsControlDown() && native_event()) {
key_ = GetDomKeyFromXEvent(native_event());
return;
}
#elif defined(USE_OZONE)
if (KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup(
code, flags(), &key_, &dummy_key_code)) {
return;
}
#else
if (native_event()) {
DCHECK(EventTypeFromNative(native_event()) == ET_KEY_PRESSED ||
EventTypeFromNative(native_event()) == ET_KEY_RELEASED);
}
#endif
if (!DomCodeToUsLayoutDomKey(code, flags(), &key_, &dummy_key_code))
key_ = DomKey::UNIDENTIFIED;
}
DomKey KeyEvent::GetDomKey() const {
// Determination of key_ may be done lazily.
if (key_ == DomKey::NONE)
ApplyLayout();
return key_;
}
base::char16 KeyEvent::GetCharacter() const {
// Determination of key_ may be done lazily.
if (key_ == DomKey::NONE)
ApplyLayout();
if (key_.IsCharacter()) {
// Historically ui::KeyEvent has held only BMP characters.
// Until this explicitly changes, require |key_| to hold a BMP character.
DomKey::Base utf32_character = key_.ToCharacter();
base::char16 ucs2_character = static_cast<base::char16>(utf32_character);
DCHECK(static_cast<DomKey::Base>(ucs2_character) == utf32_character);
// Check if the control character is down. Note that ALTGR is represented
// on Windows as CTRL|ALT, so we need to make sure that is not set.
if ((flags() & (EF_ALTGR_DOWN | EF_CONTROL_DOWN)) == EF_CONTROL_DOWN) {
// For a control character, key_ contains the corresponding printable
// character. To preserve existing behaviour for now, return the control
// character here; this will likely change -- see e.g. crbug.com/471488.
if (ucs2_character >= 0x20 && ucs2_character <= 0x7E)
return ucs2_character & 0x1F;
if (ucs2_character == '\r')
return '\n';
}
return ucs2_character;
}
return 0;
}
base::char16 KeyEvent::GetText() const {
if ((flags() & EF_CONTROL_DOWN) != 0) {
ui::DomKey key;
ui::KeyboardCode key_code;
if (DomCodeToControlCharacter(code_, flags(), &key, &key_code))
return key.ToCharacter();
}
return GetUnmodifiedText();
}
base::char16 KeyEvent::GetUnmodifiedText() const {
if (!is_char_ && (key_code_ == VKEY_RETURN))
return '\r';
return GetCharacter();
}
bool KeyEvent::IsUnicodeKeyCode() const {
#if defined(OS_WIN)
if (!IsAltDown())
return false;
const int key = key_code();
if (key >= VKEY_NUMPAD0 && key <= VKEY_NUMPAD9)
return true;
// Check whether the user is using the numeric keypad with num-lock off.
// In that case, EF_EXTENDED will not be set; if it is set, the key event
// originated from the relevant non-numpad dedicated key, e.g. [Insert].
return (!(flags() & EF_IS_EXTENDED_KEY) &&
(key == VKEY_INSERT || key == VKEY_END || key == VKEY_DOWN ||
key == VKEY_NEXT || key == VKEY_LEFT || key == VKEY_CLEAR ||
key == VKEY_RIGHT || key == VKEY_HOME || key == VKEY_UP ||
key == VKEY_PRIOR));
#else
return false;
#endif
}
void KeyEvent::NormalizeFlags() {
int mask = 0;
switch (key_code()) {
case VKEY_CONTROL:
mask = EF_CONTROL_DOWN;
break;
case VKEY_SHIFT:
mask = EF_SHIFT_DOWN;
break;
case VKEY_MENU:
mask = EF_ALT_DOWN;
break;
default:
return;
}
if (type() == ET_KEY_PRESSED)
set_flags(flags() | mask);
else
set_flags(flags() & ~mask);
}
KeyboardCode KeyEvent::GetLocatedWindowsKeyboardCode() const {
return NonLocatedToLocatedKeyboardCode(key_code_, code_);
}
uint16_t KeyEvent::GetConflatedWindowsKeyCode() const {
if (is_char_)
return key_.ToCharacter();
return key_code_;
}
std::string KeyEvent::GetCodeString() const {
return KeycodeConverter::DomCodeToCodeString(code_);
}
////////////////////////////////////////////////////////////////////////////////
// ScrollEvent
ScrollEvent::ScrollEvent(const base::NativeEvent& native_event)
: MouseEvent(native_event),
x_offset_(0.0f),
y_offset_(0.0f),
x_offset_ordinal_(0.0f),
y_offset_ordinal_(0.0f),
finger_count_(0) {
if (type() == ET_SCROLL) {
GetScrollOffsets(native_event,
&x_offset_, &y_offset_,
&x_offset_ordinal_, &y_offset_ordinal_,
&finger_count_);
} else if (type() == ET_SCROLL_FLING_START ||
type() == ET_SCROLL_FLING_CANCEL) {
GetFlingData(native_event,
&x_offset_, &y_offset_,
&x_offset_ordinal_, &y_offset_ordinal_,
NULL);
} else {
NOTREACHED() << "Unexpected event type " << type()
<< " when constructing a ScrollEvent.";
}
}
ScrollEvent::ScrollEvent(EventType type,
const gfx::Point& location,
base::TimeDelta time_stamp,
int flags,
float x_offset,
float y_offset,
float x_offset_ordinal,
float y_offset_ordinal,
int finger_count)
: MouseEvent(type, location, location, time_stamp, flags, 0),
x_offset_(x_offset),
y_offset_(y_offset),
x_offset_ordinal_(x_offset_ordinal),
y_offset_ordinal_(y_offset_ordinal),
finger_count_(finger_count) {
CHECK(IsScrollEvent());
}
void ScrollEvent::Scale(const float factor) {
x_offset_ *= factor;
y_offset_ *= factor;
x_offset_ordinal_ *= factor;
y_offset_ordinal_ *= factor;
}
////////////////////////////////////////////////////////////////////////////////
// GestureEvent
GestureEvent::GestureEvent(float x,
float y,
int flags,
base::TimeDelta time_stamp,
const GestureEventDetails& details)
: LocatedEvent(details.type(),
gfx::PointF(x, y),
gfx::PointF(x, y),
time_stamp,
flags | EF_FROM_TOUCH),
details_(details) {
}
GestureEvent::~GestureEvent() {
}
} // namespace ui