blob: ebecbfe443b4de08793a46e5ff430a37cc79ce06 [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"
#if defined(USE_X11)
#include <X11/extensions/XInput2.h>
#include <X11/Xlib.h>
#include <X11/keysym.h>
#endif
#include <cmath>
#include <cstring>
#include "base/strings/stringprintf.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/keyboard_code_conversion.h"
#include "ui/gfx/geometry/safe_integer_conversions.h"
#include "ui/gfx/point3_f.h"
#include "ui/gfx/point_conversions.h"
#include "ui/gfx/transform.h"
#include "ui/gfx/transform_util.h"
namespace ui {
////////////////////////////////////////////////////////////////////////////////
// Event
// static
scoped_ptr<Event> Event::Clone(const Event& event) {
if (event.IsKeyEvent()) {
return scoped_ptr<Event>(new KeyEvent(static_cast<const KeyEvent&>(event)));
}
if (event.IsMouseEvent()) {
if (event.IsMouseWheelEvent()) {
return scoped_ptr<Event>(
new MouseWheelEvent(static_cast<const MouseWheelEvent&>(event)));
}
return scoped_ptr<Event>(
new MouseEvent(static_cast<const MouseEvent&>(event)));
}
if (event.IsTouchEvent()) {
return scoped_ptr<Event>(
new TouchEvent(static_cast<const TouchEvent&>(event)));
}
if (event.IsGestureEvent()) {
return scoped_ptr<Event>(
new GestureEvent(static_cast<const GestureEvent&>(event)));
}
if (event.IsScrollEvent()) {
return scoped_ptr<Event>(
new ScrollEvent(static_cast<const ScrollEvent&>(event)));
}
return scoped_ptr<Event>(new Event(event));
}
Event::~Event() {
}
GestureEvent* Event::AsGestureEvent() {
CHECK(IsGestureEvent());
return static_cast<GestureEvent*>(this);
}
const GestureEvent* Event::AsGestureEvent() const {
CHECK(IsGestureEvent());
return static_cast<const GestureEvent*>(this);
}
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()
: type_(ET_UNKNOWN),
time_stamp_(base::TimeDelta()),
flags_(EF_NONE),
cancelable_(true),
target_(NULL),
phase_(EP_PREDISPATCH),
result_(ER_UNHANDLED),
source_device_id_(ED_UNKNOWN_DEVICE) {
}
Event::Event(EventType type, base::TimeDelta time_stamp, int flags)
: type_(type),
time_stamp_(time_stamp),
flags_(flags),
cancelable_(true),
target_(NULL),
phase_(EP_PREDISPATCH),
result_(ER_UNHANDLED),
source_device_id_(ED_UNKNOWN_DEVICE) {
}
Event::Event(const Event& copy)
: type_(copy.type_),
time_stamp_(copy.time_stamp_),
latency_(copy.latency_),
flags_(copy.flags_),
cancelable_(true),
target_(NULL),
phase_(EP_PREDISPATCH),
result_(ER_UNHANDLED),
source_device_id_(copy.source_device_id_) {
}
void Event::SetType(EventType type) {
type_ = type;
}
////////////////////////////////////////////////////////////////////////////////
// CancelModeEvent
CancelModeEvent::CancelModeEvent()
: Event(ET_CANCEL_MODE, base::TimeDelta(), 0) {
set_cancelable(false);
}
CancelModeEvent::~CancelModeEvent() {
}
////////////////////////////////////////////////////////////////////////////////
// LocatedEvent
LocatedEvent::LocatedEvent() : Event() {
}
LocatedEvent::~LocatedEvent() {
}
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() : LocatedEvent(), changed_button_flags_(0) {
}
MouseEvent::MouseEvent(EventType type,
const gfx::PointF& location,
const gfx::PointF& root_location,
int flags,
int changed_button_flags)
: LocatedEvent(type, location, root_location, EventTimeForNow(), flags),
changed_button_flags_(changed_button_flags) {
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;
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;
}
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() : MouseEvent(), offset_() {
}
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::PointF& location,
const gfx::PointF& root_location,
int flags,
int changed_button_flags)
: MouseEvent(ui::ET_MOUSEWHEEL, location, root_location, flags,
changed_button_flags),
offset_(offset) {
}
// This value matches GTK+ wheel scroll amount.
const int MouseWheelEvent::kWheelDelta = 53;
void MouseWheelEvent::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]) {
offset_.set_x(
gfx::ToRoundedInt(SkMScalarToFloat(offset_.x() * decomp.scale[0])));
}
if (decomp.scale[1]) {
offset_.set_y(
gfx::ToRoundedInt(SkMScalarToFloat(offset_.y() * decomp.scale[1])));
}
}
////////////////////////////////////////////////////////////////////////////////
// TouchEvent
TouchEvent::TouchEvent()
: LocatedEvent(),
touch_id_(0),
radius_x_(0),
radius_y_(0),
rotation_angle_(0),
force_(0) {
}
TouchEvent::TouchEvent(EventType type,
const gfx::PointF& location,
int touch_id,
base::TimeDelta time_stamp)
: LocatedEvent(type, location, location, time_stamp, 0),
touch_id_(touch_id),
radius_x_(0.0f),
radius_y_(0.0f),
rotation_angle_(0.0f),
force_(0.0f) {
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0);
}
TouchEvent::TouchEvent(EventType type,
const gfx::PointF& location,
int flags,
int touch_id,
base::TimeDelta time_stamp,
float radius_x,
float radius_y,
float angle,
float force)
: LocatedEvent(type, location, location, time_stamp, flags),
touch_id_(touch_id),
radius_x_(radius_x),
radius_y_(radius_y),
rotation_angle_(angle),
force_(force) {
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0);
}
TouchEvent::~TouchEvent() {
}
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])
radius_x_ *= decomp.scale[0];
if (decomp.scale[1])
radius_y_ *= decomp.scale[1];
}
////////////////////////////////////////////////////////////////////////////////
// KeyEvent
KeyEvent::KeyEvent()
: Event(),
key_code_(VKEY_UNKNOWN),
code_(),
is_char_(false),
platform_keycode_(0),
character_(0) {
}
KeyEvent::KeyEvent(EventType type,
KeyboardCode key_code,
int flags)
: Event(type, EventTimeForNow(), flags),
key_code_(key_code),
is_char_(false),
platform_keycode_(0),
character_() {
}
KeyEvent::KeyEvent(EventType type,
KeyboardCode key_code,
const std::string& code,
int flags)
: Event(type, EventTimeForNow(), flags),
key_code_(key_code),
code_(code),
is_char_(false),
platform_keycode_(0),
character_(0) {
}
KeyEvent::KeyEvent(base::char16 character, KeyboardCode key_code, int flags)
: Event(ET_KEY_PRESSED, EventTimeForNow(), flags),
key_code_(key_code),
code_(""),
is_char_(true),
platform_keycode_(0),
character_(character) {
}
KeyEvent::KeyEvent(const KeyEvent& rhs)
: Event(rhs),
key_code_(rhs.key_code_),
code_(rhs.code_),
is_char_(rhs.is_char_),
platform_keycode_(rhs.platform_keycode_),
character_(rhs.character_) {
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_;
is_char_ = rhs.is_char_;
platform_keycode_ = rhs.platform_keycode_;
character_ = rhs.character_;
if (rhs.extended_key_event_data_)
extended_key_event_data_.reset(rhs.extended_key_event_data_->Clone());
}
return *this;
}
KeyEvent::~KeyEvent() {}
void KeyEvent::SetExtendedKeyEventData(scoped_ptr<ExtendedKeyEventData> data) {
extended_key_event_data_ = data.Pass();
}
base::char16 KeyEvent::GetCharacter() const {
if (is_char_ || character_)
return character_;
// TODO(kpschoedel): streamline these cases after settling Ozone
// positional coding.
#if defined(USE_X11)
character_ = GetCharacterFromKeyCode(key_code_, flags());
return character_;
#else
return GetCharacterFromKeyCode(key_code_, flags());
#endif
}
base::char16 KeyEvent::GetText() const {
if ((flags() & EF_CONTROL_DOWN) != 0) {
return GetControlCharacterForKeycode(key_code_,
(flags() & EF_SHIFT_DOWN) != 0);
}
return GetUnmodifiedText();
}
base::char16 KeyEvent::GetUnmodifiedText() const {
if (!is_char_ && (key_code_ == VKEY_RETURN))
return '\r';
return GetCharacter();
}
bool KeyEvent::IsUnicodeKeyCode() const {
return false;
}
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;
case VKEY_CAPITAL:
mask = EF_CAPS_LOCK_DOWN;
break;
default:
return;
}
if (type() == ET_KEY_PRESSED)
set_flags(flags() | mask);
else
set_flags(flags() & ~mask);
}
bool KeyEvent::IsTranslated() const {
switch (type()) {
case ET_KEY_PRESSED:
case ET_KEY_RELEASED:
return false;
case ET_TRANSLATED_KEY_PRESS:
case ET_TRANSLATED_KEY_RELEASE:
return true;
default:
NOTREACHED();
return false;
}
}
void KeyEvent::SetTranslated(bool translated) {
switch (type()) {
case ET_KEY_PRESSED:
case ET_TRANSLATED_KEY_PRESS:
SetType(translated ? ET_TRANSLATED_KEY_PRESS : ET_KEY_PRESSED);
break;
case ET_KEY_RELEASED:
case ET_TRANSLATED_KEY_RELEASE:
SetType(translated ? ET_TRANSLATED_KEY_RELEASE : ET_KEY_RELEASED);
break;
default:
NOTREACHED();
}
}
bool KeyEvent::IsRightSideKey() const {
switch (key_code_) {
case VKEY_CONTROL:
case VKEY_SHIFT:
case VKEY_MENU:
case VKEY_LWIN:
#if defined(USE_X11)
// Under X11, setting code_ requires platform-dependent information, and
// currently assumes that X keycodes are based on Linux evdev keycodes.
// In certain test environments this is not the case, and code_ is not
// set accurately, so we need a different mechanism. Fortunately X11 key
// mapping preserves the left-right distinction, so testing keysyms works
// if the value is available (as it is for all X11 native-based events).
if (platform_keycode_) {
return (platform_keycode_ == XK_Shift_R) ||
(platform_keycode_ == XK_Control_R) ||
(platform_keycode_ == XK_Alt_R) ||
(platform_keycode_ == XK_Meta_R) ||
(platform_keycode_ == XK_Super_R) ||
(platform_keycode_ == XK_Hyper_R);
}
// Fall through to the generic code if we have no platform_keycode_.
// Under X11, this must be a synthetic event, so we can require that
// code_ be set correctly.
#endif
return ((code_.size() > 5) &&
(code_.compare(code_.size() - 5, 5, "Right", 5)) == 0);
default:
return false;
}
}
KeyboardCode KeyEvent::GetLocatedWindowsKeyboardCode() const {
switch (key_code_) {
case VKEY_SHIFT:
return IsRightSideKey() ? VKEY_RSHIFT : VKEY_LSHIFT;
case VKEY_CONTROL:
return IsRightSideKey() ? VKEY_RCONTROL : VKEY_LCONTROL;
case VKEY_MENU:
return IsRightSideKey() ? VKEY_RMENU : VKEY_LMENU;
case VKEY_LWIN:
return IsRightSideKey() ? VKEY_RWIN : VKEY_LWIN;
// TODO(kpschoedel): EF_NUMPAD_KEY is present only on X11. Currently this
// function is only called on X11. Likely the tests here will be replaced
// with a DOM-based code enumeration test in the course of Ozone
// platform-indpendent key event work.
case VKEY_0:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD0 : VKEY_0;
case VKEY_1:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD1 : VKEY_1;
case VKEY_2:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD2 : VKEY_2;
case VKEY_3:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD3 : VKEY_3;
case VKEY_4:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD4 : VKEY_4;
case VKEY_5:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD5 : VKEY_5;
case VKEY_6:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD6 : VKEY_6;
case VKEY_7:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD7 : VKEY_7;
case VKEY_8:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD8 : VKEY_8;
case VKEY_9:
return (flags() & EF_NUMPAD_KEY) ? VKEY_NUMPAD9 : VKEY_9;
default:
return key_code_;
}
}
uint16 KeyEvent::GetConflatedWindowsKeyCode() const {
if (is_char_)
return character_;
return key_code_;
}
////////////////////////////////////////////////////////////////////////////////
// ScrollEvent
ScrollEvent::ScrollEvent() : MouseEvent() {
}
ScrollEvent::ScrollEvent(EventType type,
const gfx::PointF& 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, 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) {
set_time_stamp(time_stamp);
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