| // Copyright 2014 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/devices/x11/device_data_manager_x11.h" | 
 |  | 
 | #include <X11/extensions/XInput.h> | 
 | #include <X11/extensions/XInput2.h> | 
 | #include <X11/Xlib.h> | 
 |  | 
 | #include <utility> | 
 |  | 
 | #include "base/logging.h" | 
 | #include "base/memory/singleton.h" | 
 | #include "base/sys_info.h" | 
 | #include "ui/events/devices/keyboard_device.h" | 
 | #include "ui/events/devices/x11/device_list_cache_x11.h" | 
 | #include "ui/events/devices/x11/touch_factory_x11.h" | 
 | #include "ui/events/event_constants.h" | 
 | #include "ui/events/event_switches.h" | 
 | #include "ui/events/keycodes/keyboard_code_conversion_x.h" | 
 | #include "ui/gfx/display.h" | 
 | #include "ui/gfx/geometry/point3_f.h" | 
 | #include "ui/gfx/x/x11_types.h" | 
 |  | 
 | // XIScrollClass was introduced in XI 2.1 so we need to define it here | 
 | // for backward-compatibility with older versions of XInput. | 
 | #if !defined(XIScrollClass) | 
 | #define XIScrollClass 3 | 
 | #endif | 
 |  | 
 | // Multi-touch support was introduced in XI 2.2. Add XI event types here | 
 | // for backward-compatibility with older versions of XInput. | 
 | #if !defined(XI_TouchBegin) | 
 | #define XI_TouchBegin  18 | 
 | #define XI_TouchUpdate 19 | 
 | #define XI_TouchEnd    20 | 
 | #endif | 
 |  | 
 | // Copied from xserver-properties.h | 
 | #define AXIS_LABEL_PROP_REL_HWHEEL "Rel Horiz Wheel" | 
 | #define AXIS_LABEL_PROP_REL_WHEEL "Rel Vert Wheel" | 
 |  | 
 | // CMT specific timings | 
 | #define AXIS_LABEL_PROP_ABS_DBL_START_TIME "Abs Dbl Start Timestamp" | 
 | #define AXIS_LABEL_PROP_ABS_DBL_END_TIME   "Abs Dbl End Timestamp" | 
 |  | 
 | // Ordinal values | 
 | #define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X   "Abs Dbl Ordinal X" | 
 | #define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y   "Abs Dbl Ordinal Y" | 
 |  | 
 | // Fling properties | 
 | #define AXIS_LABEL_PROP_ABS_DBL_FLING_VX   "Abs Dbl Fling X Velocity" | 
 | #define AXIS_LABEL_PROP_ABS_DBL_FLING_VY   "Abs Dbl Fling Y Velocity" | 
 | #define AXIS_LABEL_PROP_ABS_FLING_STATE   "Abs Fling State" | 
 |  | 
 | #define AXIS_LABEL_PROP_ABS_FINGER_COUNT   "Abs Finger Count" | 
 |  | 
 | // Cros metrics gesture from touchpad | 
 | #define AXIS_LABEL_PROP_ABS_METRICS_TYPE      "Abs Metrics Type" | 
 | #define AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA1 "Abs Dbl Metrics Data 1" | 
 | #define AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA2 "Abs Dbl Metrics Data 2" | 
 |  | 
 | // Touchscreen multi-touch | 
 | #define AXIS_LABEL_ABS_MT_TOUCH_MAJOR "Abs MT Touch Major" | 
 | #define AXIS_LABEL_ABS_MT_TOUCH_MINOR "Abs MT Touch Minor" | 
 | #define AXIS_LABEL_ABS_MT_ORIENTATION "Abs MT Orientation" | 
 | #define AXIS_LABEL_ABS_MT_PRESSURE    "Abs MT Pressure" | 
 | #define AXIS_LABEL_ABS_MT_POSITION_X  "Abs MT Position X" | 
 | #define AXIS_LABEL_ABS_MT_POSITION_Y  "Abs MT Position Y" | 
 | #define AXIS_LABEL_ABS_MT_TRACKING_ID "Abs MT Tracking ID" | 
 | #define AXIS_LABEL_TOUCH_TIMESTAMP    "Touch Timestamp" | 
 |  | 
 | // When you add new data types, please make sure the order here is aligned | 
 | // with the order in the DataType enum in the header file because we assume | 
 | // they are in sync when updating the device list (see UpdateDeviceList). | 
 | const char* kCachedAtoms[] = { | 
 |   AXIS_LABEL_PROP_REL_HWHEEL, | 
 |   AXIS_LABEL_PROP_REL_WHEEL, | 
 |   AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X, | 
 |   AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y, | 
 |   AXIS_LABEL_PROP_ABS_DBL_START_TIME, | 
 |   AXIS_LABEL_PROP_ABS_DBL_END_TIME, | 
 |   AXIS_LABEL_PROP_ABS_DBL_FLING_VX, | 
 |   AXIS_LABEL_PROP_ABS_DBL_FLING_VY, | 
 |   AXIS_LABEL_PROP_ABS_FLING_STATE, | 
 |   AXIS_LABEL_PROP_ABS_METRICS_TYPE, | 
 |   AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA1, | 
 |   AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA2, | 
 |   AXIS_LABEL_PROP_ABS_FINGER_COUNT, | 
 |   AXIS_LABEL_ABS_MT_TOUCH_MAJOR, | 
 |   AXIS_LABEL_ABS_MT_TOUCH_MINOR, | 
 |   AXIS_LABEL_ABS_MT_ORIENTATION, | 
 |   AXIS_LABEL_ABS_MT_PRESSURE, | 
 |   AXIS_LABEL_ABS_MT_POSITION_X, | 
 |   AXIS_LABEL_ABS_MT_POSITION_Y, | 
 |   AXIS_LABEL_ABS_MT_TRACKING_ID, | 
 |   AXIS_LABEL_TOUCH_TIMESTAMP, | 
 |  | 
 |   NULL | 
 | }; | 
 |  | 
 | // Constants for checking if a data type lies in the range of CMT/Touch data | 
 | // types. | 
 | const int kCMTDataTypeStart = ui::DeviceDataManagerX11::DT_CMT_SCROLL_X; | 
 | const int kCMTDataTypeEnd = ui::DeviceDataManagerX11::DT_CMT_FINGER_COUNT; | 
 | const int kTouchDataTypeStart = ui::DeviceDataManagerX11::DT_TOUCH_MAJOR; | 
 | const int kTouchDataTypeEnd = ui::DeviceDataManagerX11::DT_TOUCH_RAW_TIMESTAMP; | 
 |  | 
 | namespace ui { | 
 |  | 
 | namespace { | 
 |  | 
 | bool DeviceHasId(const ui::InputDevice input_device, int id) { | 
 |   return input_device.id == id; | 
 | } | 
 |  | 
 | }  // namespace | 
 |  | 
 | bool DeviceDataManagerX11::IsCMTDataType(const int type) { | 
 |   return (type >= kCMTDataTypeStart) && (type <= kCMTDataTypeEnd); | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsTouchDataType(const int type) { | 
 |   return (type >= kTouchDataTypeStart) && (type <= kTouchDataTypeEnd); | 
 | } | 
 |  | 
 | // static | 
 | void DeviceDataManagerX11::CreateInstance() { | 
 |   if (instance()) | 
 |     return; | 
 |  | 
 |   new DeviceDataManagerX11(); | 
 | } | 
 |  | 
 | // static | 
 | DeviceDataManagerX11* DeviceDataManagerX11::GetInstance() { | 
 |   return static_cast<DeviceDataManagerX11*>(DeviceDataManager::GetInstance()); | 
 | } | 
 |  | 
 | DeviceDataManagerX11::DeviceDataManagerX11() | 
 |     : xi_opcode_(-1), | 
 |       atom_cache_(gfx::GetXDisplay(), kCachedAtoms), | 
 |       button_map_count_(0) { | 
 |   CHECK(gfx::GetXDisplay()); | 
 |   InitializeXInputInternal(); | 
 |  | 
 |   // Make sure the sizes of enum and kCachedAtoms are aligned. | 
 |   CHECK(arraysize(kCachedAtoms) == static_cast<size_t>(DT_LAST_ENTRY) + 1); | 
 |   UpdateDeviceList(gfx::GetXDisplay()); | 
 |   UpdateButtonMap(); | 
 | } | 
 |  | 
 | DeviceDataManagerX11::~DeviceDataManagerX11() { | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::InitializeXInputInternal() { | 
 |   // Check if XInput is available on the system. | 
 |   xi_opcode_ = -1; | 
 |   int opcode, event, error; | 
 |   if (!XQueryExtension( | 
 |       gfx::GetXDisplay(), "XInputExtension", &opcode, &event, &error)) { | 
 |     VLOG(1) << "X Input extension not available: error=" << error; | 
 |     return false; | 
 |   } | 
 |  | 
 |   // Check the XInput version. | 
 |   int major = 2, minor = 2; | 
 |   if (XIQueryVersion(gfx::GetXDisplay(), &major, &minor) == BadRequest) { | 
 |     VLOG(1) << "XInput2 not supported in the server."; | 
 |     return false; | 
 |   } | 
 |   if (major < 2 || (major == 2 && minor < 2)) { | 
 |     DVLOG(1) << "XI version on server is " << major << "." << minor << ". " | 
 |             << "But 2.2 is required."; | 
 |     return false; | 
 |   } | 
 |  | 
 |   xi_opcode_ = opcode; | 
 |   CHECK_NE(-1, xi_opcode_); | 
 |  | 
 |   // Possible XI event types for XIDeviceEvent. See the XI2 protocol | 
 |   // specification. | 
 |   xi_device_event_types_[XI_KeyPress] = true; | 
 |   xi_device_event_types_[XI_KeyRelease] = true; | 
 |   xi_device_event_types_[XI_ButtonPress] = true; | 
 |   xi_device_event_types_[XI_ButtonRelease] = true; | 
 |   xi_device_event_types_[XI_Motion] = true; | 
 |   // Multi-touch support was introduced in XI 2.2. | 
 |   if (minor >= 2) { | 
 |     xi_device_event_types_[XI_TouchBegin] = true; | 
 |     xi_device_event_types_[XI_TouchUpdate] = true; | 
 |     xi_device_event_types_[XI_TouchEnd] = true; | 
 |   } | 
 |   return true; | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsXInput2Available() const { | 
 |   return xi_opcode_ != -1; | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::UpdateDeviceList(Display* display) { | 
 |   cmt_devices_.reset(); | 
 |   touchpads_.reset(); | 
 |   master_pointers_.clear(); | 
 |   for (int i = 0; i < kMaxDeviceNum; ++i) { | 
 |     valuator_count_[i] = 0; | 
 |     valuator_lookup_[i].clear(); | 
 |     data_type_lookup_[i].clear(); | 
 |     valuator_min_[i].clear(); | 
 |     valuator_max_[i].clear(); | 
 |     for (int j = 0; j < kMaxSlotNum; j++) | 
 |       last_seen_valuator_[i][j].clear(); | 
 |   } | 
 |  | 
 |   // Find all the touchpad devices. | 
 |   const XDeviceList& dev_list = | 
 |       ui::DeviceListCacheX11::GetInstance()->GetXDeviceList(display); | 
 |   Atom xi_touchpad = XInternAtom(display, XI_TOUCHPAD, false); | 
 |   for (int i = 0; i < dev_list.count; ++i) | 
 |     if (dev_list[i].type == xi_touchpad) | 
 |       touchpads_[dev_list[i].id] = true; | 
 |  | 
 |   if (!IsXInput2Available()) | 
 |     return; | 
 |  | 
 |   // Update the structs with new valuator information | 
 |   const XIDeviceList& info_list = | 
 |       ui::DeviceListCacheX11::GetInstance()->GetXI2DeviceList(display); | 
 |   Atom atoms[DT_LAST_ENTRY]; | 
 |   for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type) | 
 |     atoms[data_type] = atom_cache_.GetAtom(kCachedAtoms[data_type]); | 
 |  | 
 |   for (int i = 0; i < info_list.count; ++i) { | 
 |     const XIDeviceInfo& info = info_list[i]; | 
 |  | 
 |     if (info.use == XIMasterPointer) | 
 |       master_pointers_.push_back(info.deviceid); | 
 |  | 
 |     // We currently handle only slave, non-keyboard devices | 
 |     if (info.use != XISlavePointer && info.use != XIFloatingSlave) | 
 |       continue; | 
 |  | 
 |     bool possible_cmt = false; | 
 |     bool not_cmt = false; | 
 |     const int deviceid = info.deviceid; | 
 |  | 
 |     for (int j = 0; j < info.num_classes; ++j) { | 
 |       if (info.classes[j]->type == XIValuatorClass) | 
 |         ++valuator_count_[deviceid]; | 
 |       else if (info.classes[j]->type == XIScrollClass) | 
 |         not_cmt = true; | 
 |     } | 
 |  | 
 |     // Skip devices that don't use any valuator | 
 |     if (!valuator_count_[deviceid]) | 
 |       continue; | 
 |  | 
 |     valuator_lookup_[deviceid].resize(DT_LAST_ENTRY, -1); | 
 |     data_type_lookup_[deviceid].resize( | 
 |         valuator_count_[deviceid], DT_LAST_ENTRY); | 
 |     valuator_min_[deviceid].resize(DT_LAST_ENTRY, 0); | 
 |     valuator_max_[deviceid].resize(DT_LAST_ENTRY, 0); | 
 |     for (int j = 0; j < kMaxSlotNum; j++) | 
 |       last_seen_valuator_[deviceid][j].resize(DT_LAST_ENTRY, 0); | 
 |     for (int j = 0; j < info.num_classes; ++j) { | 
 |       if (info.classes[j]->type != XIValuatorClass) | 
 |         continue; | 
 |  | 
 |       XIValuatorClassInfo* v = | 
 |           reinterpret_cast<XIValuatorClassInfo*>(info.classes[j]); | 
 |       for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type) { | 
 |         if (v->label == atoms[data_type]) { | 
 |           valuator_lookup_[deviceid][data_type] = v->number; | 
 |           data_type_lookup_[deviceid][v->number] = data_type; | 
 |           valuator_min_[deviceid][data_type] = v->min; | 
 |           valuator_max_[deviceid][data_type] = v->max; | 
 |           if (IsCMTDataType(data_type)) | 
 |             possible_cmt = true; | 
 |           break; | 
 |         } | 
 |       } | 
 |     } | 
 |  | 
 |     if (possible_cmt && !not_cmt) | 
 |       cmt_devices_[deviceid] = true; | 
 |   } | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::GetSlotNumber(const XIDeviceEvent* xiev, int* slot) { | 
 |   ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | 
 |   if (!factory->IsMultiTouchDevice(xiev->sourceid)) { | 
 |     *slot = 0; | 
 |     return true; | 
 |   } | 
 |   return factory->QuerySlotForTrackingID(xiev->detail, slot); | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::GetEventRawData(const XEvent& xev, EventData* data) { | 
 |   if (xev.type != GenericEvent) | 
 |     return; | 
 |  | 
 |   XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); | 
 |   CHECK(xiev->sourceid >= 0); | 
 |   CHECK(xiev->deviceid >= 0); | 
 |   if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum) | 
 |     return; | 
 |   data->clear(); | 
 |   const int sourceid = xiev->sourceid; | 
 |   double* valuators = xiev->valuators.values; | 
 |   for (int i = 0; i <= valuator_count_[sourceid]; ++i) { | 
 |     if (XIMaskIsSet(xiev->valuators.mask, i)) { | 
 |       int type = data_type_lookup_[sourceid][i]; | 
 |       if (type != DT_LAST_ENTRY) { | 
 |         (*data)[type] = *valuators; | 
 |         if (IsTouchDataType(type)) { | 
 |           int slot = -1; | 
 |           if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum) | 
 |             last_seen_valuator_[sourceid][slot][type] = *valuators; | 
 |         } | 
 |       } | 
 |       valuators++; | 
 |     } | 
 |   } | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::GetEventData(const XEvent& xev, | 
 |     const DataType type, double* value) { | 
 |   if (xev.type != GenericEvent) | 
 |     return false; | 
 |  | 
 |   XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); | 
 |   CHECK(xiev->sourceid >= 0); | 
 |   CHECK(xiev->deviceid >= 0); | 
 |   if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum) | 
 |     return false; | 
 |   const int sourceid = xiev->sourceid; | 
 |   if (valuator_lookup_[sourceid].empty()) | 
 |     return false; | 
 |  | 
 |   if (type == DT_TOUCH_TRACKING_ID) { | 
 |     // With XInput2 MT, Tracking ID is provided in the detail field for touch | 
 |     // events. | 
 |     if (xiev->evtype == XI_TouchBegin || | 
 |         xiev->evtype == XI_TouchEnd || | 
 |         xiev->evtype == XI_TouchUpdate) { | 
 |       *value = xiev->detail; | 
 |     } else { | 
 |       *value = 0; | 
 |     } | 
 |     return true; | 
 |   } | 
 |  | 
 |   int val_index = valuator_lookup_[sourceid][type]; | 
 |   int slot = 0; | 
 |   if (val_index >= 0) { | 
 |     if (XIMaskIsSet(xiev->valuators.mask, val_index)) { | 
 |       double* valuators = xiev->valuators.values; | 
 |       while (val_index--) { | 
 |         if (XIMaskIsSet(xiev->valuators.mask, val_index)) | 
 |           ++valuators; | 
 |       } | 
 |       *value = *valuators; | 
 |       if (IsTouchDataType(type)) { | 
 |         if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum) | 
 |           last_seen_valuator_[sourceid][slot][type] = *value; | 
 |       } | 
 |       return true; | 
 |     } else if (IsTouchDataType(type)) { | 
 |       if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum) | 
 |         *value = last_seen_valuator_[sourceid][slot][type]; | 
 |     } | 
 |   } | 
 |  | 
 |   return false; | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsXIDeviceEvent( | 
 |     const base::NativeEvent& native_event) const { | 
 |   if (native_event->type != GenericEvent || | 
 |       native_event->xcookie.extension != xi_opcode_) | 
 |     return false; | 
 |   return xi_device_event_types_[native_event->xcookie.evtype]; | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsTouchpadXInputEvent( | 
 |     const base::NativeEvent& native_event) const { | 
 |   if (native_event->type != GenericEvent) | 
 |     return false; | 
 |  | 
 |   XIDeviceEvent* xievent = | 
 |       static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
 |   CHECK(xievent->sourceid >= 0); | 
 |   if (xievent->sourceid >= kMaxDeviceNum) | 
 |     return false; | 
 |   return touchpads_[xievent->sourceid]; | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsCMTDeviceEvent( | 
 |     const base::NativeEvent& native_event) const { | 
 |   if (native_event->type != GenericEvent) | 
 |     return false; | 
 |  | 
 |   XIDeviceEvent* xievent = | 
 |       static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
 |   CHECK(xievent->sourceid >= 0); | 
 |   if (xievent->sourceid >= kMaxDeviceNum) | 
 |     return false; | 
 |   return cmt_devices_[xievent->sourceid]; | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsCMTGestureEvent( | 
 |     const base::NativeEvent& native_event) const { | 
 |   return (IsScrollEvent(native_event) || | 
 |           IsFlingEvent(native_event) || | 
 |           IsCMTMetricsEvent(native_event)); | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::HasEventData( | 
 |     const XIDeviceEvent* xiev, const DataType type) const { | 
 |   CHECK(xiev->sourceid >= 0); | 
 |   if (xiev->sourceid >= kMaxDeviceNum) | 
 |     return false; | 
 |   if (type >= valuator_lookup_[xiev->sourceid].size()) | 
 |     return false; | 
 |   const int idx = valuator_lookup_[xiev->sourceid][type]; | 
 |   return (idx >= 0) && XIMaskIsSet(xiev->valuators.mask, idx); | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsScrollEvent( | 
 |     const base::NativeEvent& native_event) const { | 
 |   if (!IsCMTDeviceEvent(native_event)) | 
 |     return false; | 
 |  | 
 |   XIDeviceEvent* xiev = | 
 |       static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
 |   return (HasEventData(xiev, DT_CMT_SCROLL_X) || | 
 |           HasEventData(xiev, DT_CMT_SCROLL_Y)); | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsFlingEvent( | 
 |     const base::NativeEvent& native_event) const { | 
 |   if (!IsCMTDeviceEvent(native_event)) | 
 |     return false; | 
 |  | 
 |   XIDeviceEvent* xiev = | 
 |       static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
 |   return (HasEventData(xiev, DT_CMT_FLING_X) && | 
 |           HasEventData(xiev, DT_CMT_FLING_Y) && | 
 |           HasEventData(xiev, DT_CMT_FLING_STATE)); | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsCMTMetricsEvent( | 
 |     const base::NativeEvent& native_event) const { | 
 |   if (!IsCMTDeviceEvent(native_event)) | 
 |     return false; | 
 |  | 
 |   XIDeviceEvent* xiev = | 
 |       static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
 |   return (HasEventData(xiev, DT_CMT_METRICS_TYPE) && | 
 |           HasEventData(xiev, DT_CMT_METRICS_DATA1) && | 
 |           HasEventData(xiev, DT_CMT_METRICS_DATA2)); | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::HasGestureTimes( | 
 |     const base::NativeEvent& native_event) const { | 
 |   if (!IsCMTDeviceEvent(native_event)) | 
 |     return false; | 
 |  | 
 |   XIDeviceEvent* xiev = | 
 |       static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
 |   return (HasEventData(xiev, DT_CMT_START_TIME) && | 
 |           HasEventData(xiev, DT_CMT_END_TIME)); | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::GetScrollOffsets( | 
 |     const base::NativeEvent& native_event, | 
 |     float* x_offset, | 
 |     float* y_offset, | 
 |     float* x_offset_ordinal, | 
 |     float* y_offset_ordinal, | 
 |     int* finger_count) { | 
 |   *x_offset = 0; | 
 |   *y_offset = 0; | 
 |   *x_offset_ordinal = 0; | 
 |   *y_offset_ordinal = 0; | 
 |   *finger_count = 2; | 
 |  | 
 |   EventData data; | 
 |   GetEventRawData(*native_event, &data); | 
 |  | 
 |   if (data.find(DT_CMT_SCROLL_X) != data.end()) | 
 |     *x_offset = data[DT_CMT_SCROLL_X]; | 
 |   if (data.find(DT_CMT_SCROLL_Y) != data.end()) | 
 |     *y_offset = data[DT_CMT_SCROLL_Y]; | 
 |   if (data.find(DT_CMT_ORDINAL_X) != data.end()) | 
 |     *x_offset_ordinal = data[DT_CMT_ORDINAL_X]; | 
 |   if (data.find(DT_CMT_ORDINAL_Y) != data.end()) | 
 |     *y_offset_ordinal = data[DT_CMT_ORDINAL_Y]; | 
 |   if (data.find(DT_CMT_FINGER_COUNT) != data.end()) | 
 |     *finger_count = static_cast<int>(data[DT_CMT_FINGER_COUNT]); | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::GetFlingData( | 
 |     const base::NativeEvent& native_event, | 
 |     float* vx, | 
 |     float* vy, | 
 |     float* vx_ordinal, | 
 |     float* vy_ordinal, | 
 |     bool* is_cancel) { | 
 |   *vx = 0; | 
 |   *vy = 0; | 
 |   *vx_ordinal = 0; | 
 |   *vy_ordinal = 0; | 
 |   *is_cancel = false; | 
 |  | 
 |   EventData data; | 
 |   GetEventRawData(*native_event, &data); | 
 |  | 
 |   if (data.find(DT_CMT_FLING_X) != data.end()) | 
 |     *vx = data[DT_CMT_FLING_X]; | 
 |   if (data.find(DT_CMT_FLING_Y) != data.end()) | 
 |     *vy = data[DT_CMT_FLING_Y]; | 
 |   if (data.find(DT_CMT_FLING_STATE) != data.end()) | 
 |     *is_cancel = !!static_cast<unsigned int>(data[DT_CMT_FLING_STATE]); | 
 |   if (data.find(DT_CMT_ORDINAL_X) != data.end()) | 
 |     *vx_ordinal = data[DT_CMT_ORDINAL_X]; | 
 |   if (data.find(DT_CMT_ORDINAL_Y) != data.end()) | 
 |     *vy_ordinal = data[DT_CMT_ORDINAL_Y]; | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::GetMetricsData( | 
 |     const base::NativeEvent& native_event, | 
 |     GestureMetricsType* type, | 
 |     float* data1, | 
 |     float* data2) { | 
 |   *type = kGestureMetricsTypeUnknown; | 
 |   *data1 = 0; | 
 |   *data2 = 0; | 
 |  | 
 |   EventData data; | 
 |   GetEventRawData(*native_event, &data); | 
 |  | 
 |   if (data.find(DT_CMT_METRICS_TYPE) != data.end()) { | 
 |     int val = static_cast<int>(data[DT_CMT_METRICS_TYPE]); | 
 |     if (val == 0) | 
 |       *type = kGestureMetricsTypeNoisyGround; | 
 |     else | 
 |       *type = kGestureMetricsTypeUnknown; | 
 |   } | 
 |   if (data.find(DT_CMT_METRICS_DATA1) != data.end()) | 
 |     *data1 = data[DT_CMT_METRICS_DATA1]; | 
 |   if (data.find(DT_CMT_METRICS_DATA2) != data.end()) | 
 |     *data2 = data[DT_CMT_METRICS_DATA2]; | 
 | } | 
 |  | 
 | int DeviceDataManagerX11::GetMappedButton(int button) { | 
 |   return button > 0 && button <= button_map_count_ ? button_map_[button - 1] : | 
 |                                                      button; | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::UpdateButtonMap() { | 
 |   button_map_count_ = XGetPointerMapping(gfx::GetXDisplay(), | 
 |                                          button_map_, | 
 |                                          arraysize(button_map_)); | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::GetGestureTimes( | 
 |     const base::NativeEvent& native_event, | 
 |     double* start_time, | 
 |     double* end_time) { | 
 |   *start_time = 0; | 
 |   *end_time = 0; | 
 |  | 
 |   EventData data; | 
 |   GetEventRawData(*native_event, &data); | 
 |  | 
 |   if (data.find(DT_CMT_START_TIME) != data.end()) | 
 |     *start_time = data[DT_CMT_START_TIME]; | 
 |   if (data.find(DT_CMT_END_TIME) != data.end()) | 
 |     *end_time = data[DT_CMT_END_TIME]; | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::NormalizeData(int deviceid, | 
 |                                          const DataType type, | 
 |                                          double* value) { | 
 |   double max_value; | 
 |   double min_value; | 
 |   if (GetDataRange(deviceid, type, &min_value, &max_value)) { | 
 |     *value = (*value - min_value) / (max_value - min_value); | 
 |     DCHECK(*value >= 0.0 && *value <= 1.0); | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::GetDataRange(int deviceid, | 
 |                                         const DataType type, | 
 |                                         double* min, | 
 |                                         double* max) { | 
 |   CHECK(deviceid >= 0); | 
 |   if (deviceid >= kMaxDeviceNum) | 
 |     return false; | 
 |   if (valuator_lookup_[deviceid][type] >= 0) { | 
 |     *min = valuator_min_[deviceid][type]; | 
 |     *max = valuator_max_[deviceid][type]; | 
 |     return true; | 
 |   } | 
 |   return false; | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::SetDeviceListForTest( | 
 |     const std::vector<int>& touchscreen, | 
 |     const std::vector<int>& cmt_devices, | 
 |     const std::vector<int>& other_devices) { | 
 |   for (int i = 0; i < kMaxDeviceNum; ++i) { | 
 |     valuator_count_[i] = 0; | 
 |     valuator_lookup_[i].clear(); | 
 |     data_type_lookup_[i].clear(); | 
 |     valuator_min_[i].clear(); | 
 |     valuator_max_[i].clear(); | 
 |     for (int j = 0; j < kMaxSlotNum; j++) | 
 |       last_seen_valuator_[i][j].clear(); | 
 |   } | 
 |  | 
 |   for (int deviceid : touchscreen) { | 
 |     InitializeValuatorsForTest(deviceid, kTouchDataTypeStart, kTouchDataTypeEnd, | 
 |                                0, 1000); | 
 |   } | 
 |  | 
 |   cmt_devices_.reset(); | 
 |   for (int deviceid : cmt_devices) { | 
 |     cmt_devices_[deviceid] = true; | 
 |     touchpads_[deviceid] = true; | 
 |     InitializeValuatorsForTest(deviceid, kCMTDataTypeStart, kCMTDataTypeEnd, | 
 |                                -1000, 1000); | 
 |   } | 
 |  | 
 |   for (int deviceid : other_devices) { | 
 |     InitializeValuatorsForTest(deviceid, kCMTDataTypeStart, kCMTDataTypeEnd, | 
 |                                -1000, 1000); | 
 |   } | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::SetValuatorDataForTest(XIDeviceEvent* xievent, | 
 |                                                   DataType type, | 
 |                                                   double value) { | 
 |   int index = valuator_lookup_[xievent->deviceid][type]; | 
 |   CHECK(!XIMaskIsSet(xievent->valuators.mask, index)); | 
 |   CHECK(index >= 0 && index < valuator_count_[xievent->deviceid]); | 
 |   XISetMask(xievent->valuators.mask, index); | 
 |  | 
 |   double* valuators = xievent->valuators.values; | 
 |   for (int i = 0; i < index; ++i) { | 
 |     if (XIMaskIsSet(xievent->valuators.mask, i)) | 
 |       valuators++; | 
 |   } | 
 |   for (int i = DT_LAST_ENTRY - 1; i > valuators - xievent->valuators.values; | 
 |        --i) | 
 |     xievent->valuators.values[i] = xievent->valuators.values[i - 1]; | 
 |   *valuators = value; | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::InitializeValuatorsForTest(int deviceid, | 
 |                                                       int start_valuator, | 
 |                                                       int end_valuator, | 
 |                                                       double min_value, | 
 |                                                       double max_value) { | 
 |   valuator_lookup_[deviceid].resize(DT_LAST_ENTRY, -1); | 
 |   data_type_lookup_[deviceid].resize(DT_LAST_ENTRY, DT_LAST_ENTRY); | 
 |   valuator_min_[deviceid].resize(DT_LAST_ENTRY, 0); | 
 |   valuator_max_[deviceid].resize(DT_LAST_ENTRY, 0); | 
 |   for (int j = 0; j < kMaxSlotNum; j++) | 
 |     last_seen_valuator_[deviceid][j].resize(DT_LAST_ENTRY, 0); | 
 |   for (int j = start_valuator; j <= end_valuator; ++j) { | 
 |     valuator_lookup_[deviceid][j] = valuator_count_[deviceid]; | 
 |     data_type_lookup_[deviceid][valuator_count_[deviceid]] = j; | 
 |     valuator_min_[deviceid][j] = min_value; | 
 |     valuator_max_[deviceid][j] = max_value; | 
 |     valuator_count_[deviceid]++; | 
 |   } | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::TouchEventNeedsCalibrate(int touch_device_id) const { | 
 | #if defined(OS_CHROMEOS) | 
 |   if (!base::SysInfo::IsRunningOnChromeOS()) | 
 |     return false; | 
 |  | 
 |   const std::vector<TouchscreenDevice>& touch_devices = | 
 |       ui::DeviceDataManager::GetInstance()->touchscreen_devices(); | 
 |   std::vector<TouchscreenDevice>::const_iterator it = | 
 |       std::find_if(touch_devices.begin(), touch_devices.end(), | 
 |                    std::bind2nd(std::ptr_fun(&DeviceHasId), touch_device_id)); | 
 |   return it != touch_devices.end() && it->type == INPUT_DEVICE_INTERNAL; | 
 | #endif  // defined(OS_CHROMEOS) | 
 |   return false; | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::SetDisabledKeyboardAllowedKeys( | 
 |     scoped_ptr<std::set<KeyboardCode> > excepted_keys) { | 
 |   DCHECK(!excepted_keys.get() || | 
 |          !blocked_keyboard_allowed_keys_.get()); | 
 |   blocked_keyboard_allowed_keys_ = excepted_keys.Pass(); | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::DisableDevice(int deviceid) { | 
 |   blocked_devices_.set(deviceid, true); | 
 |   // TODO(rsadam@): Support blocking touchscreen devices. | 
 |   std::vector<KeyboardDevice> keyboards = keyboard_devices(); | 
 |   std::vector<KeyboardDevice>::iterator it = | 
 |       std::find_if(keyboards.begin(), | 
 |                    keyboards.end(), | 
 |                    std::bind2nd(std::ptr_fun(&DeviceHasId), deviceid)); | 
 |   if (it != std::end(keyboards)) { | 
 |     blocked_keyboards_.insert( | 
 |         std::pair<int, KeyboardDevice>(deviceid, *it)); | 
 |     keyboards.erase(it); | 
 |     DeviceDataManager::OnKeyboardDevicesUpdated(keyboards); | 
 |   } | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::EnableDevice(int deviceid) { | 
 |   blocked_devices_.set(deviceid, false); | 
 |   std::map<int, KeyboardDevice>::iterator it = | 
 |       blocked_keyboards_.find(deviceid); | 
 |   if (it != blocked_keyboards_.end()) { | 
 |     std::vector<KeyboardDevice> devices = keyboard_devices(); | 
 |     // Add device to current list of active devices. | 
 |     devices.push_back((*it).second); | 
 |     blocked_keyboards_.erase(it); | 
 |     DeviceDataManager::OnKeyboardDevicesUpdated(devices); | 
 |   } | 
 | } | 
 |  | 
 | bool DeviceDataManagerX11::IsEventBlocked( | 
 |     const base::NativeEvent& native_event) { | 
 |   // Only check XI2 events which have a source device id. | 
 |   if (native_event->type != GenericEvent) | 
 |     return false; | 
 |  | 
 |   XIDeviceEvent* xievent = | 
 |       static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 
 |   // Allow any key events from blocked_keyboard_allowed_keys_. | 
 |   if (blocked_keyboard_allowed_keys_ && | 
 |       (xievent->evtype == XI_KeyPress || xievent->evtype == XI_KeyRelease) && | 
 |       blocked_keyboard_allowed_keys_->find( | 
 |           KeyboardCodeFromXKeyEvent(native_event)) != | 
 |           blocked_keyboard_allowed_keys_->end()) { | 
 |     return false; | 
 |   } | 
 |  | 
 |   return blocked_devices_.test(xievent->sourceid); | 
 | } | 
 |  | 
 | void DeviceDataManagerX11::OnKeyboardDevicesUpdated( | 
 |     const std::vector<KeyboardDevice>& devices) { | 
 |   std::vector<KeyboardDevice> keyboards(devices); | 
 |   for (std::map<int, KeyboardDevice>::iterator blocked_iter = | 
 |            blocked_keyboards_.begin(); | 
 |        blocked_iter != blocked_keyboards_.end();) { | 
 |     // Check if the blocked device still exists in list of devices. | 
 |     std::vector<KeyboardDevice>::iterator it = std::find_if( | 
 |         keyboards.begin(), keyboards.end(), | 
 |         std::bind2nd(std::ptr_fun(&DeviceHasId), (*blocked_iter).first)); | 
 |     // If the device no longer exists, unblock it, else filter it out from our | 
 |     // active list. | 
 |     if (it == keyboards.end()) { | 
 |       blocked_devices_.set((*blocked_iter).first, false); | 
 |       blocked_keyboards_.erase(blocked_iter++); | 
 |     } else { | 
 |       keyboards.erase(it); | 
 |       ++blocked_iter; | 
 |     } | 
 |   } | 
 |   // Notify base class of updated list. | 
 |   DeviceDataManager::OnKeyboardDevicesUpdated(keyboards); | 
 | } | 
 |  | 
 | }  // namespace ui |