// 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/base/event.h" #if defined(USE_X11) #include #endif #include #include "ui/base/keycodes/keyboard_code_conversion.h" #include "ui/gfx/point3.h" #include "ui/gfx/interpolated_transform.h" #include "ui/gfx/transform.h" #if defined(USE_X11) #include "ui/base/keycodes/keyboard_code_conversion_x.h" #endif namespace { base::NativeEvent CopyNativeEvent(const base::NativeEvent& event) { #if defined(USE_X11) if (!event || event->type == GenericEvent) return NULL; XEvent* copy = new XEvent; *copy = *event; return copy; #elif defined(OS_WIN) return event; #else NOTREACHED() << "Don't know how to copy base::NativeEvent for this platform"; return NULL; #endif } } // namespace namespace ui { //////////////////////////////////////////////////////////////////////////////// // Event Event::~Event() { #if defined(USE_X11) if (delete_native_event_) delete native_event_; #endif } 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)); } Event::Event(EventType type, int flags) : type_(type), time_stamp_(base::Time::NowFromSystemTime() - base::Time()), flags_(flags), delete_native_event_(false) { Init(); } Event::Event(const base::NativeEvent& native_event, EventType type, int flags) : type_(type), time_stamp_(EventTimeFromNative(native_event)), flags_(flags), delete_native_event_(false) { InitWithNativeEvent(native_event); } Event::Event(const Event& copy) : native_event_(::CopyNativeEvent(copy.native_event_)), type_(copy.type_), time_stamp_(copy.time_stamp_), flags_(copy.flags_), delete_native_event_(false) { #if defined(USE_X11) if (native_event_) delete_native_event_ = true; #endif } void Event::Init() { std::memset(&native_event_, 0, sizeof(native_event_)); } void Event::InitWithNativeEvent(const base::NativeEvent& native_event) { native_event_ = native_event; } //////////////////////////////////////////////////////////////////////////////// // 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_), valid_system_location_(true), system_location_(ui::EventSystemLocationFromNative(native_event)) { } LocatedEvent::LocatedEvent(EventType type, const gfx::Point& location, const gfx::Point& root_location, int flags) : Event(type, flags), location_(location), root_location_(root_location), valid_system_location_(false), system_location_(0, 0) { } LocatedEvent::LocatedEvent(const LocatedEvent& model) : Event(model), location_(model.location_), root_location_(model.root_location_), valid_system_location_(model.valid_system_location_), system_location_(model.system_location_) { } void LocatedEvent::UpdateForRootTransform(const Transform& root_transform) { // Transform has to be done at root level. DCHECK_EQ(root_location_.x(), location_.x()); DCHECK_EQ(root_location_.y(), location_.y()); gfx::Point3f p(location_); root_transform.TransformPointReverse(p); root_location_ = location_ = p.AsPoint(); } //////////////////////////////////////////////////////////////////////////////// // MouseEvent MouseEvent::MouseEvent(const base::NativeEvent& native_event) : LocatedEvent(native_event), changed_button_flags_( GetChangedMouseButtonFlagsFromNative(native_event)) { if (type() == ET_MOUSE_PRESSED) SetClickCount(GetRepeatCount(*this)); } MouseEvent::MouseEvent(EventType type, const gfx::Point& location, const gfx::Point& root_location, int flags) : LocatedEvent(type, location, root_location, flags), changed_button_flags_(0) { } // 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 (abs(event2.x() - event1.x()) > kDoubleClickWidth / 2) return false; if (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 (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; } // static MouseEvent* MouseEvent::last_click_event_ = NULL; int MouseEvent::GetClickCount() const { if (type() != ET_MOUSE_PRESSED) 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) 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); } MouseEvent::MouseEvent(const MouseEvent& model) : LocatedEvent(model) { } //////////////////////////////////////////////////////////////////////////////// // MouseWheelEvent MouseWheelEvent::MouseWheelEvent(const base::NativeEvent& native_event) : MouseEvent(native_event), offset_(GetMouseWheelOffset(native_event)) { } MouseWheelEvent::MouseWheelEvent(const MouseEvent& mouse_event) : MouseEvent(mouse_event), offset_(GetMouseWheelOffset(mouse_event.native_event())) { } MouseWheelEvent::MouseWheelEvent(const ScrollEvent& scroll_event) : MouseEvent(scroll_event), offset_(scroll_event.y_offset()) { set_type(ET_MOUSEWHEEL); } #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)), radius_x_(GetTouchRadiusX(native_event)), radius_y_(GetTouchRadiusY(native_event)), rotation_angle_(GetTouchAngle(native_event)), force_(GetTouchForce(native_event)) { } TouchEvent::TouchEvent(EventType type, const gfx::Point& location, int touch_id, base::TimeDelta time_stamp) : LocatedEvent(type, location, location, 0), touch_id_(touch_id), radius_x_(0.0f), radius_y_(0.0f), rotation_angle_(0.0f), force_(0.0f) { set_time_stamp(time_stamp); } TouchEvent::~TouchEvent() { } void TouchEvent::UpdateForRootTransform(const Transform& root_transform) { LocatedEvent::UpdateForRootTransform(root_transform); gfx::Point3f scale; InterpolatedTransform::FactorTRS(root_transform, NULL, NULL, &scale); if (scale.x()) radius_x_ /= scale.x(); if (scale.y()) radius_y_ /= scale.y(); } //////////////////////////////////////////////////////////////////////////////// // TestTouchEvent TestTouchEvent::TestTouchEvent(EventType type, int x, int y, int flags, int touch_id, float radius_x, float radius_y, float angle, float force) : TouchEvent(type, gfx::Point(x, y), touch_id, base::TimeDelta()) { set_flags(flags); set_radius(radius_x, radius_y); set_rotation_angle(angle); set_force(force); } //////////////////////////////////////////////////////////////////////////////// // KeyEvent KeyEvent::KeyEvent(const base::NativeEvent& native_event, bool is_char) : Event(native_event, EventTypeFromNative(native_event), EventFlagsFromNative(native_event)), key_code_(KeyboardCodeFromNative(native_event)), is_char_(is_char), character_(0), unmodified_character_(0) { #if defined(USE_X11) NormalizeFlags(); #endif } KeyEvent::KeyEvent(EventType type, KeyboardCode key_code, int flags) : Event(type, flags), key_code_(key_code), is_char_(false), character_(GetCharacterFromKeyCode(key_code, flags)), unmodified_character_(0) { } uint16 KeyEvent::GetCharacter() const { if (character_) return character_; #if defined(OS_WIN) return (native_event().message == WM_CHAR) ? key_code_ : GetCharacterFromKeyCode(key_code_, flags()); #elif defined(USE_X11) if (!native_event()) return GetCharacterFromKeyCode(key_code_, flags()); DCHECK(native_event()->type == KeyPress || native_event()->type == KeyRelease); uint16 ch = 0; if (!IsControlDown()) ch = GetCharacterFromXEvent(native_event()); return ch ? ch : GetCharacterFromKeyCode(key_code_, flags()); #else NOTIMPLEMENTED(); return 0; #endif } uint16 KeyEvent::GetUnmodifiedCharacter() const { if (unmodified_character_) return unmodified_character_; #if defined(OS_WIN) // Looks like there is no way to get unmodified character on Windows. return (native_event().message == WM_CHAR) ? key_code_ : GetCharacterFromKeyCode(key_code_, flags() & EF_SHIFT_DOWN); #elif defined(USE_X11) if (!native_event()) return GetCharacterFromKeyCode(key_code_, flags() & EF_SHIFT_DOWN); DCHECK(native_event()->type == KeyPress || native_event()->type == KeyRelease); static const unsigned int kIgnoredModifiers = ControlMask | LockMask | Mod1Mask | Mod2Mask | Mod3Mask | Mod4Mask | Mod5Mask; XKeyEvent copy = native_event()->xkey; // bit-wise copy is safe. // We can't use things like (native_event()->xkey.state & ShiftMask), as it // may mask out bits used by X11 internally. copy.state &= ~kIgnoredModifiers; uint16 ch = GetCharacterFromXEvent(reinterpret_cast(©)); return ch ? ch : GetCharacterFromKeyCode(key_code_, flags() & EF_SHIFT_DOWN); #else NOTIMPLEMENTED(); return 0; #endif } KeyEvent* KeyEvent::Copy() { KeyEvent* copy = new KeyEvent(::CopyNativeEvent(native_event()), is_char()); #if defined(USE_X11) copy->set_delete_native_event(true); #endif return copy; } void KeyEvent::NormalizeFlags() { int mask = 0; switch (key_code()) { case ui::VKEY_CONTROL: mask = ui::EF_CONTROL_DOWN; break; case ui::VKEY_SHIFT: mask = ui::EF_SHIFT_DOWN; break; case ui::VKEY_MENU: mask = ui::EF_ALT_DOWN; break; case ui::VKEY_CAPITAL: mask = ui::EF_CAPS_LOCK_DOWN; break; default: return; } if (type() == ui::ET_KEY_PRESSED) set_flags(flags() | mask); else set_flags(flags() & ~mask); } //////////////////////////////////////////////////////////////////////////////// // TranslatedKeyEvent TranslatedKeyEvent::TranslatedKeyEvent(const base::NativeEvent& native_event, bool is_char) : KeyEvent(native_event, is_char) { set_type(type() == ET_KEY_PRESSED ? ET_TRANSLATED_KEY_PRESS : ET_TRANSLATED_KEY_RELEASE); } TranslatedKeyEvent::TranslatedKeyEvent(bool is_press, KeyboardCode key_code, int flags) : KeyEvent((is_press ? ET_TRANSLATED_KEY_PRESS : ET_TRANSLATED_KEY_RELEASE), key_code, flags) { } void TranslatedKeyEvent::ConvertToKeyEvent() { set_type(type() == ET_TRANSLATED_KEY_PRESS ? ET_KEY_PRESSED : ET_KEY_RELEASED); } //////////////////////////////////////////////////////////////////////////////// // ScrollEvent ScrollEvent::ScrollEvent(const base::NativeEvent& native_event) : MouseEvent(native_event) { if (type() == ET_SCROLL) { GetScrollOffsets(native_event, &x_offset_, &y_offset_); double start, end; GetGestureTimes(native_event, &start, &end); } else if (type() == ET_SCROLL_FLING_START) { bool is_cancel; GetFlingData(native_event, &x_offset_, &y_offset_, &is_cancel); } } //////////////////////////////////////////////////////////////////////////////// // GestureEvent GestureEvent::GestureEvent(EventType type, int x, int y, int flags, base::TimeDelta time_stamp, const GestureEventDetails& details, unsigned int touch_ids_bitfield) : LocatedEvent(type, gfx::Point(x, y), gfx::Point(x, y), flags), details_(details), touch_ids_bitfield_(touch_ids_bitfield) { set_time_stamp(time_stamp); } GestureEvent::~GestureEvent() { } int GestureEvent::GetLowestTouchId() const { if (touch_ids_bitfield_ == 0) return -1; int i = -1; // Find the index of the least significant 1 bit while (!(1 << ++i & touch_ids_bitfield_)); return i; } } // namespace ui