| OLD | NEW |
| (Empty) |
| 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_pump_aurax11.h" | |
| 14 #include "ui/base/events/event_constants.h" | |
| 15 #include "ui/base/events/event_utils.h" | |
| 16 #include "ui/base/x/device_list_cache_x.h" | |
| 17 #include "ui/base/x/x11_util.h" | |
| 18 | |
| 19 // Copied from xserver-properties.h | |
| 20 #define AXIS_LABEL_PROP_REL_HWHEEL "Rel Horiz Wheel" | |
| 21 #define AXIS_LABEL_PROP_REL_WHEEL "Rel Vert Wheel" | |
| 22 | |
| 23 // CMT specific timings | |
| 24 #define AXIS_LABEL_PROP_ABS_DBL_START_TIME "Abs Dbl Start Timestamp" | |
| 25 #define AXIS_LABEL_PROP_ABS_DBL_END_TIME "Abs Dbl End Timestamp" | |
| 26 | |
| 27 // Ordinal values | |
| 28 #define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X "Abs Dbl Ordinal X" | |
| 29 #define AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y "Abs Dbl Ordinal Y" | |
| 30 | |
| 31 // Fling properties | |
| 32 #define AXIS_LABEL_PROP_ABS_DBL_FLING_VX "Abs Dbl Fling X Velocity" | |
| 33 #define AXIS_LABEL_PROP_ABS_DBL_FLING_VY "Abs Dbl Fling Y Velocity" | |
| 34 #define AXIS_LABEL_PROP_ABS_FLING_STATE "Abs Fling State" | |
| 35 | |
| 36 #define AXIS_LABEL_PROP_ABS_FINGER_COUNT "Abs Finger Count" | |
| 37 | |
| 38 // Touchscreen multi-touch | |
| 39 #define AXIS_LABEL_ABS_MT_TOUCH_MAJOR "Abs MT Touch Major" | |
| 40 #define AXIS_LABEL_ABS_MT_TOUCH_MINOR "Abs MT Touch Minor" | |
| 41 #define AXIS_LABEL_ABS_MT_ORIENTATION "Abs MT Orientation" | |
| 42 #define AXIS_LABEL_ABS_MT_PRESSURE "Abs MT Pressure" | |
| 43 #define AXIS_LABEL_ABS_MT_SLOT_ID "Abs MT Slot ID" | |
| 44 #define AXIS_LABEL_ABS_MT_TRACKING_ID "Abs MT Tracking ID" | |
| 45 #define AXIS_LABEL_TOUCH_TIMESTAMP "Touch Timestamp" | |
| 46 | |
| 47 // When you add new data types, please make sure the order here is aligned | |
| 48 // with the order in the DataType enum in the header file because we assume | |
| 49 // they are in sync when updating the device list (see UpdateDeviceList). | |
| 50 const char* kCachedAtoms[] = { | |
| 51 AXIS_LABEL_PROP_REL_HWHEEL, | |
| 52 AXIS_LABEL_PROP_REL_WHEEL, | |
| 53 AXIS_LABEL_PROP_ABS_DBL_ORDINAL_X, | |
| 54 AXIS_LABEL_PROP_ABS_DBL_ORDINAL_Y, | |
| 55 AXIS_LABEL_PROP_ABS_DBL_START_TIME, | |
| 56 AXIS_LABEL_PROP_ABS_DBL_END_TIME, | |
| 57 AXIS_LABEL_PROP_ABS_DBL_FLING_VX, | |
| 58 AXIS_LABEL_PROP_ABS_DBL_FLING_VY, | |
| 59 AXIS_LABEL_PROP_ABS_FLING_STATE, | |
| 60 AXIS_LABEL_PROP_ABS_FINGER_COUNT, | |
| 61 AXIS_LABEL_ABS_MT_TOUCH_MAJOR, | |
| 62 AXIS_LABEL_ABS_MT_TOUCH_MINOR, | |
| 63 AXIS_LABEL_ABS_MT_ORIENTATION, | |
| 64 AXIS_LABEL_ABS_MT_PRESSURE, | |
| 65 #if !defined(USE_XI2_MT) | |
| 66 AXIS_LABEL_ABS_MT_SLOT_ID, | |
| 67 #endif | |
| 68 AXIS_LABEL_ABS_MT_TRACKING_ID, | |
| 69 AXIS_LABEL_TOUCH_TIMESTAMP, | |
| 70 | |
| 71 NULL | |
| 72 }; | |
| 73 | |
| 74 // Constants for checking if a data type lies in the range of CMT/Touch data | |
| 75 // types. | |
| 76 const int kCMTDataTypeStart = ui::DeviceDataManager::DT_CMT_SCROLL_X; | |
| 77 const int kCMTDataTypeEnd = ui::DeviceDataManager::DT_CMT_FINGER_COUNT; | |
| 78 const int kTouchDataTypeStart = ui::DeviceDataManager::DT_TOUCH_MAJOR; | |
| 79 const int kTouchDataTypeEnd = ui::DeviceDataManager::DT_TOUCH_RAW_TIMESTAMP; | |
| 80 | |
| 81 namespace ui { | |
| 82 | |
| 83 bool DeviceDataManager::IsCMTDataType(const int type) { | |
| 84 return (type >= kCMTDataTypeStart) && (type <= kCMTDataTypeEnd); | |
| 85 } | |
| 86 | |
| 87 bool DeviceDataManager::IsTouchDataType(const int type) { | |
| 88 return (type >= kTouchDataTypeStart) && (type <= kTouchDataTypeEnd); | |
| 89 } | |
| 90 | |
| 91 DeviceDataManager* DeviceDataManager::GetInstance() { | |
| 92 return Singleton<DeviceDataManager>::get(); | |
| 93 } | |
| 94 | |
| 95 DeviceDataManager::DeviceDataManager() | |
| 96 : natural_scroll_enabled_(false), | |
| 97 atom_cache_(ui::GetXDisplay(), kCachedAtoms) { | |
| 98 // Make sure the sizes of enum and kCachedAtoms are aligned. | |
| 99 CHECK(arraysize(kCachedAtoms) == static_cast<size_t>(DT_LAST_ENTRY) + 1); | |
| 100 UpdateDeviceList(ui::GetXDisplay()); | |
| 101 } | |
| 102 | |
| 103 DeviceDataManager::~DeviceDataManager() { | |
| 104 } | |
| 105 | |
| 106 float DeviceDataManager::GetNaturalScrollFactor(int sourceid) const { | |
| 107 // Natural scroll is touchpad-only. | |
| 108 if (sourceid >= kMaxDeviceNum || !touchpads_[sourceid]) | |
| 109 return -1.0f; | |
| 110 | |
| 111 return natural_scroll_enabled_ ? 1.0f : -1.0f; | |
| 112 } | |
| 113 | |
| 114 void DeviceDataManager::UpdateDeviceList(Display* display) { | |
| 115 cmt_devices_.reset(); | |
| 116 touchpads_.reset(); | |
| 117 for (int i = 0; i < kMaxDeviceNum; ++i) { | |
| 118 valuator_count_[i] = 0; | |
| 119 valuator_lookup_[i].clear(); | |
| 120 data_type_lookup_[i].clear(); | |
| 121 valuator_min_[i].clear(); | |
| 122 valuator_max_[i].clear(); | |
| 123 last_seen_valuator_[i].clear(); | |
| 124 } | |
| 125 | |
| 126 // Find all the touchpad devices. | |
| 127 XDeviceList dev_list = | |
| 128 ui::DeviceListCacheX::GetInstance()->GetXDeviceList(display); | |
| 129 Atom xi_touchpad = XInternAtom(display, XI_TOUCHPAD, false); | |
| 130 for (int i = 0; i < dev_list.count; ++i) | |
| 131 if (dev_list[i].type == xi_touchpad) | |
| 132 touchpads_[dev_list[i].id] = true; | |
| 133 | |
| 134 // Update the structs with new valuator information | |
| 135 XIDeviceList info_list = | |
| 136 ui::DeviceListCacheX::GetInstance()->GetXI2DeviceList(display); | |
| 137 Atom atoms[DT_LAST_ENTRY]; | |
| 138 for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type) | |
| 139 atoms[data_type] = atom_cache_.GetAtom(kCachedAtoms[data_type]); | |
| 140 | |
| 141 for (int i = 0; i < info_list.count; ++i) { | |
| 142 XIDeviceInfo* info = info_list.devices + i; | |
| 143 | |
| 144 // We currently handle only slave, non-keyboard devices | |
| 145 if (info->use != XISlavePointer && info->use != XIFloatingSlave) | |
| 146 continue; | |
| 147 | |
| 148 bool possible_cmt = false; | |
| 149 bool not_cmt = false; | |
| 150 const int deviceid = info->deviceid; | |
| 151 | |
| 152 for (int j = 0; j < info->num_classes; ++j) { | |
| 153 if (info->classes[j]->type == XIValuatorClass) | |
| 154 ++valuator_count_[deviceid]; | |
| 155 else if (info->classes[j]->type == XIScrollClass) | |
| 156 not_cmt = true; | |
| 157 } | |
| 158 | |
| 159 // Skip devices that don't use any valuator | |
| 160 if (!valuator_count_[deviceid]) | |
| 161 continue; | |
| 162 | |
| 163 valuator_lookup_[deviceid].resize(DT_LAST_ENTRY, -1); | |
| 164 data_type_lookup_[deviceid].resize( | |
| 165 valuator_count_[deviceid], DT_LAST_ENTRY); | |
| 166 valuator_min_[deviceid].resize(DT_LAST_ENTRY, 0); | |
| 167 valuator_max_[deviceid].resize(DT_LAST_ENTRY, 0); | |
| 168 last_seen_valuator_[deviceid].resize(DT_LAST_ENTRY, 0); | |
| 169 for (int j = 0; j < info->num_classes; ++j) { | |
| 170 if (info->classes[j]->type != XIValuatorClass) | |
| 171 continue; | |
| 172 | |
| 173 XIValuatorClassInfo* v = | |
| 174 reinterpret_cast<XIValuatorClassInfo*>(info->classes[j]); | |
| 175 for (int data_type = 0; data_type < DT_LAST_ENTRY; ++data_type) { | |
| 176 if (v->label == atoms[data_type]) { | |
| 177 valuator_lookup_[deviceid][data_type] = v->number; | |
| 178 data_type_lookup_[deviceid][v->number] = data_type; | |
| 179 valuator_min_[deviceid][data_type] = v->min; | |
| 180 valuator_max_[deviceid][data_type] = v->max; | |
| 181 if (IsCMTDataType(data_type)) | |
| 182 possible_cmt = true; | |
| 183 break; | |
| 184 } | |
| 185 } | |
| 186 } | |
| 187 | |
| 188 if (possible_cmt && !not_cmt) | |
| 189 cmt_devices_[deviceid] = true; | |
| 190 } | |
| 191 } | |
| 192 | |
| 193 void DeviceDataManager::GetEventRawData(const XEvent& xev, EventData* data) { | |
| 194 if (xev.type != GenericEvent) | |
| 195 return; | |
| 196 | |
| 197 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); | |
| 198 if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum) | |
| 199 return; | |
| 200 data->clear(); | |
| 201 const int sourceid = xiev->sourceid; | |
| 202 double* valuators = xiev->valuators.values; | |
| 203 for (int i = 0; i <= valuator_count_[sourceid]; ++i) { | |
| 204 if (XIMaskIsSet(xiev->valuators.mask, i)) { | |
| 205 int type = data_type_lookup_[sourceid][i]; | |
| 206 if (type != DT_LAST_ENTRY) { | |
| 207 (*data)[type] = *valuators; | |
| 208 if (IsTouchDataType(type)) | |
| 209 last_seen_valuator_[sourceid][type] = *valuators; | |
| 210 } | |
| 211 valuators++; | |
| 212 } | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 bool DeviceDataManager::GetEventData(const XEvent& xev, | |
| 217 const DataType type, double* value) { | |
| 218 if (xev.type != GenericEvent) | |
| 219 return false; | |
| 220 | |
| 221 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); | |
| 222 if (xiev->sourceid >= kMaxDeviceNum || xiev->deviceid >= kMaxDeviceNum) | |
| 223 return false; | |
| 224 const int sourceid = xiev->sourceid; | |
| 225 if (valuator_lookup_[sourceid].empty()) | |
| 226 return false; | |
| 227 | |
| 228 int val_index = valuator_lookup_[sourceid][type]; | |
| 229 if (val_index >= 0) { | |
| 230 if (XIMaskIsSet(xiev->valuators.mask, val_index)) { | |
| 231 double* valuators = xiev->valuators.values; | |
| 232 while (val_index--) { | |
| 233 if (XIMaskIsSet(xiev->valuators.mask, val_index)) | |
| 234 ++valuators; | |
| 235 } | |
| 236 *value = *valuators; | |
| 237 last_seen_valuator_[sourceid][type] = *value; | |
| 238 return true; | |
| 239 } else if (IsTouchDataType(type)) { | |
| 240 *value = last_seen_valuator_[sourceid][type]; | |
| 241 } | |
| 242 } | |
| 243 | |
| 244 #if defined(USE_XI2_MT) | |
| 245 // With XInput2 MT, Tracking ID is provided in the detail field. | |
| 246 if (type == DT_TOUCH_TRACKING_ID) { | |
| 247 *value = xiev->detail; | |
| 248 return true; | |
| 249 } | |
| 250 #endif | |
| 251 | |
| 252 return false; | |
| 253 } | |
| 254 | |
| 255 bool DeviceDataManager::IsTouchpadXInputEvent( | |
| 256 const base::NativeEvent& native_event) const { | |
| 257 if (native_event->type != GenericEvent) | |
| 258 return false; | |
| 259 | |
| 260 XIDeviceEvent* xievent = | |
| 261 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 262 if (xievent->sourceid >= kMaxDeviceNum) | |
| 263 return false; | |
| 264 return touchpads_[xievent->sourceid]; | |
| 265 } | |
| 266 | |
| 267 bool DeviceDataManager::IsCMTDeviceEvent( | |
| 268 const base::NativeEvent& native_event) const { | |
| 269 if (native_event->type != GenericEvent) | |
| 270 return false; | |
| 271 | |
| 272 XIDeviceEvent* xievent = | |
| 273 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 274 if (xievent->sourceid >= kMaxDeviceNum) | |
| 275 return false; | |
| 276 return cmt_devices_[xievent->sourceid]; | |
| 277 } | |
| 278 | |
| 279 bool DeviceDataManager::IsCMTGestureEvent( | |
| 280 const base::NativeEvent& native_event) const { | |
| 281 return (IsScrollEvent(native_event) || | |
| 282 IsFlingEvent(native_event)); | |
| 283 } | |
| 284 | |
| 285 bool DeviceDataManager::HasEventData( | |
| 286 const XIDeviceEvent* xiev, const DataType type) const { | |
| 287 const int idx = valuator_lookup_[xiev->sourceid][type]; | |
| 288 return (idx >= 0) && XIMaskIsSet(xiev->valuators.mask, idx); | |
| 289 } | |
| 290 | |
| 291 bool DeviceDataManager::IsScrollEvent( | |
| 292 const base::NativeEvent& native_event) const { | |
| 293 if (!IsCMTDeviceEvent(native_event)) | |
| 294 return false; | |
| 295 | |
| 296 XIDeviceEvent* xiev = | |
| 297 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 298 return (HasEventData(xiev, DT_CMT_SCROLL_X) || | |
| 299 HasEventData(xiev, DT_CMT_SCROLL_Y)); | |
| 300 } | |
| 301 | |
| 302 bool DeviceDataManager::IsFlingEvent( | |
| 303 const base::NativeEvent& native_event) const { | |
| 304 if (!IsCMTDeviceEvent(native_event)) | |
| 305 return false; | |
| 306 | |
| 307 XIDeviceEvent* xiev = | |
| 308 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 309 return (HasEventData(xiev, DT_CMT_FLING_X) && | |
| 310 HasEventData(xiev, DT_CMT_FLING_Y) && | |
| 311 HasEventData(xiev, DT_CMT_FLING_STATE)); | |
| 312 } | |
| 313 | |
| 314 bool DeviceDataManager::HasGestureTimes( | |
| 315 const base::NativeEvent& native_event) const { | |
| 316 if (!IsCMTDeviceEvent(native_event)) | |
| 317 return false; | |
| 318 | |
| 319 XIDeviceEvent* xiev = | |
| 320 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 321 return (HasEventData(xiev, DT_CMT_START_TIME) && | |
| 322 HasEventData(xiev, DT_CMT_END_TIME)); | |
| 323 } | |
| 324 | |
| 325 void DeviceDataManager::GetScrollOffsets(const base::NativeEvent& native_event, | |
| 326 float* x_offset, float* y_offset, | |
| 327 float* x_offset_ordinal, | |
| 328 float* y_offset_ordinal, | |
| 329 int* finger_count) { | |
| 330 *x_offset = 0; | |
| 331 *y_offset = 0; | |
| 332 *x_offset_ordinal = 0; | |
| 333 *y_offset_ordinal = 0; | |
| 334 *finger_count = 2; | |
| 335 | |
| 336 XIDeviceEvent* xiev = | |
| 337 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 338 const float natural_scroll_factor = GetNaturalScrollFactor(xiev->sourceid); | |
| 339 EventData data; | |
| 340 GetEventRawData(*native_event, &data); | |
| 341 | |
| 342 if (data.find(DT_CMT_SCROLL_X) != data.end()) | |
| 343 *x_offset = data[DT_CMT_SCROLL_X] * natural_scroll_factor; | |
| 344 if (data.find(DT_CMT_SCROLL_Y) != data.end()) | |
| 345 *y_offset = data[DT_CMT_SCROLL_Y] * natural_scroll_factor; | |
| 346 if (data.find(DT_CMT_ORDINAL_X) != data.end()) | |
| 347 *x_offset_ordinal = data[DT_CMT_ORDINAL_X] * natural_scroll_factor; | |
| 348 if (data.find(DT_CMT_ORDINAL_Y) != data.end()) | |
| 349 *y_offset_ordinal = data[DT_CMT_ORDINAL_Y] * natural_scroll_factor; | |
| 350 if (data.find(DT_CMT_FINGER_COUNT) != data.end()) | |
| 351 *finger_count = static_cast<int>(data[DT_CMT_FINGER_COUNT]); | |
| 352 } | |
| 353 | |
| 354 void DeviceDataManager::GetFlingData(const base::NativeEvent& native_event, | |
| 355 float* vx, float* vy, | |
| 356 float* vx_ordinal, float* vy_ordinal, | |
| 357 bool* is_cancel) { | |
| 358 *vx = 0; | |
| 359 *vy = 0; | |
| 360 *vx_ordinal = 0; | |
| 361 *vy_ordinal = 0; | |
| 362 *is_cancel = false; | |
| 363 | |
| 364 XIDeviceEvent* xiev = | |
| 365 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 366 const float natural_scroll_factor = GetNaturalScrollFactor(xiev->sourceid); | |
| 367 EventData data; | |
| 368 GetEventRawData(*native_event, &data); | |
| 369 | |
| 370 if (data.find(DT_CMT_FLING_X) != data.end()) | |
| 371 *vx = data[DT_CMT_FLING_X] * natural_scroll_factor; | |
| 372 if (data.find(DT_CMT_FLING_Y) != data.end()) | |
| 373 *vy = data[DT_CMT_FLING_Y] * natural_scroll_factor; | |
| 374 if (data.find(DT_CMT_FLING_STATE) != data.end()) | |
| 375 *is_cancel = !!static_cast<unsigned int>(data[DT_CMT_FLING_STATE]); | |
| 376 if (data.find(DT_CMT_ORDINAL_X) != data.end()) | |
| 377 *vx_ordinal = data[DT_CMT_ORDINAL_X] * natural_scroll_factor; | |
| 378 if (data.find(DT_CMT_ORDINAL_Y) != data.end()) | |
| 379 *vy_ordinal = data[DT_CMT_ORDINAL_Y] * natural_scroll_factor; | |
| 380 } | |
| 381 | |
| 382 void DeviceDataManager::GetGestureTimes(const base::NativeEvent& native_event, | |
| 383 double* start_time, | |
| 384 double* end_time) { | |
| 385 *start_time = 0; | |
| 386 *end_time = 0; | |
| 387 | |
| 388 EventData data; | |
| 389 GetEventRawData(*native_event, &data); | |
| 390 | |
| 391 if (data.find(DT_CMT_START_TIME) != data.end()) | |
| 392 *start_time = data[DT_CMT_START_TIME]; | |
| 393 if (data.find(DT_CMT_END_TIME) != data.end()) | |
| 394 *end_time = data[DT_CMT_END_TIME]; | |
| 395 } | |
| 396 | |
| 397 bool DeviceDataManager::NormalizeData(unsigned int deviceid, | |
| 398 const DataType type, | |
| 399 double* value) { | |
| 400 double max_value; | |
| 401 double min_value; | |
| 402 if (GetDataRange(deviceid, type, &min_value, &max_value)) { | |
| 403 *value = (*value - min_value) / (max_value - min_value); | |
| 404 DCHECK(*value >= 0.0 && *value <= 1.0); | |
| 405 return true; | |
| 406 } | |
| 407 return false; | |
| 408 } | |
| 409 | |
| 410 bool DeviceDataManager::GetDataRange(unsigned int deviceid, | |
| 411 const DataType type, | |
| 412 double* min, double* max) { | |
| 413 if (deviceid >= static_cast<unsigned int>(kMaxDeviceNum)) | |
| 414 return false; | |
| 415 if (valuator_lookup_[deviceid][type] >= 0) { | |
| 416 *min = valuator_min_[deviceid][type]; | |
| 417 *max = valuator_max_[deviceid][type]; | |
| 418 return true; | |
| 419 } | |
| 420 return false; | |
| 421 } | |
| 422 | |
| 423 } // namespace ui | |
| OLD | NEW |