|  | // 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_constants.h" | 
|  |  | 
|  | #include <cmath> | 
|  | #include <string.h> | 
|  | #include <X11/extensions/XInput.h> | 
|  | #include <X11/extensions/XInput2.h> | 
|  | #include <X11/Xlib.h> | 
|  | #include <X11/Xutil.h> | 
|  | #include <X11/XKBlib.h> | 
|  |  | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/singleton.h" | 
|  | #include "ui/events/event.h" | 
|  | #include "ui/events/event_utils.h" | 
|  | #include "ui/events/keycodes/keyboard_code_conversion_x.h" | 
|  | #include "ui/events/x/device_data_manager_x11.h" | 
|  | #include "ui/events/x/device_list_cache_x.h" | 
|  | #include "ui/events/x/touch_factory_x11.h" | 
|  | #include "ui/gfx/display.h" | 
|  | #include "ui/gfx/point.h" | 
|  | #include "ui/gfx/rect.h" | 
|  | #include "ui/gfx/screen.h" | 
|  | #include "ui/gfx/x/x11_atom_cache.h" | 
|  | #include "ui/gfx/x/x11_types.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+. | 
|  | const int kWheelScrollAmount = 53; | 
|  |  | 
|  | const int kMinWheelButton = 4; | 
|  | const int kMaxWheelButton = 7; | 
|  |  | 
|  | // A class to track current modifier state on master device. Only track ctrl, | 
|  | // alt, shift and caps lock keys currently. The tracked state can then be used | 
|  | // by floating device. | 
|  | class XModifierStateWatcher{ | 
|  | public: | 
|  | static XModifierStateWatcher* GetInstance() { | 
|  | return Singleton<XModifierStateWatcher>::get(); | 
|  | } | 
|  |  | 
|  | int StateFromKeyboardCode(ui::KeyboardCode keyboard_code) { | 
|  | switch (keyboard_code) { | 
|  | case ui::VKEY_CONTROL: | 
|  | return ControlMask; | 
|  | case ui::VKEY_SHIFT: | 
|  | return ShiftMask; | 
|  | case ui::VKEY_MENU: | 
|  | return Mod1Mask; | 
|  | case ui::VKEY_CAPITAL: | 
|  | return LockMask; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | void UpdateStateFromXEvent(const base::NativeEvent& native_event) { | 
|  | ui::KeyboardCode keyboard_code = ui::KeyboardCodeFromNative(native_event); | 
|  | unsigned int mask = StateFromKeyboardCode(keyboard_code); | 
|  | // Floating device can't access the modifer state from master device. | 
|  | // We need to track the states of modifier keys in a singleton for | 
|  | // floating devices such as touch screen. Issue 106426 is one example | 
|  | // of why we need the modifier states for floating device. | 
|  | switch (native_event->type) { | 
|  | case KeyPress: | 
|  | state_ = native_event->xkey.state | mask; | 
|  | break; | 
|  | case KeyRelease: | 
|  | state_ = native_event->xkey.state & ~mask; | 
|  | break; | 
|  | case GenericEvent: { | 
|  | XIDeviceEvent* xievent = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | switch (xievent->evtype) { | 
|  | case XI_KeyPress: | 
|  | state_ = xievent->mods.effective |= mask; | 
|  | break; | 
|  | case XI_KeyRelease: | 
|  | state_ = xievent->mods.effective &= ~mask; | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | break; | 
|  | } | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Returns the current modifer state in master device. It only contains the | 
|  | // state of ctrl, shift, alt and caps lock keys. | 
|  | unsigned int state() { return state_; } | 
|  |  | 
|  | private: | 
|  | friend struct DefaultSingletonTraits<XModifierStateWatcher>; | 
|  |  | 
|  | XModifierStateWatcher() : state_(0) { } | 
|  |  | 
|  | unsigned int state_; | 
|  |  | 
|  | DISALLOW_COPY_AND_ASSIGN(XModifierStateWatcher); | 
|  | }; | 
|  |  | 
|  | #if defined(USE_XI2_MT) | 
|  | // Detects if a touch event is a driver-generated 'special event'. | 
|  | // A 'special event' is a touch event with maximum radius and pressure at | 
|  | // location (0, 0). | 
|  | // This needs to be done in a cleaner way: http://crbug.com/169256 | 
|  | bool TouchEventIsGeneratedHack(const base::NativeEvent& native_event) { | 
|  | XIDeviceEvent* event = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | CHECK(event->evtype == XI_TouchBegin || | 
|  | event->evtype == XI_TouchUpdate || | 
|  | event->evtype == XI_TouchEnd); | 
|  |  | 
|  | // Force is normalized to [0, 1]. | 
|  | if (ui::GetTouchForce(native_event) < 1.0f) | 
|  | return false; | 
|  |  | 
|  | if (ui::EventLocationFromNative(native_event) != gfx::Point()) | 
|  | return false; | 
|  |  | 
|  | // Radius is in pixels, and the valuator is the diameter in pixels. | 
|  | double radius = ui::GetTouchRadiusX(native_event), min, max; | 
|  | unsigned int deviceid = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; | 
|  | if (!ui::DeviceDataManagerX11::GetInstance()->GetDataRange( | 
|  | deviceid, ui::DeviceDataManagerX11::DT_TOUCH_MAJOR, &min, &max)) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return radius * 2 == max; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | int GetEventFlagsFromXState(unsigned int state) { | 
|  | int flags = 0; | 
|  | if (state & ControlMask) | 
|  | flags |= ui::EF_CONTROL_DOWN; | 
|  | if (state & ShiftMask) | 
|  | flags |= ui::EF_SHIFT_DOWN; | 
|  | if (state & Mod1Mask) | 
|  | flags |= ui::EF_ALT_DOWN; | 
|  | if (state & LockMask) | 
|  | flags |= ui::EF_CAPS_LOCK_DOWN; | 
|  | if (state & Mod3Mask) | 
|  | flags |= ui::EF_MOD3_DOWN; | 
|  | if (state & Mod4Mask) | 
|  | flags |= ui::EF_COMMAND_DOWN; | 
|  | if (state & Mod5Mask) | 
|  | flags |= ui::EF_ALTGR_DOWN; | 
|  | if (state & Button1Mask) | 
|  | flags |= ui::EF_LEFT_MOUSE_BUTTON; | 
|  | if (state & Button2Mask) | 
|  | flags |= ui::EF_MIDDLE_MOUSE_BUTTON; | 
|  | if (state & Button3Mask) | 
|  | flags |= ui::EF_RIGHT_MOUSE_BUTTON; | 
|  | return flags; | 
|  | } | 
|  |  | 
|  | int GetEventFlagsFromXKeyEvent(XEvent* xevent) { | 
|  | DCHECK(xevent->type == KeyPress || xevent->type == KeyRelease); | 
|  |  | 
|  | #if defined(OS_CHROMEOS) | 
|  | const int ime_fabricated_flag = 0; | 
|  | #else | 
|  | // XIM fabricates key events for the character compositions by XK_Multi_key. | 
|  | // For example, when a user hits XK_Multi_key, XK_apostrophe, and XK_e in | 
|  | // order to input "é", then XIM generates a key event with keycode=0 and | 
|  | // state=0 for the composition, and the sequence of X11 key events will be | 
|  | // XK_Multi_key, XK_apostrophe, **NoSymbol**, and XK_e.  If the user used | 
|  | // shift key and/or caps lock key, state can be ShiftMask, LockMask or both. | 
|  | // | 
|  | // We have to send these fabricated key events to XIM so it can correctly | 
|  | // handle the character compositions. | 
|  | const unsigned int shift_lock_mask = ShiftMask | LockMask; | 
|  | const bool fabricated_by_xim = | 
|  | xevent->xkey.keycode == 0 && | 
|  | (xevent->xkey.state & ~shift_lock_mask) == 0; | 
|  | const int ime_fabricated_flag = | 
|  | fabricated_by_xim ? ui::EF_IME_FABRICATED_KEY : 0; | 
|  | #endif | 
|  |  | 
|  | return GetEventFlagsFromXState(xevent->xkey.state) | | 
|  | (xevent->xkey.send_event ? ui::EF_FINAL : 0) | | 
|  | (IsKeypadKey(XLookupKeysym(&xevent->xkey, 0)) ? ui::EF_NUMPAD_KEY : 0) | | 
|  | (IsFunctionKey(XLookupKeysym(&xevent->xkey, 0)) ? | 
|  | ui::EF_FUNCTION_KEY : 0) | | 
|  | ime_fabricated_flag; | 
|  | } | 
|  |  | 
|  | int GetEventFlagsFromXGenericEvent(XEvent* xevent) { | 
|  | DCHECK(xevent->type == GenericEvent); | 
|  | XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xevent->xcookie.data); | 
|  | DCHECK((xievent->evtype == XI_KeyPress) || | 
|  | (xievent->evtype == XI_KeyRelease)); | 
|  | return GetEventFlagsFromXState(xievent->mods.effective) | | 
|  | (xevent->xkey.send_event ? ui::EF_FINAL : 0) | | 
|  | (IsKeypadKey( | 
|  | XkbKeycodeToKeysym(xievent->display, xievent->detail, 0, 0)) | 
|  | ? ui::EF_NUMPAD_KEY | 
|  | : 0); | 
|  | } | 
|  |  | 
|  | // Get the event flag for the button in XButtonEvent. During a ButtonPress | 
|  | // event, |state| in XButtonEvent does not include the button that has just been | 
|  | // pressed. Instead |state| contains flags for the buttons (if any) that had | 
|  | // already been pressed before the current button, and |button| stores the most | 
|  | // current pressed button. So, if you press down left mouse button, and while | 
|  | // pressing it down, press down the right mouse button, then for the latter | 
|  | // event, |state| would have Button1Mask set but not Button3Mask, and |button| | 
|  | // would be 3. | 
|  | int GetEventFlagsForButton(int button) { | 
|  | switch (button) { | 
|  | case 1: | 
|  | return ui::EF_LEFT_MOUSE_BUTTON; | 
|  | case 2: | 
|  | return ui::EF_MIDDLE_MOUSE_BUTTON; | 
|  | case 3: | 
|  | return ui::EF_RIGHT_MOUSE_BUTTON; | 
|  | default: | 
|  | return 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | int GetButtonMaskForX2Event(XIDeviceEvent* xievent) { | 
|  | int buttonflags = 0; | 
|  | for (int i = 0; i < 8 * xievent->buttons.mask_len; i++) { | 
|  | if (XIMaskIsSet(xievent->buttons.mask, i)) { | 
|  | int button = (xievent->sourceid == xievent->deviceid) ? | 
|  | ui::DeviceDataManagerX11::GetInstance()->GetMappedButton(i) : i; | 
|  | buttonflags |= GetEventFlagsForButton(button); | 
|  | } | 
|  | } | 
|  | return buttonflags; | 
|  | } | 
|  |  | 
|  | ui::EventType GetTouchEventType(const base::NativeEvent& native_event) { | 
|  | XIDeviceEvent* event = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | #if defined(USE_XI2_MT) | 
|  | switch(event->evtype) { | 
|  | case XI_TouchBegin: | 
|  | return TouchEventIsGeneratedHack(native_event) ? ui::ET_UNKNOWN : | 
|  | ui::ET_TOUCH_PRESSED; | 
|  | case XI_TouchUpdate: | 
|  | return TouchEventIsGeneratedHack(native_event) ? ui::ET_UNKNOWN : | 
|  | ui::ET_TOUCH_MOVED; | 
|  | case XI_TouchEnd: | 
|  | return TouchEventIsGeneratedHack(native_event) ? ui::ET_TOUCH_CANCELLED : | 
|  | ui::ET_TOUCH_RELEASED; | 
|  | } | 
|  | #endif  // defined(USE_XI2_MT) | 
|  |  | 
|  | DCHECK(ui::TouchFactory::GetInstance()->IsTouchDevice(event->sourceid)); | 
|  | switch (event->evtype) { | 
|  | case XI_ButtonPress: | 
|  | return ui::ET_TOUCH_PRESSED; | 
|  | case XI_ButtonRelease: | 
|  | return ui::ET_TOUCH_RELEASED; | 
|  | case XI_Motion: | 
|  | // Should not convert any emulated Motion event from touch device to | 
|  | // touch event. | 
|  | if (!(event->flags & XIPointerEmulated) && | 
|  | GetButtonMaskForX2Event(event)) | 
|  | return ui::ET_TOUCH_MOVED; | 
|  | return ui::ET_UNKNOWN; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | return ui::ET_UNKNOWN; | 
|  | } | 
|  |  | 
|  | double GetTouchParamFromXEvent(XEvent* xev, | 
|  | ui::DeviceDataManagerX11::DataType val, | 
|  | double default_value) { | 
|  | ui::DeviceDataManagerX11::GetInstance()->GetEventData( | 
|  | *xev, val, &default_value); | 
|  | return default_value; | 
|  | } | 
|  |  | 
|  | void ScaleTouchRadius(XEvent* xev, double* radius) { | 
|  | DCHECK_EQ(GenericEvent, xev->type); | 
|  | XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev->xcookie.data); | 
|  | ui::DeviceDataManagerX11::GetInstance()->ApplyTouchRadiusScale( | 
|  | xiev->sourceid, radius); | 
|  | } | 
|  |  | 
|  | unsigned int UpdateX11EventFlags(int ui_flags, unsigned int old_x_flags) { | 
|  | static struct { | 
|  | int ui; | 
|  | int x; | 
|  | } flags[] = { | 
|  | {ui::EF_CONTROL_DOWN, ControlMask}, | 
|  | {ui::EF_SHIFT_DOWN, ShiftMask}, | 
|  | {ui::EF_ALT_DOWN, Mod1Mask}, | 
|  | {ui::EF_CAPS_LOCK_DOWN, LockMask}, | 
|  | {ui::EF_ALTGR_DOWN, Mod5Mask}, | 
|  | {ui::EF_COMMAND_DOWN, Mod4Mask}, | 
|  | {ui::EF_MOD3_DOWN, Mod3Mask}, | 
|  | {ui::EF_NUMPAD_KEY, Mod2Mask}, | 
|  | {ui::EF_LEFT_MOUSE_BUTTON, Button1Mask}, | 
|  | {ui::EF_MIDDLE_MOUSE_BUTTON, Button2Mask}, | 
|  | {ui::EF_RIGHT_MOUSE_BUTTON, Button3Mask}, | 
|  | }; | 
|  | unsigned int new_x_flags = old_x_flags; | 
|  | for (size_t i = 0; i < arraysize(flags); ++i) { | 
|  | if (ui_flags & flags[i].ui) | 
|  | new_x_flags |= flags[i].x; | 
|  | else | 
|  | new_x_flags &= ~flags[i].x; | 
|  | } | 
|  | return new_x_flags; | 
|  | } | 
|  |  | 
|  | unsigned int UpdateX11EventButton(int ui_flag, unsigned int old_x_button) { | 
|  | switch (ui_flag) { | 
|  | case ui::EF_LEFT_MOUSE_BUTTON: | 
|  | return Button1; | 
|  | case ui::EF_MIDDLE_MOUSE_BUTTON: | 
|  | return Button2; | 
|  | case ui::EF_RIGHT_MOUSE_BUTTON: | 
|  | return Button3; | 
|  | default: | 
|  | return old_x_button; | 
|  | } | 
|  | NOTREACHED(); | 
|  | } | 
|  |  | 
|  | bool GetGestureTimes(const base::NativeEvent& native_event, | 
|  | double* start_time, | 
|  | double* end_time) { | 
|  | if (!ui::DeviceDataManagerX11::GetInstance()->HasGestureTimes(native_event)) | 
|  | return false; | 
|  |  | 
|  | double start_time_, end_time_; | 
|  | if (!start_time) | 
|  | start_time = &start_time_; | 
|  | if (!end_time) | 
|  | end_time = &end_time_; | 
|  |  | 
|  | ui::DeviceDataManagerX11::GetInstance()->GetGestureTimes( | 
|  | native_event, start_time, end_time); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace ui { | 
|  |  | 
|  | void UpdateDeviceList() { | 
|  | XDisplay* display = gfx::GetXDisplay(); | 
|  | DeviceListCacheX::GetInstance()->UpdateDeviceList(display); | 
|  | TouchFactory::GetInstance()->UpdateDeviceList(display); | 
|  | DeviceDataManagerX11::GetInstance()->UpdateDeviceList(display); | 
|  | } | 
|  |  | 
|  | EventType EventTypeFromNative(const base::NativeEvent& native_event) { | 
|  | // Allow the DeviceDataManager to block the event. If blocked return | 
|  | // ET_UNKNOWN as the type so this event will not be further processed. | 
|  | // NOTE: During some events unittests there is no device data manager. | 
|  | if (DeviceDataManager::HasInstance() && | 
|  | static_cast<DeviceDataManagerX11*>(DeviceDataManager::GetInstance())-> | 
|  | IsEventBlocked(native_event)) { | 
|  | return ET_UNKNOWN; | 
|  | } | 
|  |  | 
|  | switch (native_event->type) { | 
|  | case KeyPress: | 
|  | return ET_KEY_PRESSED; | 
|  | case KeyRelease: | 
|  | return ET_KEY_RELEASED; | 
|  | case ButtonPress: | 
|  | if (static_cast<int>(native_event->xbutton.button) >= kMinWheelButton && | 
|  | static_cast<int>(native_event->xbutton.button) <= kMaxWheelButton) | 
|  | return ET_MOUSEWHEEL; | 
|  | return ET_MOUSE_PRESSED; | 
|  | case ButtonRelease: | 
|  | // Drop wheel events; we should've already scrolled on the press. | 
|  | if (static_cast<int>(native_event->xbutton.button) >= kMinWheelButton && | 
|  | static_cast<int>(native_event->xbutton.button) <= kMaxWheelButton) | 
|  | return ET_UNKNOWN; | 
|  | return ET_MOUSE_RELEASED; | 
|  | case MotionNotify: | 
|  | if (native_event->xmotion.state & | 
|  | (Button1Mask | Button2Mask | Button3Mask)) | 
|  | return ET_MOUSE_DRAGGED; | 
|  | return ET_MOUSE_MOVED; | 
|  | case EnterNotify: | 
|  | // The standard on Windows is to send a MouseMove event when the mouse | 
|  | // first enters a window instead of sending a special mouse enter event. | 
|  | // To be consistent we follow the same style. | 
|  | return ET_MOUSE_MOVED; | 
|  | case LeaveNotify: | 
|  | return ET_MOUSE_EXITED; | 
|  | case GenericEvent: { | 
|  | TouchFactory* factory = TouchFactory::GetInstance(); | 
|  | if (!factory->ShouldProcessXI2Event(native_event)) | 
|  | return ET_UNKNOWN; | 
|  |  | 
|  | XIDeviceEvent* xievent = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  |  | 
|  | // This check works only for master and floating slave devices. That is | 
|  | // why it is necessary to check for the XI_Touch* events in the following | 
|  | // switch statement to account for attached-slave touchscreens. | 
|  | if (factory->IsTouchDevice(xievent->sourceid)) | 
|  | return GetTouchEventType(native_event); | 
|  |  | 
|  | switch (xievent->evtype) { | 
|  | case XI_TouchBegin: | 
|  | return ui::ET_TOUCH_PRESSED; | 
|  | case XI_TouchUpdate: | 
|  | return ui::ET_TOUCH_MOVED; | 
|  | case XI_TouchEnd: | 
|  | return ui::ET_TOUCH_RELEASED; | 
|  | case XI_ButtonPress: { | 
|  | int button = EventButtonFromNative(native_event); | 
|  | if (button >= kMinWheelButton && button <= kMaxWheelButton) | 
|  | return ET_MOUSEWHEEL; | 
|  | return ET_MOUSE_PRESSED; | 
|  | } | 
|  | case XI_ButtonRelease: { | 
|  | int button = EventButtonFromNative(native_event); | 
|  | // Drop wheel events; we should've already scrolled on the press. | 
|  | if (button >= kMinWheelButton && button <= kMaxWheelButton) | 
|  | return ET_UNKNOWN; | 
|  | return ET_MOUSE_RELEASED; | 
|  | } | 
|  | case XI_Motion: { | 
|  | bool is_cancel; | 
|  | DeviceDataManagerX11* devices = DeviceDataManagerX11::GetInstance(); | 
|  | if (GetFlingData(native_event, NULL, NULL, NULL, NULL, &is_cancel)) | 
|  | return is_cancel ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START; | 
|  | if (devices->IsScrollEvent(native_event)) { | 
|  | return devices->IsTouchpadXInputEvent(native_event) ? ET_SCROLL | 
|  | : ET_MOUSEWHEEL; | 
|  | } | 
|  | if (devices->IsCMTMetricsEvent(native_event)) | 
|  | return ET_UMA_DATA; | 
|  | if (GetButtonMaskForX2Event(xievent)) | 
|  | return ET_MOUSE_DRAGGED; | 
|  | return ET_MOUSE_MOVED; | 
|  | } | 
|  | case XI_KeyPress: | 
|  | return ET_KEY_PRESSED; | 
|  | case XI_KeyRelease: | 
|  | return ET_KEY_RELEASED; | 
|  | } | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return ET_UNKNOWN; | 
|  | } | 
|  |  | 
|  | int EventFlagsFromNative(const base::NativeEvent& native_event) { | 
|  | switch (native_event->type) { | 
|  | case KeyPress: | 
|  | case KeyRelease: { | 
|  | XModifierStateWatcher::GetInstance()->UpdateStateFromXEvent(native_event); | 
|  | return GetEventFlagsFromXKeyEvent(native_event); | 
|  | } | 
|  | case ButtonPress: | 
|  | case ButtonRelease: { | 
|  | int flags = GetEventFlagsFromXState(native_event->xbutton.state); | 
|  | const EventType type = EventTypeFromNative(native_event); | 
|  | if (type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) | 
|  | flags |= GetEventFlagsForButton(native_event->xbutton.button); | 
|  | return flags; | 
|  | } | 
|  | case EnterNotify: | 
|  | case LeaveNotify: | 
|  | return GetEventFlagsFromXState(native_event->xcrossing.state); | 
|  | case MotionNotify: | 
|  | return GetEventFlagsFromXState(native_event->xmotion.state); | 
|  | case GenericEvent: { | 
|  | XIDeviceEvent* xievent = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  |  | 
|  | switch (xievent->evtype) { | 
|  | #if defined(USE_XI2_MT) | 
|  | case XI_TouchBegin: | 
|  | case XI_TouchUpdate: | 
|  | case XI_TouchEnd: | 
|  | return GetButtonMaskForX2Event(xievent) | | 
|  | GetEventFlagsFromXState(xievent->mods.effective) | | 
|  | GetEventFlagsFromXState( | 
|  | XModifierStateWatcher::GetInstance()->state()); | 
|  | break; | 
|  | #endif | 
|  | case XI_ButtonPress: | 
|  | case XI_ButtonRelease: { | 
|  | const bool touch = | 
|  | TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid); | 
|  | int flags = GetButtonMaskForX2Event(xievent) | | 
|  | GetEventFlagsFromXState(xievent->mods.effective); | 
|  | if (touch) { | 
|  | flags |= GetEventFlagsFromXState( | 
|  | XModifierStateWatcher::GetInstance()->state()); | 
|  | } | 
|  |  | 
|  | const EventType type = EventTypeFromNative(native_event); | 
|  | int button = EventButtonFromNative(native_event); | 
|  | if ((type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) && !touch) | 
|  | flags |= GetEventFlagsForButton(button); | 
|  | return flags; | 
|  | } | 
|  | case XI_Motion: | 
|  | return GetButtonMaskForX2Event(xievent) | | 
|  | GetEventFlagsFromXState(xievent->mods.effective); | 
|  | case XI_KeyPress: | 
|  | case XI_KeyRelease: { | 
|  | XModifierStateWatcher::GetInstance()->UpdateStateFromXEvent( | 
|  | native_event); | 
|  | return GetEventFlagsFromXGenericEvent(native_event); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | base::TimeDelta EventTimeFromNative(const base::NativeEvent& native_event) { | 
|  | switch(native_event->type) { | 
|  | case KeyPress: | 
|  | case KeyRelease: | 
|  | return base::TimeDelta::FromMilliseconds(native_event->xkey.time); | 
|  | case ButtonPress: | 
|  | case ButtonRelease: | 
|  | return base::TimeDelta::FromMilliseconds(native_event->xbutton.time); | 
|  | break; | 
|  | case MotionNotify: | 
|  | return base::TimeDelta::FromMilliseconds(native_event->xmotion.time); | 
|  | break; | 
|  | case EnterNotify: | 
|  | case LeaveNotify: | 
|  | return base::TimeDelta::FromMilliseconds(native_event->xcrossing.time); | 
|  | break; | 
|  | case GenericEvent: { | 
|  | double start, end; | 
|  | double touch_timestamp; | 
|  | if (GetGestureTimes(native_event, &start, &end)) { | 
|  | // If the driver supports gesture times, use them. | 
|  | return base::TimeDelta::FromMicroseconds(end * 1000000); | 
|  | } else if (DeviceDataManagerX11::GetInstance()->GetEventData( | 
|  | *native_event, | 
|  | DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP, | 
|  | &touch_timestamp)) { | 
|  | return base::TimeDelta::FromMicroseconds(touch_timestamp * 1000000); | 
|  | } else { | 
|  | XIDeviceEvent* xide = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | return base::TimeDelta::FromMilliseconds(xide->time); | 
|  | } | 
|  | break; | 
|  | } | 
|  | } | 
|  | NOTREACHED(); | 
|  | return base::TimeDelta(); | 
|  | } | 
|  |  | 
|  | gfx::Point EventLocationFromNative(const base::NativeEvent& native_event) { | 
|  | switch (native_event->type) { | 
|  | case EnterNotify: | 
|  | case LeaveNotify: | 
|  | return gfx::Point(native_event->xcrossing.x, native_event->xcrossing.y); | 
|  | case ButtonPress: | 
|  | case ButtonRelease: | 
|  | return gfx::Point(native_event->xbutton.x, native_event->xbutton.y); | 
|  | case MotionNotify: | 
|  | return gfx::Point(native_event->xmotion.x, native_event->xmotion.y); | 
|  | case GenericEvent: { | 
|  | XIDeviceEvent* xievent = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | float x = xievent->event_x; | 
|  | float y = xievent->event_y; | 
|  | #if defined(OS_CHROMEOS) | 
|  | switch (xievent->evtype) { | 
|  | case XI_TouchBegin: | 
|  | case XI_TouchUpdate: | 
|  | case XI_TouchEnd: | 
|  | ui::DeviceDataManagerX11::GetInstance()->ApplyTouchTransformer( | 
|  | xievent->deviceid, &x, &y); | 
|  | break; | 
|  | default: | 
|  | break; | 
|  | } | 
|  | #endif  // defined(OS_CHROMEOS) | 
|  | return gfx::Point(static_cast<int>(x), static_cast<int>(y)); | 
|  | } | 
|  | } | 
|  | return gfx::Point(); | 
|  | } | 
|  |  | 
|  | gfx::Point EventSystemLocationFromNative( | 
|  | const base::NativeEvent& native_event) { | 
|  | switch (native_event->type) { | 
|  | case EnterNotify: | 
|  | case LeaveNotify: { | 
|  | return gfx::Point(native_event->xcrossing.x_root, | 
|  | native_event->xcrossing.y_root); | 
|  | } | 
|  | case ButtonPress: | 
|  | case ButtonRelease: { | 
|  | return gfx::Point(native_event->xbutton.x_root, | 
|  | native_event->xbutton.y_root); | 
|  | } | 
|  | case MotionNotify: { | 
|  | return gfx::Point(native_event->xmotion.x_root, | 
|  | native_event->xmotion.y_root); | 
|  | } | 
|  | case GenericEvent: { | 
|  | XIDeviceEvent* xievent = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | return gfx::Point(xievent->root_x, xievent->root_y); | 
|  | } | 
|  | } | 
|  |  | 
|  | return gfx::Point(); | 
|  | } | 
|  |  | 
|  | int EventButtonFromNative(const base::NativeEvent& native_event) { | 
|  | CHECK_EQ(GenericEvent, native_event->type); | 
|  | XIDeviceEvent* xievent = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | int button = xievent->detail; | 
|  |  | 
|  | return (xievent->sourceid == xievent->deviceid) ? | 
|  | DeviceDataManagerX11::GetInstance()->GetMappedButton(button) : button; | 
|  | } | 
|  |  | 
|  | KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) { | 
|  | return KeyboardCodeFromXKeyEvent(native_event); | 
|  | } | 
|  |  | 
|  | const char* CodeFromNative(const base::NativeEvent& native_event) { | 
|  | return CodeFromXEvent(native_event); | 
|  | } | 
|  |  | 
|  | uint32 PlatformKeycodeFromNative(const base::NativeEvent& native_event) { | 
|  | XKeyEvent* xkey = NULL; | 
|  | XEvent xkey_from_xi2; | 
|  | switch (native_event->type) { | 
|  | case KeyPress: | 
|  | case KeyRelease: | 
|  | xkey = &native_event->xkey; | 
|  | break; | 
|  | case GenericEvent: { | 
|  | XIDeviceEvent* xievent = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | switch (xievent->evtype) { | 
|  | case XI_KeyPress: | 
|  | case XI_KeyRelease: | 
|  | // Build an XKeyEvent corresponding to the XI2 event, | 
|  | // so that we can call XLookupString on it. | 
|  | InitXKeyEventFromXIDeviceEvent(*native_event, &xkey_from_xi2); | 
|  | xkey = &xkey_from_xi2.xkey; | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | break; | 
|  | } | 
|  | default: | 
|  | NOTREACHED(); | 
|  | break; | 
|  | } | 
|  | KeySym keysym = XK_VoidSymbol; | 
|  | if (xkey) | 
|  | XLookupString(xkey, NULL, 0, &keysym, NULL); | 
|  | return keysym; | 
|  | } | 
|  |  | 
|  | bool IsCharFromNative(const base::NativeEvent& native_event) { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int GetChangedMouseButtonFlagsFromNative( | 
|  | const base::NativeEvent& native_event) { | 
|  | switch (native_event->type) { | 
|  | case ButtonPress: | 
|  | case ButtonRelease: | 
|  | return GetEventFlagsFromXState(native_event->xbutton.state); | 
|  | case GenericEvent: { | 
|  | XIDeviceEvent* xievent = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
|  | switch (xievent->evtype) { | 
|  | case XI_ButtonPress: | 
|  | case XI_ButtonRelease: | 
|  | return GetEventFlagsForButton(EventButtonFromNative(native_event)); | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) { | 
|  | float x_offset, y_offset; | 
|  | if (GetScrollOffsets( | 
|  | native_event, &x_offset, &y_offset, NULL, NULL, NULL)) { | 
|  | return gfx::Vector2d(static_cast<int>(x_offset), | 
|  | static_cast<int>(y_offset)); | 
|  | } | 
|  |  | 
|  | int button = native_event->type == GenericEvent ? | 
|  | EventButtonFromNative(native_event) : native_event->xbutton.button; | 
|  |  | 
|  | switch (button) { | 
|  | case 4: | 
|  | return gfx::Vector2d(0, kWheelScrollAmount); | 
|  | case 5: | 
|  | return gfx::Vector2d(0, -kWheelScrollAmount); | 
|  | case 6: | 
|  | return gfx::Vector2d(kWheelScrollAmount, 0); | 
|  | case 7: | 
|  | return gfx::Vector2d(-kWheelScrollAmount, 0); | 
|  | default: | 
|  | return gfx::Vector2d(); | 
|  | } | 
|  | } | 
|  |  | 
|  | base::NativeEvent CopyNativeEvent(const base::NativeEvent& event) { | 
|  | if (!event || event->type == GenericEvent) | 
|  | return NULL; | 
|  | XEvent* copy = new XEvent; | 
|  | *copy = *event; | 
|  | return copy; | 
|  | } | 
|  |  | 
|  | void ReleaseCopiedNativeEvent(const base::NativeEvent& event) { | 
|  | delete event; | 
|  | } | 
|  |  | 
|  | void IncrementTouchIdRefCount(const base::NativeEvent& xev) { | 
|  | ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance(); | 
|  | double tracking_id; | 
|  | if (!manager->GetEventData( | 
|  | *xev, ui::DeviceDataManagerX11::DT_TOUCH_TRACKING_ID, &tracking_id)) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | 
|  | factory->AcquireSlotForTrackingID(tracking_id); | 
|  | } | 
|  |  | 
|  | void ClearTouchIdIfReleased(const base::NativeEvent& xev) { | 
|  | ui::EventType type = ui::EventTypeFromNative(xev); | 
|  | if (type == ui::ET_TOUCH_CANCELLED || | 
|  | type == ui::ET_TOUCH_RELEASED) { | 
|  | ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | 
|  | ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance(); | 
|  | double tracking_id; | 
|  | if (manager->GetEventData( | 
|  | *xev, ui::DeviceDataManagerX11::DT_TOUCH_TRACKING_ID, &tracking_id)) { | 
|  | factory->ReleaseSlotForTrackingID(tracking_id); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | int GetTouchId(const base::NativeEvent& xev) { | 
|  | double slot = 0; | 
|  | ui::DeviceDataManagerX11* manager = ui::DeviceDataManagerX11::GetInstance(); | 
|  | double tracking_id; | 
|  | if (!manager->GetEventData( | 
|  | *xev, ui::DeviceDataManagerX11::DT_TOUCH_TRACKING_ID, &tracking_id)) { | 
|  | LOG(ERROR) << "Could not get the tracking ID for the event. Using 0."; | 
|  | } else { | 
|  | ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | 
|  | slot = factory->GetSlotForTrackingID(tracking_id); | 
|  | } | 
|  | return slot; | 
|  | } | 
|  |  | 
|  | float GetTouchRadiusX(const base::NativeEvent& native_event) { | 
|  | double radius = GetTouchParamFromXEvent(native_event, | 
|  | ui::DeviceDataManagerX11::DT_TOUCH_MAJOR, 0.0) / 2.0; | 
|  | ScaleTouchRadius(native_event, &radius); | 
|  | return radius; | 
|  | } | 
|  |  | 
|  | float GetTouchRadiusY(const base::NativeEvent& native_event) { | 
|  | double radius = GetTouchParamFromXEvent(native_event, | 
|  | ui::DeviceDataManagerX11::DT_TOUCH_MINOR, 0.0) / 2.0; | 
|  | ScaleTouchRadius(native_event, &radius); | 
|  | return radius; | 
|  | } | 
|  |  | 
|  | float GetTouchAngle(const base::NativeEvent& native_event) { | 
|  | return GetTouchParamFromXEvent(native_event, | 
|  | ui::DeviceDataManagerX11::DT_TOUCH_ORIENTATION, 0.0) / 2.0; | 
|  | } | 
|  |  | 
|  | float GetTouchForce(const base::NativeEvent& native_event) { | 
|  | double force = 0.0; | 
|  | force = GetTouchParamFromXEvent(native_event, | 
|  | ui::DeviceDataManagerX11::DT_TOUCH_PRESSURE, 0.0); | 
|  | unsigned int deviceid = | 
|  | static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; | 
|  | // Force is normalized to fall into [0, 1] | 
|  | if (!ui::DeviceDataManagerX11::GetInstance()->NormalizeData( | 
|  | deviceid, ui::DeviceDataManagerX11::DT_TOUCH_PRESSURE, &force)) | 
|  | force = 0.0; | 
|  | return force; | 
|  | } | 
|  |  | 
|  | bool GetScrollOffsets(const base::NativeEvent& native_event, | 
|  | float* x_offset, | 
|  | float* y_offset, | 
|  | float* x_offset_ordinal, | 
|  | float* y_offset_ordinal, | 
|  | int* finger_count) { | 
|  | if (!DeviceDataManagerX11::GetInstance()->IsScrollEvent(native_event)) | 
|  | return false; | 
|  |  | 
|  | // Temp values to prevent passing NULLs to DeviceDataManager. | 
|  | float x_offset_, y_offset_; | 
|  | float x_offset_ordinal_, y_offset_ordinal_; | 
|  | int finger_count_; | 
|  | if (!x_offset) | 
|  | x_offset = &x_offset_; | 
|  | if (!y_offset) | 
|  | y_offset = &y_offset_; | 
|  | if (!x_offset_ordinal) | 
|  | x_offset_ordinal = &x_offset_ordinal_; | 
|  | if (!y_offset_ordinal) | 
|  | y_offset_ordinal = &y_offset_ordinal_; | 
|  | if (!finger_count) | 
|  | finger_count = &finger_count_; | 
|  |  | 
|  | DeviceDataManagerX11::GetInstance()->GetScrollOffsets( | 
|  | native_event, | 
|  | x_offset, y_offset, | 
|  | x_offset_ordinal, y_offset_ordinal, | 
|  | finger_count); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | bool GetFlingData(const base::NativeEvent& native_event, | 
|  | float* vx, | 
|  | float* vy, | 
|  | float* vx_ordinal, | 
|  | float* vy_ordinal, | 
|  | bool* is_cancel) { | 
|  | if (!DeviceDataManagerX11::GetInstance()->IsFlingEvent(native_event)) | 
|  | return false; | 
|  |  | 
|  | float vx_, vy_; | 
|  | float vx_ordinal_, vy_ordinal_; | 
|  | bool is_cancel_; | 
|  | if (!vx) | 
|  | vx = &vx_; | 
|  | if (!vy) | 
|  | vy = &vy_; | 
|  | if (!vx_ordinal) | 
|  | vx_ordinal = &vx_ordinal_; | 
|  | if (!vy_ordinal) | 
|  | vy_ordinal = &vy_ordinal_; | 
|  | if (!is_cancel) | 
|  | is_cancel = &is_cancel_; | 
|  |  | 
|  | DeviceDataManagerX11::GetInstance()->GetFlingData( | 
|  | native_event, vx, vy, vx_ordinal, vy_ordinal, is_cancel); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | void UpdateX11EventForFlags(Event* event) { | 
|  | XEvent* xev = event->native_event(); | 
|  | if (!xev) | 
|  | return; | 
|  | switch (xev->type) { | 
|  | case KeyPress: | 
|  | case KeyRelease: | 
|  | xev->xkey.state = UpdateX11EventFlags(event->flags(), xev->xkey.state); | 
|  | break; | 
|  | case ButtonPress: | 
|  | case ButtonRelease: | 
|  | xev->xbutton.state = | 
|  | UpdateX11EventFlags(event->flags(), xev->xbutton.state); | 
|  | break; | 
|  | case GenericEvent: { | 
|  | XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data); | 
|  | DCHECK(xievent); | 
|  | xievent->mods.effective = | 
|  | UpdateX11EventFlags(event->flags(), xievent->mods.effective); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | void UpdateX11EventForChangedButtonFlags(MouseEvent* event) { | 
|  | XEvent* xev = event->native_event(); | 
|  | if (!xev) | 
|  | return; | 
|  | switch (xev->type) { | 
|  | case ButtonPress: | 
|  | case ButtonRelease: | 
|  | xev->xbutton.button = UpdateX11EventButton(event->changed_button_flags(), | 
|  | xev->xbutton.button); | 
|  | break; | 
|  | case GenericEvent: { | 
|  | XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data); | 
|  | CHECK(xievent && (xievent->evtype == XI_ButtonPress || | 
|  | xievent->evtype == XI_ButtonRelease)); | 
|  | xievent->detail = | 
|  | UpdateX11EventButton(event->changed_button_flags(), xievent->detail); | 
|  | break; | 
|  | } | 
|  | default: | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | }  // namespace ui |