Home | History | Annotate | Download | only in x
      1 // Copyright 2013 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 
      5 #include "ui/base/x/device_data_manager.h"
      6 
      7 #include <X11/extensions/XInput.h>
      8 #include <X11/extensions/XInput2.h>
      9 #include <X11/Xlib.h>
     10 
     11 #include "base/logging.h"
     12 #include "base/memory/singleton.h"
     13 #include "base/message_loop/message_pump_aurax11.h"
     14 #include "ui/base/events/event_constants.h"
     15 #include "ui/base/events/event_utils.h"
     16 #include "ui/base/touch/touch_factory_x11.h"
     17 #include "ui/base/x/device_list_cache_x.h"
     18 #include "ui/base/x/x11_util.h"
     19 
     20 // XIScrollClass was introduced in XI 2.1 so we need to define it here
     21 // for backward-compatibility with older versions of XInput.
     22 #if !defined(XIScrollClass)
     23 #define XIScrollClass 3
     24 #endif
     25 
     26 // Multi-touch support was introduced in XI 2.2. Add XI event types here
     27 // for backward-compatibility with older versions of XInput.
     28 #if !defined(XI_TouchBegin)
     29 #define XI_TouchBegin  18
     30 #define XI_TouchUpdate 19
     31 #define XI_TouchEnd    20
     32 #endif
     33 
     34 // Copied from xserver-properties.h
     35 #define AXIS_LABEL_PROP_REL_HWHEEL "Rel Horiz Wheel"
     36 #define AXIS_LABEL_PROP_REL_WHEEL "Rel Vert Wheel"
     37 
     38 // CMT specific timings
     39 #define AXIS_LABEL_PROP_ABS_DBL_START_TIME "Abs Dbl Start Timestamp"
     40 #define AXIS_LABEL_PROP_ABS_DBL_END_TIME   "Abs Dbl End Timestamp"
     41 
     42 // Ordinal values
     43 #define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X   "Abs Dbl Ordinal X"
     44 #define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y   "Abs Dbl Ordinal Y"
     45 
     46 // Fling properties
     47 #define AXIS_LABEL_PROP_ABS_DBL_FLING_VX   "Abs Dbl Fling X Velocity"
     48 #define AXIS_LABEL_PROP_ABS_DBL_FLING_VY   "Abs Dbl Fling Y Velocity"
     49 #define AXIS_LABEL_PROP_ABS_FLING_STATE   "Abs Fling State"
     50 
     51 #define AXIS_LABEL_PROP_ABS_FINGER_COUNT   "Abs Finger Count"
     52 
     53 // Cros metrics gesture from touchpad
     54 #define AXIS_LABEL_PROP_ABS_METRICS_TYPE      "Abs Metrics Type"
     55 #define AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA1 "Abs Dbl Metrics Data 1"
     56 #define AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA2 "Abs Dbl Metrics Data 2"
     57 
     58 // Touchscreen multi-touch
     59 #define AXIS_LABEL_ABS_MT_TOUCH_MAJOR "Abs MT Touch Major"
     60 #define AXIS_LABEL_ABS_MT_TOUCH_MINOR "Abs MT Touch Minor"
     61 #define AXIS_LABEL_ABS_MT_ORIENTATION "Abs MT Orientation"
     62 #define AXIS_LABEL_ABS_MT_PRESSURE    "Abs MT Pressure"
     63 #define AXIS_LABEL_ABS_MT_SLOT_ID     "Abs MT Slot ID"
     64 #define AXIS_LABEL_ABS_MT_TRACKING_ID "Abs MT Tracking ID"
     65 #define AXIS_LABEL_TOUCH_TIMESTAMP    "Touch Timestamp"
     66 
     67 // When you add new data types, please make sure the order here is aligned
     68 // with the order in the DataType enum in the header file because we assume
     69 // they are in sync when updating the device list (see UpdateDeviceList).
     70 const char* kCachedAtoms[] = {
     71   AXIS_LABEL_PROP_REL_HWHEEL,
     72   AXIS_LABEL_PROP_REL_WHEEL,
     73   AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X,
     74   AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y,
     75   AXIS_LABEL_PROP_ABS_DBL_START_TIME,
     76   AXIS_LABEL_PROP_ABS_DBL_END_TIME,
     77   AXIS_LABEL_PROP_ABS_DBL_FLING_VX,
     78   AXIS_LABEL_PROP_ABS_DBL_FLING_VY,
     79   AXIS_LABEL_PROP_ABS_FLING_STATE,
     80   AXIS_LABEL_PROP_ABS_METRICS_TYPE,
     81   AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA1,
     82   AXIS_LABEL_PROP_ABS_DBL_METRICS_DATA2,
     83   AXIS_LABEL_PROP_ABS_FINGER_COUNT,
     84   AXIS_LABEL_ABS_MT_TOUCH_MAJOR,
     85   AXIS_LABEL_ABS_MT_TOUCH_MINOR,
     86   AXIS_LABEL_ABS_MT_ORIENTATION,
     87   AXIS_LABEL_ABS_MT_PRESSURE,
     88 #if !defined(USE_XI2_MT)
     89   AXIS_LABEL_ABS_MT_SLOT_ID,
     90 #endif
     91   AXIS_LABEL_ABS_MT_TRACKING_ID,
     92   AXIS_LABEL_TOUCH_TIMESTAMP,
     93 
     94   NULL
     95 };
     96 
     97 // Constants for checking if a data type lies in the range of CMT/Touch data
     98 // types.
     99 const int kCMTDataTypeStart = ui::DeviceDataManager::DT_CMT_SCROLL_X;
    100 const int kCMTDataTypeEnd = ui::DeviceDataManager::DT_CMT_FINGER_COUNT;
    101 const int kTouchDataTypeStart = ui::DeviceDataManager::DT_TOUCH_MAJOR;
    102 const int kTouchDataTypeEnd = ui::DeviceDataManager::DT_TOUCH_RAW_TIMESTAMP;
    103 
    104 namespace ui {
    105 
    106 bool DeviceDataManager::IsCMTDataType(const int type) {
    107   return (type >= kCMTDataTypeStart) && (type <= kCMTDataTypeEnd);
    108 }
    109 
    110 bool DeviceDataManager::IsTouchDataType(const int type) {
    111   return (type >= kTouchDataTypeStart) && (type <= kTouchDataTypeEnd);
    112 }
    113 
    114 DeviceDataManager* DeviceDataManager::GetInstance() {
    115   return Singleton<DeviceDataManager>::get();
    116 }
    117 
    118 DeviceDataManager::DeviceDataManager()
    119     : natural_scroll_enabled_(false),
    120       atom_cache_(ui::GetXDisplay(), kCachedAtoms) {
    121   InitializeXInputInternal();
    122 
    123   // Make sure the sizes of enum and kCachedAtoms are aligned.
    124   CHECK(arraysize(kCachedAtoms) == static_cast<size_t>(DT_LAST_ENTRY) + 1);
    125   UpdateDeviceList(ui::GetXDisplay());
    126 }
    127 
    128 DeviceDataManager::~DeviceDataManager() {
    129 }
    130 
    131 bool DeviceDataManager::InitializeXInputInternal() {
    132   // Check if XInput is available on the system.
    133   xi_opcode_ = -1;
    134   int opcode, event, error;
    135   if (!XQueryExtension(
    136       ui::GetXDisplay(), "XInputExtension", &opcode, &event, &error)) {
    137     VLOG(1) << "X Input extension not available: error=" << error;
    138     return false;
    139   }
    140   xi_opcode_ = opcode;
    141 
    142   // Check the XInput version.
    143 #if defined(USE_XI2_MT)
    144   int major = 2, minor = USE_XI2_MT;
    145 #else
    146   int major = 2, minor = 0;
    147 #endif
    148   if (XIQueryVersion(ui::GetXDisplay(), &major, &minor) == BadRequest) {
    149     VLOG(1) << "XInput2 not supported in the server.";
    150     return false;
    151   }
    152 
    153   // Possible XI event types for XIDeviceEvent. See the XI2 protocol
    154   // specification.
    155   xi_device_event_types_[XI_KeyPress] = true;
    156   xi_device_event_types_[XI_KeyRelease] = true;
    157   xi_device_event_types_[XI_ButtonPress] = true;
    158   xi_device_event_types_[XI_ButtonRelease] = true;
    159   xi_device_event_types_[XI_Motion] = true;
    160   // Multi-touch support was introduced in XI 2.2.
    161   if (minor >= 2) {
    162     xi_device_event_types_[XI_TouchBegin] = true;
    163     xi_device_event_types_[XI_TouchUpdate] = true;
    164     xi_device_event_types_[XI_TouchEnd] = true;
    165   }
    166   return true;
    167 }
    168 
    169 float DeviceDataManager::GetNaturalScrollFactor(int sourceid) const {
    170   // Natural scroll is touchpad-only.
    171   if (sourceid >= kMaxDeviceNum || !touchpads_[sourceid])
    172     return -1.0f;
    173 
    174   return natural_scroll_enabled_ ? 1.0f : -1.0f;
    175 }
    176 
    177 void DeviceDataManager::UpdateDeviceList(Display* display) {
    178   cmt_devices_.reset();
    179   touchpads_.reset();
    180   for (int i = 0; i < kMaxDeviceNum; ++i) {
    181     valuator_count_[i] = 0;
    182     valuator_lookup_[i].clear();
    183     data_type_lookup_[i].clear();
    184     valuator_min_[i].clear();
    185     valuator_max_[i].clear();
    186     for (int j = 0; j < kMaxSlotNum; j++)
    187       last_seen_valuator_[i][j].clear();
    188   }
    189 
    190   // Find all the touchpad devices.
    191   XDeviceList dev_list =
    192       ui::DeviceListCacheX::GetInstance()->GetXDeviceList(display);
    193   Atom xi_touchpad = XInternAtom(display, XI_TOUCHPAD, false);
    194   for (int i = 0; i < dev_list.count; ++i)
    195     if (dev_list[i].type == xi_touchpad)
    196       touchpads_[dev_list[i].id] = true;
    197 
    198   // Update the structs with new valuator information
    199   XIDeviceList info_list =
    200       ui::DeviceListCacheX::GetInstance()->GetXI2DeviceList(display);
    201   Atom atoms[DT_LAST_ENTRY];
    202   for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type)
    203     atoms[data_type] = atom_cache_.GetAtom(kCachedAtoms[data_type]);
    204 
    205   for (int i = 0; i < info_list.count; ++i) {
    206     XIDeviceInfo* info = info_list.devices + i;
    207 
    208     // We currently handle only slave, non-keyboard devices
    209     if (info->use != XISlavePointer && info->use != XIFloatingSlave)
    210       continue;
    211 
    212     bool possible_cmt = false;
    213     bool not_cmt = false;
    214     const int deviceid = info->deviceid;
    215 
    216     for (int j = 0; j < info->num_classes; ++j) {
    217       if (info->classes[j]->type == XIValuatorClass)
    218         ++valuator_count_[deviceid];
    219       else if (info->classes[j]->type == XIScrollClass)
    220         not_cmt = true;
    221     }
    222 
    223     // Skip devices that don't use any valuator
    224     if (!valuator_count_[deviceid])
    225       continue;
    226 
    227     valuator_lookup_[deviceid].resize(DT_LAST_ENTRY, -1);
    228     data_type_lookup_[deviceid].resize(
    229         valuator_count_[deviceid], DT_LAST_ENTRY);
    230     valuator_min_[deviceid].resize(DT_LAST_ENTRY, 0);
    231     valuator_max_[deviceid].resize(DT_LAST_ENTRY, 0);
    232     for (int j = 0; j < kMaxSlotNum; j++)
    233       last_seen_valuator_[deviceid][j].resize(DT_LAST_ENTRY, 0);
    234     for (int j = 0; j < info->num_classes; ++j) {
    235       if (info->classes[j]->type != XIValuatorClass)
    236         continue;
    237 
    238       XIValuatorClassInfo* v =
    239           reinterpret_cast<XIValuatorClassInfo*>(info->classes[j]);
    240       for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type) {
    241         if (v->label == atoms[data_type]) {
    242           valuator_lookup_[deviceid][data_type] = v->number;
    243           data_type_lookup_[deviceid][v->number] = data_type;
    244           valuator_min_[deviceid][data_type] = v->min;
    245           valuator_max_[deviceid][data_type] = v->max;
    246           if (IsCMTDataType(data_type))
    247             possible_cmt = true;
    248           break;
    249         }
    250       }
    251     }
    252 
    253     if (possible_cmt && !not_cmt)
    254       cmt_devices_[deviceid] = true;
    255   }
    256 }
    257 
    258 bool DeviceDataManager::GetSlotNumber(const XIDeviceEvent* xiev, int* slot) {
    259 #if defined(USE_XI2_MT)
    260   ui::TouchFactory* factory = ui::TouchFactory::GetInstance();
    261   if (!factory->IsMultiTouchDevice(xiev->sourceid)) {
    262     *slot = 0;
    263     return true;
    264   }
    265   return factory->QuerySlotForTrackingID(xiev->detail, slot);
    266 #else
    267   *slot = 0;
    268   return true;
    269 #endif
    270 }
    271 
    272 void DeviceDataManager::GetEventRawData(const XEvent& xev, EventData* data) {
    273   if (xev.type != GenericEvent)
    274     return;
    275 
    276   XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
    277   if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
    278     return;
    279   data->clear();
    280   const int sourceid = xiev->sourceid;
    281   double* valuators = xiev->valuators.values;
    282   for (int i = 0; i <= valuator_count_[sourceid]; ++i) {
    283     if (XIMaskIsSet(xiev->valuators.mask, i)) {
    284       int type = data_type_lookup_[sourceid][i];
    285       if (type != DT_LAST_ENTRY) {
    286         (*data)[type] = *valuators;
    287         if (IsTouchDataType(type)) {
    288           int slot = -1;
    289           if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
    290             last_seen_valuator_[sourceid][slot][type] = *valuators;
    291         }
    292       }
    293       valuators++;
    294     }
    295   }
    296 }
    297 
    298 bool DeviceDataManager::GetEventData(const XEvent& xev,
    299     const DataType type, double* value) {
    300   if (xev.type != GenericEvent)
    301     return false;
    302 
    303   XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data);
    304   if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum)
    305     return false;
    306   const int sourceid = xiev->sourceid;
    307   if (valuator_lookup_[sourceid].empty())
    308     return false;
    309 
    310 #if defined(USE_XI2_MT)
    311   // With XInput2 MT, Tracking ID is provided in the detail field.
    312   if (type == DT_TOUCH_TRACKING_ID) {
    313     *value = xiev->detail;
    314     return true;
    315   }
    316 #endif
    317 
    318   int val_index = valuator_lookup_[sourceid][type];
    319   int slot = 0;
    320   if (val_index >= 0) {
    321     if (XIMaskIsSet(xiev->valuators.mask, val_index)) {
    322       double* valuators = xiev->valuators.values;
    323       while (val_index--) {
    324         if (XIMaskIsSet(xiev->valuators.mask, val_index))
    325           ++valuators;
    326       }
    327       *value = *valuators;
    328       if (IsTouchDataType(type)) {
    329         if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
    330           last_seen_valuator_[sourceid][slot][type] = *value;
    331       }
    332       return true;
    333     } else if (IsTouchDataType(type)) {
    334       if (GetSlotNumber(xiev, &slot) && slot >= 0 && slot < kMaxSlotNum)
    335         *value = last_seen_valuator_[sourceid][slot][type];
    336     }
    337   }
    338 
    339   return false;
    340 }
    341 
    342 bool DeviceDataManager::IsXIDeviceEvent(
    343     const base::NativeEvent& native_event) const {
    344   if (native_event->type != GenericEvent ||
    345       native_event->xcookie.extension != xi_opcode_)
    346     return false;
    347   return xi_device_event_types_[native_event->xcookie.evtype];
    348 }
    349 
    350 bool DeviceDataManager::IsTouchpadXInputEvent(
    351     const base::NativeEvent& native_event) const {
    352   if (native_event->type != GenericEvent)
    353     return false;
    354 
    355   XIDeviceEvent* xievent =
    356       static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    357   if (xievent->sourceid >= kMaxDeviceNum)
    358     return false;
    359   return touchpads_[xievent->sourceid];
    360 }
    361 
    362 bool DeviceDataManager::IsCMTDeviceEvent(
    363     const base::NativeEvent& native_event) const {
    364   if (native_event->type != GenericEvent)
    365     return false;
    366 
    367   XIDeviceEvent* xievent =
    368       static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    369   if (xievent->sourceid >= kMaxDeviceNum)
    370     return false;
    371   return cmt_devices_[xievent->sourceid];
    372 }
    373 
    374 bool DeviceDataManager::IsCMTGestureEvent(
    375     const base::NativeEvent& native_event) const {
    376   return (IsScrollEvent(native_event) ||
    377           IsFlingEvent(native_event) ||
    378           IsCMTMetricsEvent(native_event));
    379 }
    380 
    381 bool DeviceDataManager::HasEventData(
    382     const XIDeviceEvent* xiev, const DataType type) const {
    383   const int idx = valuator_lookup_[xiev->sourceid][type];
    384   return (idx >= 0) && XIMaskIsSet(xiev->valuators.mask, idx);
    385 }
    386 
    387 bool DeviceDataManager::IsScrollEvent(
    388     const base::NativeEvent& native_event) const {
    389   if (!IsCMTDeviceEvent(native_event))
    390     return false;
    391 
    392   XIDeviceEvent* xiev =
    393       static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    394   return (HasEventData(xiev, DT_CMT_SCROLL_X) ||
    395           HasEventData(xiev, DT_CMT_SCROLL_Y));
    396 }
    397 
    398 bool DeviceDataManager::IsFlingEvent(
    399     const base::NativeEvent& native_event) const {
    400   if (!IsCMTDeviceEvent(native_event))
    401     return false;
    402 
    403   XIDeviceEvent* xiev =
    404       static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    405   return (HasEventData(xiev, DT_CMT_FLING_X) &&
    406           HasEventData(xiev, DT_CMT_FLING_Y) &&
    407           HasEventData(xiev, DT_CMT_FLING_STATE));
    408 }
    409 
    410 bool DeviceDataManager::IsCMTMetricsEvent(
    411     const base::NativeEvent& native_event) const {
    412   if (!IsCMTDeviceEvent(native_event))
    413     return false;
    414 
    415   XIDeviceEvent* xiev =
    416       static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    417   return (HasEventData(xiev, DT_CMT_METRICS_TYPE) &&
    418           HasEventData(xiev, DT_CMT_METRICS_DATA1) &&
    419           HasEventData(xiev, DT_CMT_METRICS_DATA2));
    420 }
    421 
    422 bool DeviceDataManager::HasGestureTimes(
    423     const base::NativeEvent& native_event) const {
    424   if (!IsCMTDeviceEvent(native_event))
    425     return false;
    426 
    427   XIDeviceEvent* xiev =
    428       static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    429   return (HasEventData(xiev, DT_CMT_START_TIME) &&
    430           HasEventData(xiev, DT_CMT_END_TIME));
    431 }
    432 
    433 void DeviceDataManager::GetScrollOffsets(const base::NativeEvent& native_event,
    434                                          float* x_offset, float* y_offset,
    435                                          float* x_offset_ordinal,
    436                                          float* y_offset_ordinal,
    437                                          int* finger_count) {
    438   *x_offset = 0;
    439   *y_offset = 0;
    440   *x_offset_ordinal = 0;
    441   *y_offset_ordinal = 0;
    442   *finger_count = 2;
    443 
    444   XIDeviceEvent* xiev =
    445       static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    446   const float natural_scroll_factor = GetNaturalScrollFactor(xiev->sourceid);
    447   EventData data;
    448   GetEventRawData(*native_event, &data);
    449 
    450   if (data.find(DT_CMT_SCROLL_X) != data.end())
    451     *x_offset = data[DT_CMT_SCROLL_X] * natural_scroll_factor;
    452   if (data.find(DT_CMT_SCROLL_Y) != data.end())
    453     *y_offset = data[DT_CMT_SCROLL_Y] * natural_scroll_factor;
    454   if (data.find(DT_CMT_ORDINAL_X) != data.end())
    455     *x_offset_ordinal = data[DT_CMT_ORDINAL_X] * natural_scroll_factor;
    456   if (data.find(DT_CMT_ORDINAL_Y) != data.end())
    457     *y_offset_ordinal = data[DT_CMT_ORDINAL_Y] * natural_scroll_factor;
    458   if (data.find(DT_CMT_FINGER_COUNT) != data.end())
    459     *finger_count = static_cast<int>(data[DT_CMT_FINGER_COUNT]);
    460 }
    461 
    462 void DeviceDataManager::GetFlingData(const base::NativeEvent& native_event,
    463                                      float* vx, float* vy,
    464                                      float* vx_ordinal, float* vy_ordinal,
    465                                      bool* is_cancel) {
    466   *vx = 0;
    467   *vy = 0;
    468   *vx_ordinal = 0;
    469   *vy_ordinal = 0;
    470   *is_cancel = false;
    471 
    472   XIDeviceEvent* xiev =
    473       static_cast<XIDeviceEvent*>(native_event->xcookie.data);
    474   const float natural_scroll_factor = GetNaturalScrollFactor(xiev->sourceid);
    475   EventData data;
    476   GetEventRawData(*native_event, &data);
    477 
    478   if (data.find(DT_CMT_FLING_X) != data.end())
    479     *vx = data[DT_CMT_FLING_X] * natural_scroll_factor;
    480   if (data.find(DT_CMT_FLING_Y) != data.end())
    481     *vy = data[DT_CMT_FLING_Y] * natural_scroll_factor;
    482   if (data.find(DT_CMT_FLING_STATE) != data.end())
    483     *is_cancel = !!static_cast<unsigned int>(data[DT_CMT_FLING_STATE]);
    484   if (data.find(DT_CMT_ORDINAL_X) != data.end())
    485     *vx_ordinal = data[DT_CMT_ORDINAL_X] * natural_scroll_factor;
    486   if (data.find(DT_CMT_ORDINAL_Y) != data.end())
    487     *vy_ordinal = data[DT_CMT_ORDINAL_Y] * natural_scroll_factor;
    488 }
    489 
    490 void DeviceDataManager::GetMetricsData(const base::NativeEvent& native_event,
    491                                        GestureMetricsType* type,
    492                                        float* data1, float* data2) {
    493   *type = kGestureMetricsTypeUnknown;
    494   *data1 = 0;
    495   *data2 = 0;
    496 
    497   EventData data;
    498   GetEventRawData(*native_event, &data);
    499 
    500   if (data.find(DT_CMT_METRICS_TYPE) != data.end()) {
    501     int val = static_cast<int>(data[DT_CMT_METRICS_TYPE]);
    502     if (val == 0)
    503       *type = kGestureMetricsTypeNoisyGround;
    504     else
    505       *type = kGestureMetricsTypeUnknown;
    506   }
    507   if (data.find(DT_CMT_METRICS_DATA1) != data.end())
    508     *data1 = data[DT_CMT_METRICS_DATA1];
    509   if (data.find(DT_CMT_METRICS_DATA2) != data.end())
    510     *data2 = data[DT_CMT_METRICS_DATA2];
    511 }
    512 
    513 void DeviceDataManager::GetGestureTimes(const base::NativeEvent& native_event,
    514                                         double* start_time,
    515                                         double* end_time) {
    516   *start_time = 0;
    517   *end_time = 0;
    518 
    519   EventData data;
    520   GetEventRawData(*native_event, &data);
    521 
    522   if (data.find(DT_CMT_START_TIME) != data.end())
    523     *start_time = data[DT_CMT_START_TIME];
    524   if (data.find(DT_CMT_END_TIME) != data.end())
    525     *end_time = data[DT_CMT_END_TIME];
    526 }
    527 
    528 bool DeviceDataManager::NormalizeData(unsigned int deviceid,
    529                                       const DataType type,
    530                                       double* value) {
    531   double max_value;
    532   double min_value;
    533   if (GetDataRange(deviceid, type, &min_value, &max_value)) {
    534     *value = (*value - min_value) / (max_value - min_value);
    535     DCHECK(*value >= 0.0 && *value <= 1.0);
    536     return true;
    537   }
    538   return false;
    539 }
    540 
    541 bool DeviceDataManager::GetDataRange(unsigned int deviceid,
    542                                      const DataType type,
    543                                      double* min, double* max) {
    544   if (deviceid >= static_cast<unsigned int>(kMaxDeviceNum))
    545     return false;
    546   if (valuator_lookup_[deviceid][type] >= 0) {
    547     *min = valuator_min_[deviceid][type];
    548     *max = valuator_max_[deviceid][type];
    549     return true;
    550   }
    551   return false;
    552 }
    553 
    554 void DeviceDataManager::SetDeviceListForTest(
    555     const std::vector<unsigned int>& devices) {
    556   for (int i = 0; i < kMaxDeviceNum; ++i) {
    557     valuator_count_[i] = 0;
    558     valuator_lookup_[i].clear();
    559     data_type_lookup_[i].clear();
    560     valuator_min_[i].clear();
    561     valuator_max_[i].clear();
    562     for (int j = 0; j < kMaxSlotNum; j++)
    563       last_seen_valuator_[i][j].clear();
    564   }
    565 
    566   for (size_t i = 0; i < devices.size(); i++) {
    567     unsigned int deviceid = devices[i];
    568     valuator_lookup_[deviceid].resize(DT_LAST_ENTRY, -1);
    569     data_type_lookup_[deviceid].resize(DT_LAST_ENTRY, DT_LAST_ENTRY);
    570     valuator_min_[deviceid].resize(DT_LAST_ENTRY, 0);
    571     valuator_max_[deviceid].resize(DT_LAST_ENTRY, 0);
    572     for (int j = 0; j < kMaxSlotNum; j++)
    573       last_seen_valuator_[deviceid][j].resize(DT_LAST_ENTRY, 0);
    574   }
    575 }
    576 
    577 void DeviceDataManager::SetDeviceValuatorForTest(int deviceid,
    578                                                  int val_index,
    579                                                  DataType data_type,
    580                                                  double min,
    581                                                  double max) {
    582   valuator_lookup_[deviceid][data_type] = val_index;
    583   data_type_lookup_[deviceid][val_index] = data_type;
    584   valuator_min_[deviceid][data_type] = min;
    585   valuator_max_[deviceid][data_type] = max;
    586 }
    587 }  // namespace ui
    588