// 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 #include #include #endif #include #include #include "base/metrics/histogram.h" #include "base/strings/stringprintf.h" #include "ui/events/event_utils.h" #include "ui/events/keycodes/dom3/dom_code.h" #include "ui/events/keycodes/dom3/dom_key.h" #include "ui/events/keycodes/dom4/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" #elif defined(USE_OZONE) #include "ui/events/ozone/layout/keyboard_layout_engine.h" #include "ui/events/ozone/layout/keyboard_layout_engine_manager.h" #endif 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_TRANSLATED_KEY_PRESS); CASE_TYPE(ET_TRANSLATED_KEY_RELEASE); 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 } unsigned long long get_next_touch_event_id() { static unsigned long long id = 0; return id++; } } // namespace namespace ui { //////////////////////////////////////////////////////////////////////////////// // Event // static scoped_ptr Event::Clone(const Event& event) { if (event.IsKeyEvent()) { return scoped_ptr(new KeyEvent(static_cast(event))); } if (event.IsMouseEvent()) { if (event.IsMouseWheelEvent()) { return scoped_ptr( new MouseWheelEvent(static_cast(event))); } return scoped_ptr( new MouseEvent(static_cast(event))); } if (event.IsTouchEvent()) { return scoped_ptr( new TouchEvent(static_cast(event))); } if (event.IsGestureEvent()) { return scoped_ptr( new GestureEvent(static_cast(event))); } if (event.IsScrollEvent()) { return scoped_ptr( new ScrollEvent(static_cast(event))); } return scoped_ptr(new Event(event)); } Event::~Event() { if (delete_native_event_) ReleaseCopiedNativeEvent(native_event_); } GestureEvent* Event::AsGestureEvent() { CHECK(IsGestureEvent()); return static_cast(this); } const GestureEvent* Event::AsGestureEvent() const { CHECK(IsGestureEvent()); return static_cast(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(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(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(delta.InMicroseconds()); UMA_HISTOGRAM_CUSTOM_COUNTS("Event.Latency.Browser", delta_sample, 1, 1000000, 100); std::string name_for_event = base::StringPrintf("Event.Latency.Browser.%s", name_.c_str()); base::HistogramBase* counter_for_type = base::Histogram::FactoryGet( name_for_event, 1, 1000000, 100, base::HistogramBase::kUmaTargetedHistogramFlag); counter_for_type->Add(delta_sample); #if defined(USE_X11) if (native_event->type == GenericEvent) { XIDeviceEvent* xiev = static_cast(native_event->xcookie.data); source_device_id_ = xiev->sourceid; } #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)) { if (type() == ET_MOUSE_PRESSED || type() == ET_MOUSE_RELEASED) SetClickCount(GetRepeatCount(*this)); } 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; } // 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::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) { } #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 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(const base::NativeEvent& native_event) : LocatedEvent(native_event), touch_id_(GetTouchId(native_event)), unique_event_id_(get_next_touch_event_id()), radius_x_(GetTouchRadiusX(native_event)), radius_y_(GetTouchRadiusY(native_event)), rotation_angle_(GetTouchAngle(native_event)), force_(GetTouchForce(native_event)), may_cause_scrolling_(false) { 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); if (type() == ET_TOUCH_PRESSED) IncrementTouchIdRefCount(native_event); } 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), unique_event_id_(get_next_touch_event_id()), radius_x_(0.0f), radius_y_(0.0f), rotation_angle_(0.0f), force_(0.0f), may_cause_scrolling_(false) { 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), unique_event_id_(get_next_touch_event_id()), radius_x_(radius_x), radius_y_(radius_y), rotation_angle_(angle), force_(force), may_cause_scrolling_(false) { latency()->AddLatencyNumber(INPUT_EVENT_LATENCY_UI_COMPONENT, 0, 0); } 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 (HasNativeEvent()) 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]) radius_x_ *= decomp.scale[0]; if (decomp.scale[1]) radius_y_ *= decomp.scale[1]; } void TouchEvent::DisableSynchronousHandling() { DispatcherApi dispatcher_api(this); dispatcher_api.set_result( static_cast(result() | ER_DISABLE_SYNC_HANDLING)); } //////////////////////////////////////////////////////////////////////////////// // 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; } if (event.key_code() == last_key_event_->key_code() && event.flags() == last_key_event_->flags() && (event.time_stamp() - last_key_event_->time_stamp()).InMilliseconds() < kMaxAutoRepeatTimeMs) { last_key_event_->set_time_stamp(event.time_stamp()); return true; } delete last_key_event_; last_key_event_ = new KeyEvent(event); return false; } 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)), platform_keycode_(PlatformKeycodeFromNative(native_event)), key_(DomKey::NONE), character_(0) { 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_) character_ = native_event.wParam; #endif } KeyEvent::KeyEvent(EventType type, KeyboardCode key_code, int flags) : Event(type, EventTimeForNow(), flags), key_code_(key_code), code_(DomCode::NONE), is_char_(false), platform_keycode_(0), key_(DomKey::NONE), character_() { } KeyEvent::KeyEvent(EventType type, KeyboardCode key_code, DomCode code, int flags) : Event(type, EventTimeForNow(), flags), key_code_(key_code), code_(code), is_char_(false), platform_keycode_(0), key_(DomKey::NONE), character_(0) { } KeyEvent::KeyEvent(EventType type, KeyboardCode key_code, DomCode code, int flags, DomKey key, base::char16 character) : Event(type, EventTimeForNow(), flags), key_code_(key_code), code_(code), is_char_(false), platform_keycode_(0), key_(key), character_(character) { } 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), platform_keycode_(0), key_(DomKey::CHARACTER), 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_), key_(rhs.key_), 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_; key_ = rhs.key_; 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 data) { extended_key_event_data_ = data.Pass(); } void KeyEvent::ApplyLayout() const { // If the client has set the character (e.g. faked key events from virtual // keyboard), it's client's responsibility to set the dom key correctly. // Otherwise, set the dom key as unidentified. // Please refer to crbug.com/443889. if (character_ != 0) { key_ = DomKey::UNIDENTIFIED; return; } #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. GetMeaningFromKeyCode(key_code_, flags(), &key_, &character_); #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. character_ = (IsControlDown() || !native_event()) ? GetCharacterFromKeyCode(key_code_, flags()) : GetCharacterFromXEvent(native_event()); // TODO(kpschoedel): set key_ field for X11. #elif defined(USE_OZONE) KeyboardCode key_code; if (!KeyboardLayoutEngineManager::GetKeyboardLayoutEngine()->Lookup( code_, flags(), &key_, &character_, &key_code, &platform_keycode_)) { GetMeaningFromKeyCode(key_code_, flags(), &key_, &character_); } #else if (native_event()) { DCHECK(EventTypeFromNative(native_event()) == ET_KEY_PRESSED || EventTypeFromNative(native_event()) == ET_KEY_RELEASED); } // TODO(kpschoedel): revise to use DOM code_ instead of Windows key_code_ GetMeaningFromKeyCode(key_code_, flags(), &key_, &character_); #endif } DomKey KeyEvent::GetDomKey() const { // Determination of character_ and key_ may be done lazily. if (key_ == DomKey::NONE) ApplyLayout(); return key_; } base::char16 KeyEvent::GetCharacter() const { // Determination of character_ and key_ may be done lazily. if (key_ == DomKey::NONE) ApplyLayout(); return character_; } base::char16 KeyEvent::GetText() const { if ((flags() & EF_CONTROL_DOWN) != 0) { // TODO(kpschoedel): revise to use DOM code_ instead of Windows key_code_ 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 { #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_EXTENDED) && (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; 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_ == DomCode::SHIFT_RIGHT) || (code_ == DomCode::CONTROL_RIGHT) || (code_ == DomCode::ALT_RIGHT) || (code_ == DomCode::OS_RIGHT); 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_; } std::string KeyEvent::GetCodeString() const { return KeycodeConverter::DomCodeToCodeString(code_); } //////////////////////////////////////////////////////////////////////////////// // ScrollEvent ScrollEvent::ScrollEvent(const base::NativeEvent& native_event) : MouseEvent(native_event) { 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::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