blob: b8bc5bca397bfa91f1c5b46bd4e235dad8f3b544 [file] [log] [blame]
// Copyright 2012 The Chromium Authors
// 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 <cmath>
#include <cstring>
#include <memory>
#include <string>
#include <utility>
#include "base/logging.h"
#include "base/memory/ptr_util.h"
#include "base/metrics/histogram.h"
#include "base/metrics/histogram_macros.h"
#include "base/notreached.h"
#include "base/numerics/safe_conversions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "build/build_config.h"
#include "ui/events/base_event_utils.h"
#include "ui/events/event_constants.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/transform.h"
#include "ui/gfx/geometry/transform_util.h"
#if BUILDFLAG(IS_OZONE)
#include "ui/base/ui_base_features.h" // nogncheck
#include "ui/events/ozone/events_ozone.h" // nogncheck
#include "ui/events/ozone/layout/keyboard_layout_engine.h" // nogncheck
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" // nogncheck
#endif
#if BUILDFLAG(IS_WIN)
#include "ui/events/keycodes/platform_key_map_win.h"
#endif
namespace ui {
namespace {
constexpr int kChangedButtonFlagMask =
EF_LEFT_MOUSE_BUTTON | EF_MIDDLE_MOUSE_BUTTON | EF_RIGHT_MOUSE_BUTTON |
EF_BACK_MOUSE_BUTTON | EF_FORWARD_MOUSE_BUTTON;
SourceEventType EventTypeToLatencySourceEventType(EventType type) {
switch (type) {
case ET_UNKNOWN:
// SourceEventType for GestureEvents can be either TOUCH or WHEEL. The
// proper value is assigned in the constructors.
case ET_GESTURE_SCROLL_BEGIN:
case ET_GESTURE_SCROLL_END:
case ET_GESTURE_SCROLL_UPDATE:
case ET_GESTURE_TAP:
case ET_GESTURE_TAP_DOWN:
case ET_GESTURE_TAP_CANCEL:
case ET_GESTURE_TAP_UNCONFIRMED:
case ET_GESTURE_DOUBLE_TAP:
case ET_GESTURE_BEGIN:
case ET_GESTURE_END:
case ET_GESTURE_TWO_FINGER_TAP:
case ET_GESTURE_PINCH_BEGIN:
case ET_GESTURE_PINCH_END:
case ET_GESTURE_PINCH_UPDATE:
case ET_GESTURE_SHORT_PRESS:
case ET_GESTURE_LONG_PRESS:
case ET_GESTURE_LONG_TAP:
case ET_GESTURE_SWIPE:
case ET_GESTURE_SHOW_PRESS:
// Flings can be GestureEvents too.
case ET_SCROLL_FLING_START:
case ET_SCROLL_FLING_CANCEL:
return SourceEventType::UNKNOWN;
case ET_KEY_PRESSED:
return SourceEventType::KEY_PRESS;
case ET_MOUSE_PRESSED:
case ET_MOUSE_DRAGGED:
case ET_MOUSE_RELEASED:
case ET_MOUSE_MOVED:
case ET_MOUSE_ENTERED:
case ET_MOUSE_EXITED:
// We measure latency for key presses, not key releases. Most behavior is
// keyed off of presses, and release latency is higher than press latency as
// it's impacted by event handling of the press event.
case ET_KEY_RELEASED:
case ET_MOUSE_CAPTURE_CHANGED:
case ET_DROP_TARGET_EVENT:
case ET_CANCEL_MODE:
case ET_UMA_DATA:
return SourceEventType::OTHER;
case ET_TOUCH_RELEASED:
case ET_TOUCH_PRESSED:
case ET_TOUCH_MOVED:
case ET_TOUCH_CANCELLED:
return SourceEventType::TOUCH;
case ET_MOUSEWHEEL:
case ET_SCROLL:
return SourceEventType::WHEEL;
case ET_LAST:
NOTREACHED();
return SourceEventType::UNKNOWN;
}
NOTREACHED();
return SourceEventType::UNKNOWN;
}
std::string MomentumPhaseToString(EventMomentumPhase phase) {
switch (phase) {
case EventMomentumPhase::NONE:
return "NONE";
case EventMomentumPhase::BEGAN:
return "BEGAN";
case EventMomentumPhase::MAY_BEGIN:
return "MAY_BEGIN";
case EventMomentumPhase::INERTIAL_UPDATE:
return "INERTIAL_UPDATE";
case EventMomentumPhase::END:
return "END";
case EventMomentumPhase::BLOCKED:
return "BLOCKED";
}
}
std::string ScrollEventPhaseToString(ScrollEventPhase phase) {
switch (phase) {
case ScrollEventPhase::kNone:
return "kEnd";
case ScrollEventPhase::kBegan:
return "kBegan";
case ScrollEventPhase::kUpdate:
return "kUpdate";
case ScrollEventPhase::kEnd:
return "kEnd";
}
}
#if BUILDFLAG(IS_OZONE)
uint32_t ScanCodeFromNative(const PlatformEvent& native_event) {
const KeyEvent* event = static_cast<const KeyEvent*>(native_event);
DCHECK(event->IsKeyEvent());
return event->scan_code();
}
#endif // BUILDFLAG(IS_OZONE)
bool IsNearZero(const float num) {
// Epsilon of 1e-10 at 0.
return (std::fabs(num) < 1e-10);
}
bool IsRepeatedClickTimes(const base::TimeTicks& time_stamp1,
const base::TimeTicks& time_stamp2) {
// The new event has been created from the same native event.
if (time_stamp1 == time_stamp2) {
return false;
}
base::TimeDelta time_difference = time_stamp2 - time_stamp1;
return time_difference <= base::Milliseconds(ui::kDoubleClickTimeMs);
}
} // namespace
////////////////////////////////////////////////////////////////////////////////
// Event
Event::~Event() = default;
void Event::SetNativeEvent(const PlatformEvent& event) {
if (!ShouldCopyPlatformEvents()) {
return;
}
native_event_ = event;
}
const char* Event::GetName() const {
return EventTypeName(type_).data();
}
void Event::SetProperties(const Properties& properties) {
properties_ = std::make_unique<Properties>(properties);
}
CancelModeEvent* Event::AsCancelModeEvent() {
CHECK(IsCancelModeEvent());
return static_cast<CancelModeEvent*>(this);
}
const CancelModeEvent* Event::AsCancelModeEvent() const {
CHECK(IsCancelModeEvent());
return static_cast<const CancelModeEvent*>(this);
}
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);
}
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 {
return IsPlatformEventValid(native_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);
}
void Event::SetSkipped() {
CHECK(cancelable_);
result_ = static_cast<EventResult>(result_ | ER_CONSUMED | ER_SKIPPED);
}
void Event::SetFlags(int flags) {
flags_ = flags;
OnFlagsUpdated();
}
std::string Event::ToString() const {
return base::StrCat(
{GetName(), " time_stamp=",
base::NumberToString(time_stamp_.since_origin().InSecondsF()),
" source_device_id=", base::NumberToString(source_device_id_)});
}
Event::Event(EventType type, base::TimeTicks time_stamp, int flags)
: type_(type),
time_stamp_(time_stamp.is_null() ? EventTimeForNow() : time_stamp),
flags_(flags),
native_event_(CreateInvalidPlatformEvent()) {
if (type_ < ET_LAST)
latency()->set_source_event_type(EventTypeToLatencySourceEventType(type));
}
Event::Event(const PlatformEvent& native_event, EventType type, int flags)
: type_(type),
time_stamp_(EventTimeFromNative(native_event)),
flags_(flags),
// Note that the construction of an Event directly from a PlatformEvent
// is the only time that ShouldCopyPlatformEvents() is not consulted.
native_event_(native_event) {
if (type_ < ET_LAST)
latency()->set_source_event_type(EventTypeToLatencySourceEventType(type));
ComputeEventLatencyOS(native_event);
#if BUILDFLAG(IS_OZONE)
source_device_id_ = native_event->source_device_id();
if (auto* properties = native_event->properties())
properties_ = std::make_unique<Properties>(*properties);
#endif
}
Event::Event(const Event& copy)
: type_(copy.type_),
time_stamp_(copy.time_stamp_),
latency_(copy.latency_),
flags_(copy.flags_),
native_event_(ShouldCopyPlatformEvents() ? copy.native_event_
: CreateInvalidPlatformEvent()),
source_device_id_(copy.source_device_id_),
properties_(copy.properties_
? std::make_unique<Properties>(*copy.properties_)
: nullptr) {}
Event& Event::operator=(const Event& rhs) {
if (this != &rhs) {
type_ = rhs.type_;
time_stamp_ = rhs.time_stamp_;
latency_ = rhs.latency_;
flags_ = rhs.flags_;
native_event_ = ShouldCopyPlatformEvents() ? rhs.native_event_
: CreateInvalidPlatformEvent();
cancelable_ = rhs.cancelable_;
phase_ = rhs.phase_;
result_ = rhs.result_;
source_device_id_ = rhs.source_device_id_;
if (rhs.properties_)
properties_ = std::make_unique<Properties>(*rhs.properties_);
else
properties_.reset();
}
latency_.set_source_event_type(SourceEventType::OTHER);
return *this;
}
void Event::SetType(EventType type) {
type_ = type;
if (type_ < ET_LAST)
latency()->set_source_event_type(EventTypeToLatencySourceEventType(type));
}
////////////////////////////////////////////////////////////////////////////////
// CancelModeEvent
CancelModeEvent::CancelModeEvent()
: Event(ET_CANCEL_MODE, base::TimeTicks(), 0) {
set_cancelable(false);
}
CancelModeEvent::~CancelModeEvent() = default;
std::unique_ptr<Event> CancelModeEvent::Clone() const {
return std::make_unique<CancelModeEvent>(*this);
}
////////////////////////////////////////////////////////////////////////////////
// LocatedEvent
LocatedEvent::~LocatedEvent() = default;
LocatedEvent::LocatedEvent(const PlatformEvent& 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::TimeTicks time_stamp,
int flags)
: Event(type, time_stamp, flags),
location_(location),
root_location_(root_location) {}
LocatedEvent::LocatedEvent(const LocatedEvent& copy) = default;
void LocatedEvent::UpdateForRootTransform(
const gfx::Transform& reversed_root_transform,
const gfx::Transform& reversed_local_transform) {
if (target()) {
location_ = reversed_local_transform.MapPoint(location_);
root_location_ = reversed_root_transform.MapPoint(root_location_);
} else {
// This mirrors what the code previously did.
location_ = reversed_root_transform.MapPoint(location_);
root_location_ = location_;
}
}
std::string LocatedEvent::ToString() const {
return base::StrCat({Event::ToString(), " location=", location_.ToString(),
" root_location=", root_location_.ToString()});
}
////////////////////////////////////////////////////////////////////////////////
// MouseEvent
MouseEvent::MouseEvent(const PlatformEvent& native_event)
: LocatedEvent(native_event),
changed_button_flags_(GetChangedMouseButtonFlagsFromNative(native_event)),
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_LINUX)
movement_(GetMouseMovementFromNative(native_event)),
#endif
pointer_details_(GetMousePointerDetailsFromNative(native_event)) {
latency()->set_source_event_type(SourceEventType::MOUSE);
latency()->AddLatencyNumberWithTimestamp(
INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, time_stamp());
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT);
InitializeNative();
}
MouseEvent::MouseEvent(EventType type,
const gfx::PointF& location,
const gfx::PointF& root_location,
base::TimeTicks time_stamp,
int flags,
int changed_button_flags,
const PointerDetails& pointer_details)
: LocatedEvent(type, location, root_location, time_stamp, flags),
changed_button_flags_(changed_button_flags),
pointer_details_(pointer_details) {
DCHECK_NE(ET_MOUSEWHEEL, type);
DCHECK_EQ(changed_button_flags_,
changed_button_flags_ & kChangedButtonFlagMask);
latency()->set_source_event_type(SourceEventType::MOUSE);
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT);
if (this->type() == ET_MOUSE_MOVED && IsAnyButton())
SetType(ET_MOUSE_DRAGGED);
}
MouseEvent::MouseEvent(EventType type,
const gfx::Point& location,
const gfx::Point& root_location,
base::TimeTicks time_stamp,
int flags,
int changed_button_flags,
const PointerDetails& pointer_details)
: MouseEvent(type,
gfx::PointF(location),
gfx::PointF(root_location),
time_stamp,
flags,
changed_button_flags,
pointer_details) {}
MouseEvent::MouseEvent(const MouseEvent& other) = default;
MouseEvent::~MouseEvent() = default;
void MouseEvent::InitializeNative() {
if (type() == ET_MOUSE_PRESSED || type() == ET_MOUSE_RELEASED) {
SetClickCount(GetRepeatCount(*this));
}
}
// static
bool MouseEvent::IsRepeatedClickEvent(const MouseEvent& event1,
const MouseEvent& event2) {
// These values match the Windows defaults.
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;
if (!IsRepeatedClickTimes(event1.time_stamp(), event2.time_stamp())) {
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() == ET_MOUSE_RELEASED) {
if (event.changed_button_flags() ==
last_click_event_->changed_button_flags()) {
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;
}
}
// Return the prior click count and do not update |last_click_event_| when
// re-processing a native event, or when proccesing a reposted event.
if (event.time_stamp() == last_click_event_->time_stamp())
return last_click_event_->GetClickCount();
if (IsRepeatedClickEvent(*last_click_event_, event))
click_count = last_click_event_->GetClickCount() + 1;
delete last_click_event_;
}
last_click_event_ = new MouseEvent(event);
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_ = nullptr;
}
}
// static
MouseEvent* MouseEvent::last_click_event_ = nullptr;
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_LT(0, click_count);
DCHECK_GE(3, click_count);
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;
}
SetFlags(f);
}
std::string MouseEvent::ToString() const {
return base::StrCat({
LocatedEvent::ToString(),
" flags=",
base::JoinString(base::make_span(MouseEventFlagsNames(flags())), "|"),
base::StringPrintf("(0x%04x)", flags()),
});
}
std::unique_ptr<Event> MouseEvent::Clone() const {
return std::make_unique<MouseEvent>(*this);
}
////////////////////////////////////////////////////////////////////////////////
// MouseWheelEvent
MouseWheelEvent::MouseWheelEvent(const PlatformEvent& native_event)
: MouseEvent(native_event),
offset_(GetMouseWheelOffset(native_event)),
tick_120ths_(GetMouseWheelTick120ths(native_event)) {}
MouseWheelEvent::MouseWheelEvent(const ScrollEvent& scroll_event)
: MouseEvent(scroll_event),
offset_(base::ClampRound(scroll_event.x_offset()),
base::ClampRound(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) {
SetType(ET_MOUSEWHEEL);
}
MouseWheelEvent::MouseWheelEvent(const MouseWheelEvent& mouse_wheel_event)
: MouseEvent(mouse_wheel_event),
offset_(mouse_wheel_event.offset()),
tick_120ths_(mouse_wheel_event.tick_120ths()) {
DCHECK_EQ(ET_MOUSEWHEEL, type());
}
MouseWheelEvent::MouseWheelEvent(const gfx::Vector2d& offset,
const gfx::PointF& location,
const gfx::PointF& root_location,
base::TimeTicks time_stamp,
int flags,
int changed_button_flags,
const std::optional<gfx::Vector2d> tick_120ths)
: MouseEvent(ET_UNKNOWN,
location,
root_location,
time_stamp,
flags,
changed_button_flags),
offset_(offset) {
// Set event type to ET_UNKNOWN initially in MouseEvent() to pass the
// DCHECK for type to enforce that we use MouseWheelEvent() to create
// a MouseWheelEvent.
SetType(ET_MOUSEWHEEL);
if (!tick_120ths) {
// Since no wheel ticks have been specified, assume that scrolling is linear
// (not accelerated, like it is on Chrome OS).
tick_120ths_ =
gfx::Vector2d(offset_.x() / MouseWheelEvent::kWheelDelta * 120,
offset_.y() / MouseWheelEvent::kWheelDelta * 120);
} else {
tick_120ths_ = tick_120ths.value();
}
}
MouseWheelEvent::MouseWheelEvent(const gfx::Vector2d& offset,
const gfx::Point& location,
const gfx::Point& root_location,
base::TimeTicks time_stamp,
int flags,
int changed_button_flags)
: MouseWheelEvent(offset,
gfx::PointF(location),
gfx::PointF(root_location),
time_stamp,
flags,
changed_button_flags) {}
MouseWheelEvent::~MouseWheelEvent() = default;
std::unique_ptr<Event> MouseWheelEvent::Clone() const {
return std::make_unique<MouseWheelEvent>(*this);
}
#if BUILDFLAG(IS_WIN) || BUILDFLAG(IS_FUCHSIA) || BUILDFLAG(IS_LINUX)
// This value matches Windows, Fuchsia WHEEL_DELTA, and (roughly) Firefox on
// Linux.
// static
const int MouseWheelEvent::kWheelDelta = 120;
#else
// This is a legacy value that matches GTK+ wheel scroll amount. Although being
// inherited from Linux, it is no longer used on Linux itself, but is still used
// on some other platforms.
// See https://crbug.com/1270089 for the detailed reasoning.
// static
const int MouseWheelEvent::kWheelDelta = 53;
#endif
////////////////////////////////////////////////////////////////////////////////
// TouchEvent
TouchEvent::TouchEvent(const PlatformEvent& native_event)
: LocatedEvent(native_event),
unique_event_id_(ui::GetNextTouchEventId()),
pointer_details_(GetTouchPointerDetailsFromNative(native_event)) {
latency()->AddLatencyNumberWithTimestamp(
INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, time_stamp());
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT);
}
TouchEvent::TouchEvent(EventType type,
const gfx::PointF& location,
const gfx::PointF& root_location,
base::TimeTicks time_stamp,
const PointerDetails& pointer_details,
int flags)
: LocatedEvent(type, location, root_location, time_stamp, flags),
unique_event_id_(ui::GetNextTouchEventId()),
pointer_details_(pointer_details) {
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT);
}
TouchEvent::TouchEvent(EventType type,
const gfx::Point& location,
base::TimeTicks time_stamp,
const PointerDetails& pointer_details,
int flags)
: TouchEvent(type,
gfx::PointF(location),
gfx::PointF(location),
time_stamp,
pointer_details,
flags) {}
TouchEvent::TouchEvent(const TouchEvent& copy)
: LocatedEvent(copy),
unique_event_id_(copy.unique_event_id_),
may_cause_scrolling_(copy.may_cause_scrolling_),
hovering_(copy.hovering_),
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() = default;
void TouchEvent::UpdateForRootTransform(
const gfx::Transform& inverted_root_transform,
const gfx::Transform& inverted_local_transform) {
LocatedEvent::UpdateForRootTransform(inverted_root_transform,
inverted_local_transform);
// We could create a vector and then rely on Transform::MapVector, but
// that ends up creating a 4 dimensional vector and applying a 4 dim
// transform. Really what we're looking at is only in the (x,y) plane, and
// given that we can run this relatively frequently we will inline execute the
// matrix here.
const double new_x =
fabs(pointer_details_.radius_x * inverted_root_transform.rc(0, 0) +
pointer_details_.radius_y * inverted_root_transform.rc(0, 1));
const double new_y =
fabs(pointer_details_.radius_x * inverted_root_transform.rc(1, 0) +
pointer_details_.radius_y * inverted_root_transform.rc(1, 1));
pointer_details_.radius_x = new_x;
pointer_details_.radius_y = new_y;
// for stylus touches, tilt needs to be rotated appropriately. We don't handle
// screen rotations other than 0/90/180/270, but those should be handled and
// translated appropriately. Other rotations leave tilts untouched for now. We
// add a small check that tilt is set at all before looking through this
// section.
if (!IsNearZero(pointer_details_.tilt_x) ||
!IsNearZero(pointer_details_.tilt_y)) {
if (IsNearZero(inverted_root_transform.rc(0, 1)) &&
IsNearZero(inverted_root_transform.rc(1, 0))) {
pointer_details_.tilt_x *=
std::copysign(1, inverted_root_transform.rc(0, 0));
pointer_details_.tilt_y *=
std::copysign(1, inverted_root_transform.rc(1, 1));
} else if (IsNearZero(inverted_root_transform.rc(0, 0)) &&
IsNearZero(inverted_root_transform.rc(1, 1))) {
double new_tilt_x = pointer_details_.tilt_y *
std::copysign(1, inverted_root_transform.rc(0, 1));
double new_tilt_y = pointer_details_.tilt_x *
std::copysign(1, inverted_root_transform.rc(1, 0));
pointer_details_.tilt_x = new_tilt_x;
pointer_details_.tilt_y = new_tilt_y;
}
}
}
void TouchEvent::DisableSynchronousHandling() {
DispatcherApi dispatcher_api(this);
dispatcher_api.set_result(
static_cast<EventResult>(result() | ER_DISABLE_SYNC_HANDLING));
}
void TouchEvent::ForceProcessGesture() {
DispatcherApi dispatcher_api(this);
dispatcher_api.set_result(
static_cast<EventResult>(result() | ER_FORCE_PROCESS_GESTURE));
}
void TouchEvent::SetPointerDetailsForTest(
const PointerDetails& pointer_details) {
DCHECK_EQ(pointer_details_.id, pointer_details.id);
pointer_details_ = pointer_details;
}
float TouchEvent::ComputeRotationAngle() const {
float rotation_angle = pointer_details_.twist;
while (rotation_angle < 0)
rotation_angle += 180.f;
while (rotation_angle >= 180)
rotation_angle -= 180.f;
return rotation_angle;
}
std::unique_ptr<Event> TouchEvent::Clone() const {
return std::make_unique<TouchEvent>(*this);
}
////////////////////////////////////////////////////////////////////////////////
// KeyEvent
// static
KeyEvent* KeyEvent::last_key_event_ = nullptr;
#if BUILDFLAG(IS_OZONE)
KeyEvent* KeyEvent::last_ibus_key_event_ = nullptr;
#endif
KeyEvent::KeyEvent(const PlatformEvent& native_event)
: KeyEvent(native_event, EventFlagsFromNative(native_event)) {}
KeyEvent::KeyEvent(const PlatformEvent& native_event, int event_flags)
: Event(native_event, EventTypeFromNative(native_event), event_flags),
key_code_(KeyboardCodeFromNative(native_event)),
#if BUILDFLAG(IS_OZONE)
scan_code_(ScanCodeFromNative(native_event)),
#endif // BUILDFLAG(IS_OZONE)
code_(CodeFromNative(native_event)),
is_char_(IsCharFromNative(native_event)) {
#if BUILDFLAG(IS_OZONE)
DCHECK(native_event->IsKeyEvent());
key_ = native_event->AsKeyEvent()->key_;
#endif
InitializeNative();
}
KeyEvent::KeyEvent(EventType type,
KeyboardCode key_code,
int flags,
base::TimeTicks time_stamp)
: Event(type, time_stamp, 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::TimeTicks time_stamp,
bool is_char)
: Event(type, time_stamp, flags),
key_code_(key_code),
code_(code),
is_char_(is_char),
key_(key) {}
KeyEvent::KeyEvent(EventType type,
KeyboardCode key_code,
DomCode code,
int flags,
base::TimeTicks time_stamp)
: Event(type, time_stamp, flags), key_code_(key_code), code_(code) {}
KeyEvent::KeyEvent(const KeyEvent& rhs)
: Event(rhs),
key_code_(rhs.key_code_),
#if BUILDFLAG(IS_OZONE)
scan_code_(rhs.scan_code_),
#endif // BUILDFLAG(IS_OZONE)
code_(rhs.code_),
is_char_(rhs.is_char_),
key_(rhs.key_) {
}
KeyEvent& KeyEvent::operator=(const KeyEvent& rhs) {
if (this != &rhs) {
Event::operator=(rhs);
key_code_ = rhs.key_code_;
#if BUILDFLAG(IS_OZONE)
scan_code_ = rhs.scan_code_;
#endif // BUILDFLAG(IS_OZONE)
code_ = rhs.code_;
key_ = rhs.key_;
is_char_ = rhs.is_char_;
}
return *this;
}
KeyEvent::~KeyEvent() = default;
// static
// For compatibility, this is enabled by default.
bool KeyEvent::synthesize_key_repeat_enabled_ = true;
// static
bool KeyEvent::IsSynthesizeKeyRepeatEnabled() {
return synthesize_key_repeat_enabled_;
}
// static
void KeyEvent::SetSynthesizeKeyRepeatEnabled(bool enabled) {
synthesize_key_repeat_enabled_ = enabled;
}
KeyEvent KeyEvent::FromCharacter(char16_t character,
KeyboardCode key_code,
DomCode code,
int flags,
base::TimeTicks time_stamp) {
return KeyEvent(ET_KEY_PRESSED, key_code, code, flags,
DomKey::FromCharacter(character), time_stamp, true);
}
void KeyEvent::InitializeNative() {
latency()->AddLatencyNumberWithTimestamp(
INPUT_EVENT_LATENCY_ORIGINAL_COMPONENT, time_stamp());
latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT);
// Check if this is a key repeat. This must be called before initial flags
// processing, e.g: NormalizeFlags(), to avoid issues like crbug.com/1069690.
if (synthesize_key_repeat_enabled_ && IsRepeated(GetLastKeyEvent()))
SetFlags(flags() | EF_IS_REPEAT);
#if BUILDFLAG(IS_LINUX)
NormalizeFlags();
#elif BUILDFLAG(IS_WIN)
// Only Windows has native character events.
if (is_char_) {
key_ = DomKey::FromCharacter(static_cast<int32_t>(native_event().wParam));
SetFlags(PlatformKeyMap::ReplaceControlAndAltWithAltGraph(flags()));
} else {
int adjusted_flags = flags();
key_ = PlatformKeyMap::DomKeyFromKeyboardCode(key_code(), &adjusted_flags);
SetFlags(adjusted_flags);
}
#endif
}
void KeyEvent::ApplyLayout() const {
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;
}
}
if (key_ != DomKey::NONE)
return;
KeyboardCode dummy_key_code;
#if BUILDFLAG(IS_OZONE)
if (KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup(
code, flags(), &key_, &dummy_key_code)) {
return;
}
#endif
#if !BUILDFLAG(IS_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.
if (IsPlatformEventValid(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;
}
bool KeyEvent::IsRepeated(KeyEvent** last_key_event) {
DCHECK(last_key_event);
// A safe guard in case if there were continuous key pressed events that are
// not auto repeat.
const int kMaxAutoRepeatTimeMs = 2000;
if (is_char())
return false;
if (type() == ET_KEY_RELEASED) {
delete *last_key_event;
*last_key_event = nullptr;
return false;
}
CHECK_EQ(ET_KEY_PRESSED, type());
KeyEvent* last = *last_key_event;
if (!last) {
*last_key_event = new KeyEvent(*this);
return false;
} else if (time_stamp() == last->time_stamp()) {
// The KeyEvent is created from the same native event.
return (last->flags() & EF_IS_REPEAT) != 0;
}
DCHECK(last);
bool is_repeat = false;
#if BUILDFLAG(IS_WIN)
if (HasNativeEvent()) {
// Bit 30 of lParam represents the "previous key state". If set, the key
// was already down, therefore this is an auto-repeat.
is_repeat = (native_event().lParam & 0x40000000) != 0;
}
#endif
if (!is_repeat) {
if (key_code() == last->key_code() &&
(flags() & ~EF_MOUSE_BUTTON) ==
(last->flags() & ~EF_IS_REPEAT & ~EF_MOUSE_BUTTON) &&
(time_stamp() - last->time_stamp()).InMilliseconds() <
kMaxAutoRepeatTimeMs) {
is_repeat = true;
}
}
if (is_repeat) {
last->set_time_stamp(time_stamp());
last->SetFlags(last->flags() | EF_IS_REPEAT);
return true;
}
delete *last_key_event;
*last_key_event = new KeyEvent(*this);
return false;
}
KeyEvent** KeyEvent::GetLastKeyEvent() {
#if BUILDFLAG(IS_OZONE)
// Use a different static variable for key events that have non standard
// state masks as it may be reposted by an IME. IBUS-GTK and fcitx-GTK uses
// this field to detect the re-posted event for example. crbug.com/385873.
return properties() && properties()->contains(kPropertyKeyboardImeFlag)
? &last_ibus_key_event_
: &last_key_event_;
#else
return &last_key_event_;
#endif
}
DomKey KeyEvent::GetDomKey() const {
// Determination of key_ may be done lazily.
if (key_ == DomKey::NONE)
ApplyLayout();
return key_;
}
void KeyEvent::OnFlagsUpdated() {
// TODO(https://crbug.com/324462727): this is problematic on windows.
#if BUILDFLAG(IS_CHROMEOS)
key_ = DomKey::NONE;
#endif
}
char16_t 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();
char16_t ucs2_character = static_cast<char16_t>(utf32_character);
DCHECK_EQ(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;
}
char16_t KeyEvent::GetText() const {
if ((flags() & EF_CONTROL_DOWN) != 0) {
DomKey key;
KeyboardCode key_code;
if (DomCodeToControlCharacter(code_, flags(), &key, &key_code))
return key.ToCharacter();
}
return GetUnmodifiedText();
}
char16_t KeyEvent::GetUnmodifiedText() const {
if (!is_char_ && (key_code_ == VKEY_RETURN))
return '\r';
return GetCharacter();
}
bool KeyEvent::IsUnicodeKeyCode() const {
#if BUILDFLAG(IS_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)
SetFlags(flags() | mask);
else
SetFlags(flags() & ~mask);
}
std::string KeyEvent::ToString() const {
auto dom_key = GetDomKey();
return base::StrCat({
Event::ToString(),
" code=",
KeycodeConverter::DomCodeToCodeString(code()),
base::StringPrintf("(0x%04x)", static_cast<uint32_t>(code_)),
" key=",
KeycodeConverter::DomKeyToKeyString(dom_key),
base::StringPrintf("(0x%04x)", static_cast<uint32_t>(dom_key)),
" keycode=",
base::StringPrintf("(0x%04x)", key_code_),
#if BUILDFLAG(IS_OZONE)
" scan_code=",
base::StringPrintf("(0x%04x)", scan_code_),
#endif // BUILDFLAG(IS_OZONE)
" flags=",
base::JoinString(base::make_span(KeyEventFlagsNames(flags())), "|"),
base::StringPrintf("(0x%04x)", flags()),
});
}
std::unique_ptr<Event> KeyEvent::Clone() const {
return std::make_unique<KeyEvent>(*this);
}
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 PlatformEvent& 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),
momentum_phase_(EventMomentumPhase::NONE),
scroll_event_phase_(ScrollEventPhase::kNone) {
// TODO(bokan): This should be populating the |scroll_event_phase_| member but
// currently isn't.
if (type() == ET_SCROLL) {
GetScrollOffsets(native_event, &x_offset_, &y_offset_, &x_offset_ordinal_,
&y_offset_ordinal_, &finger_count_, &momentum_phase_);
} 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_, nullptr);
} else {
NOTREACHED() << "Unexpected event type " << type()
<< " when constructing a ScrollEvent.";
}
if (IsScrollEvent())
latency()->set_source_event_type(SourceEventType::WHEEL);
else
latency()->set_source_event_type(SourceEventType::TOUCH);
}
ScrollEvent::ScrollEvent(EventType type,
const gfx::PointF& location,
const gfx::PointF& root_location,
base::TimeTicks time_stamp,
int flags,
float x_offset,
float y_offset,
float x_offset_ordinal,
float y_offset_ordinal,
int finger_count,
EventMomentumPhase momentum_phase,
ScrollEventPhase scroll_event_phase)
: MouseEvent(type, location, root_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),
momentum_phase_(momentum_phase),
scroll_event_phase_(scroll_event_phase) {
CHECK(IsScrollEvent());
latency()->set_source_event_type(SourceEventType::WHEEL);
}
ScrollEvent::ScrollEvent(EventType type,
const gfx::Point& location,
base::TimeTicks time_stamp,
int flags,
float x_offset,
float y_offset,
float x_offset_ordinal,
float y_offset_ordinal,
int finger_count,
EventMomentumPhase momentum_phase,
ScrollEventPhase scroll_event_phase)
: ScrollEvent(type,
gfx::PointF(location),
gfx::PointF(location),
time_stamp,
flags,
x_offset,
y_offset,
x_offset_ordinal,
y_offset_ordinal,
finger_count,
momentum_phase,
scroll_event_phase) {}
ScrollEvent::ScrollEvent(const ScrollEvent& other) = default;
ScrollEvent::~ScrollEvent() = default;
void ScrollEvent::Scale(const float factor) {
x_offset_ *= factor;
y_offset_ *= factor;
x_offset_ordinal_ *= factor;
y_offset_ordinal_ *= factor;
}
std::string ScrollEvent::ToString() const {
return base::StrCat({
MouseEvent::ToString(),
base::StringPrintf(" offset=%g,%g", x_offset_, y_offset_),
base::StringPrintf(" offset_ordinal=%g,%g", x_offset_ordinal_,
y_offset_ordinal_),
" momentum_phase=",
MomentumPhaseToString(momentum_phase_),
" event_phase=",
ScrollEventPhaseToString(scroll_event_phase_),
});
}
std::unique_ptr<Event> ScrollEvent::Clone() const {
return std::make_unique<ScrollEvent>(*this);
}
////////////////////////////////////////////////////////////////////////////////
// GestureEvent
GestureEvent::GestureEvent(float x,
float y,
int flags,
base::TimeTicks time_stamp,
const GestureEventDetails& details,
uint32_t unique_touch_event_id)
: LocatedEvent(details.type(),
gfx::PointF(x, y),
gfx::PointF(x, y),
time_stamp,
flags | EF_FROM_TOUCH),
details_(details),
unique_touch_event_id_(unique_touch_event_id) {
latency()->set_source_event_type(SourceEventType::TOUCH);
// TODO(crbug.com/868056) Other touchpad gesture should report as TOUCHPAD.
if (IsPinchEvent() &&
details.device_type() == GestureDeviceType::DEVICE_TOUCHPAD) {
latency()->set_source_event_type(SourceEventType::TOUCHPAD);
}
}
GestureEvent::GestureEvent(const GestureEvent& other) = default;
GestureEvent::~GestureEvent() = default;
std::string GestureEvent::ToString() const {
return base::StrCat({
LocatedEvent::ToString(),
" touch_event_id=",
base::NumberToString(unique_touch_event_id_),
});
}
std::unique_ptr<Event> GestureEvent::Clone() const {
return std::make_unique<GestureEvent>(*this);
}
} // namespace ui