| OLD | NEW |
| (Empty) |
| 1 // Copyright (c) 2012 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/events.h" | |
| 6 | |
| 7 #include <X11/Xlib.h> | |
| 8 #include <X11/extensions/XInput.h> | |
| 9 #include <X11/extensions/XInput2.h> | |
| 10 #include <string.h> | |
| 11 | |
| 12 #include "base/command_line.h" | |
| 13 #include "base/logging.h" | |
| 14 #include "base/message_pump_x.h" | |
| 15 #include "ui/base/keycodes/keyboard_code_conversion_x.h" | |
| 16 #include "ui/base/ui_base_switches.h" | |
| 17 #include "ui/base/touch/touch_factory.h" | |
| 18 #include "ui/base/x/x11_util.h" | |
| 19 #include "ui/gfx/point.h" | |
| 20 #include "ui/gfx/monitor.h" | |
| 21 #include "ui/gfx/rect.h" | |
| 22 #include "ui/gfx/screen.h" | |
| 23 | |
| 24 // Copied from xserver-properties.h | |
| 25 #define AXIS_LABEL_PROP_REL_HWHEEL "Rel Horiz Wheel" | |
| 26 #define AXIS_LABEL_PROP_REL_WHEEL "Rel Vert Wheel" | |
| 27 | |
| 28 // CMT specific timings | |
| 29 #define AXIS_LABEL_PROP_ABS_START_TIME "Abs Start Timestamp" | |
| 30 #define AXIS_LABEL_PROP_ABS_END_TIME "Abs End Timestamp" | |
| 31 | |
| 32 // Fling properties | |
| 33 #define AXIS_LABEL_PROP_ABS_FLING_X "Abs Fling X Velocity" | |
| 34 #define AXIS_LABEL_PROP_ABS_FLING_Y "Abs Fling Y Velocity" | |
| 35 #define AXIS_LABEL_PROP_ABS_FLING_STATE "Abs Fling State" | |
| 36 | |
| 37 // New versions of the valuators, with double values instead of fixed point. | |
| 38 #define AXIS_LABEL_PROP_ABS_DBL_START_TIME "Abs Dbl Start Timestamp" | |
| 39 #define AXIS_LABEL_PROP_ABS_DBL_END_TIME "Abs Dbl End Timestamp" | |
| 40 #define AXIS_LABEL_PROP_ABS_DBL_FLING_VX "Abs Dbl Fling X Velocity" | |
| 41 #define AXIS_LABEL_PROP_ABS_DBL_FLING_VY "Abs Dbl Fling Y Velocity" | |
| 42 | |
| 43 namespace { | |
| 44 | |
| 45 // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+. | |
| 46 const int kWheelScrollAmount = 53; | |
| 47 | |
| 48 const int kMinWheelButton = 4; | |
| 49 const int kMaxWheelButton = 7; | |
| 50 | |
| 51 // A class to support the detection of scroll events, using X11 valuators. | |
| 52 class UI_EXPORT CMTEventData { | |
| 53 public: | |
| 54 // Returns the ScrollEventData singleton. | |
| 55 static CMTEventData* GetInstance() { | |
| 56 return Singleton<CMTEventData>::get(); | |
| 57 } | |
| 58 | |
| 59 // Updates the list of devices. | |
| 60 void UpdateDeviceList(Display* display) { | |
| 61 cmt_devices_.reset(); | |
| 62 touchpads_.reset(); | |
| 63 device_to_valuators_.clear(); | |
| 64 | |
| 65 int count = 0; | |
| 66 | |
| 67 // Find all the touchpad devices. | |
| 68 XDeviceInfo* dev_list = XListInputDevices(display, &count); | |
| 69 Atom xi_touchpad = XInternAtom(display, XI_TOUCHPAD, false); | |
| 70 for (int i = 0; i < count; ++i) { | |
| 71 XDeviceInfo* dev = dev_list + i; | |
| 72 if (dev->type == xi_touchpad) | |
| 73 touchpads_[dev_list[i].id] = true; | |
| 74 } | |
| 75 if (dev_list) | |
| 76 XFreeDeviceList(dev_list); | |
| 77 | |
| 78 XIDeviceInfo* info_list = XIQueryDevice(display, XIAllDevices, &count); | |
| 79 Atom x_axis = XInternAtom(display, AXIS_LABEL_PROP_REL_HWHEEL, false); | |
| 80 Atom y_axis = XInternAtom(display, AXIS_LABEL_PROP_REL_WHEEL, false); | |
| 81 Atom start_time = | |
| 82 XInternAtom(display, AXIS_LABEL_PROP_ABS_START_TIME, false); | |
| 83 Atom start_time_dbl = | |
| 84 XInternAtom(display, AXIS_LABEL_PROP_ABS_DBL_START_TIME, false); | |
| 85 Atom end_time = XInternAtom(display, AXIS_LABEL_PROP_ABS_END_TIME, false); | |
| 86 Atom end_time_dbl = | |
| 87 XInternAtom(display, AXIS_LABEL_PROP_ABS_DBL_END_TIME, false); | |
| 88 Atom fling_vx = XInternAtom(display, AXIS_LABEL_PROP_ABS_FLING_X, false); | |
| 89 Atom fling_vx_dbl = | |
| 90 XInternAtom(display, AXIS_LABEL_PROP_ABS_DBL_FLING_VX, false); | |
| 91 Atom fling_vy = XInternAtom(display, AXIS_LABEL_PROP_ABS_FLING_Y, false); | |
| 92 Atom fling_vy_dbl = | |
| 93 XInternAtom(display, AXIS_LABEL_PROP_ABS_DBL_FLING_VY, false); | |
| 94 Atom fling_state = | |
| 95 XInternAtom(display, AXIS_LABEL_PROP_ABS_FLING_STATE, false); | |
| 96 | |
| 97 for (int i = 0; i < count; ++i) { | |
| 98 XIDeviceInfo* info = info_list + i; | |
| 99 | |
| 100 if (info->use != XISlavePointer && info->use != XIFloatingSlave) | |
| 101 continue; | |
| 102 | |
| 103 Valuators valuators; | |
| 104 bool is_cmt = false; | |
| 105 for (int j = 0; j < info->num_classes; ++j) { | |
| 106 if (info->classes[j]->type != XIValuatorClass) | |
| 107 continue; | |
| 108 | |
| 109 XIValuatorClassInfo* v = | |
| 110 reinterpret_cast<XIValuatorClassInfo*>(info->classes[j]); | |
| 111 int number = v->number; | |
| 112 if (number > valuators.max) | |
| 113 valuators.max = number; | |
| 114 if (v->label == x_axis) { | |
| 115 valuators.scroll_x = number; | |
| 116 is_cmt = true; | |
| 117 } else if (v->label == y_axis) { | |
| 118 valuators.scroll_y = number; | |
| 119 is_cmt = true; | |
| 120 } else if (v->label == start_time) { | |
| 121 valuators.start_time = number; | |
| 122 is_cmt = true; | |
| 123 } else if (v->label == start_time_dbl) { | |
| 124 valuators.start_time_dbl = number; | |
| 125 is_cmt = true; | |
| 126 } else if (v->label == end_time) { | |
| 127 valuators.end_time = number; | |
| 128 is_cmt = true; | |
| 129 } else if (v->label == end_time_dbl) { | |
| 130 valuators.end_time_dbl = number; | |
| 131 is_cmt = true; | |
| 132 } else if (v->label == fling_vx) { | |
| 133 valuators.fling_vx = number; | |
| 134 is_cmt = true; | |
| 135 } else if (v->label == fling_vx_dbl) { | |
| 136 valuators.fling_vx_dbl = number; | |
| 137 is_cmt = true; | |
| 138 } else if (v->label == fling_vy) { | |
| 139 valuators.fling_vy = number; | |
| 140 is_cmt = true; | |
| 141 } else if (v->label == fling_vy_dbl) { | |
| 142 valuators.fling_vy_dbl = number; | |
| 143 is_cmt = true; | |
| 144 } else if (v->label == fling_state) { | |
| 145 valuators.fling_state = number; | |
| 146 is_cmt = true; | |
| 147 } | |
| 148 } | |
| 149 if (is_cmt) { | |
| 150 // Double valuators override fixed point ones. | |
| 151 if (valuators.start_time_dbl >= 0) | |
| 152 valuators.start_time = -1; | |
| 153 if (valuators.end_time_dbl >= 0) | |
| 154 valuators.end_time = -1; | |
| 155 if (valuators.fling_vx_dbl >= 0) | |
| 156 valuators.fling_vx = -1; | |
| 157 if (valuators.fling_vy_dbl >= 0) | |
| 158 valuators.fling_vy = -1; | |
| 159 device_to_valuators_[info->deviceid] = valuators; | |
| 160 cmt_devices_[info->deviceid] = true; | |
| 161 } | |
| 162 } | |
| 163 if (info_list) | |
| 164 XIFreeDeviceInfo(info_list); | |
| 165 } | |
| 166 | |
| 167 bool natural_scroll_enabled() const { return natural_scroll_enabled_; } | |
| 168 void set_natural_scroll_enabled(bool enabled) { | |
| 169 natural_scroll_enabled_ = enabled; | |
| 170 } | |
| 171 | |
| 172 bool IsTouchpadXInputEvent(const base::NativeEvent& native_event) { | |
| 173 if (native_event->type != GenericEvent) | |
| 174 return false; | |
| 175 | |
| 176 XIDeviceEvent* xievent = | |
| 177 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 178 return touchpads_[xievent->sourceid]; | |
| 179 } | |
| 180 | |
| 181 float GetNaturalScrollFactor(int deviceid) { | |
| 182 // Natural scroll is touchpad-only. | |
| 183 if (!touchpads_[deviceid]) | |
| 184 return -1.0f; | |
| 185 | |
| 186 return natural_scroll_enabled_ ? 1.0f : -1.0f; | |
| 187 } | |
| 188 | |
| 189 // Returns true if this is a scroll event (a motion event with the necessary | |
| 190 // valuators. Also returns the offsets. |x_offset| and |y_offset| can be | |
| 191 // NULL. | |
| 192 bool GetScrollOffsets(const XEvent& xev, float* x_offset, float* y_offset) { | |
| 193 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); | |
| 194 | |
| 195 if (x_offset) | |
| 196 *x_offset = 0; | |
| 197 if (y_offset) | |
| 198 *y_offset = 0; | |
| 199 | |
| 200 const int deviceid = xiev->deviceid; | |
| 201 if (!cmt_devices_[deviceid]) | |
| 202 return false; | |
| 203 | |
| 204 const float natural_scroll_factor = GetNaturalScrollFactor(deviceid); | |
| 205 const Valuators v = device_to_valuators_[deviceid]; | |
| 206 const bool has_x_offset = XIMaskIsSet(xiev->valuators.mask, v.scroll_x); | |
| 207 const bool has_y_offset = XIMaskIsSet(xiev->valuators.mask, v.scroll_y); | |
| 208 const bool is_scroll = has_x_offset || has_y_offset; | |
| 209 | |
| 210 if (!is_scroll || (!x_offset && !y_offset)) | |
| 211 return is_scroll; | |
| 212 | |
| 213 double* valuators = xiev->valuators.values; | |
| 214 for (int i = 0; i <= v.max; ++i) { | |
| 215 if (XIMaskIsSet(xiev->valuators.mask, i)) { | |
| 216 if (x_offset && v.scroll_x == i) | |
| 217 *x_offset = *valuators * natural_scroll_factor; | |
| 218 else if (y_offset && v.scroll_y == i) | |
| 219 *y_offset = *valuators * natural_scroll_factor; | |
| 220 valuators++; | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 return true; | |
| 225 } | |
| 226 | |
| 227 bool GetFlingData(const XEvent& xev, | |
| 228 float* vx, float* vy, | |
| 229 bool* is_cancel) { | |
| 230 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); | |
| 231 | |
| 232 *vx = 0; | |
| 233 *vy = 0; | |
| 234 *is_cancel = false; | |
| 235 | |
| 236 const int deviceid = xiev->deviceid; | |
| 237 if (!cmt_devices_[deviceid]) | |
| 238 return false; | |
| 239 | |
| 240 const float natural_scroll_factor = GetNaturalScrollFactor(deviceid); | |
| 241 const Valuators v = device_to_valuators_[deviceid]; | |
| 242 if ((!XIMaskIsSet(xiev->valuators.mask, v.fling_vx) && | |
| 243 !XIMaskIsSet(xiev->valuators.mask, v.fling_vx_dbl)) || | |
| 244 (!XIMaskIsSet(xiev->valuators.mask, v.fling_vy) && | |
| 245 !XIMaskIsSet(xiev->valuators.mask, v.fling_vy_dbl)) || | |
| 246 !XIMaskIsSet(xiev->valuators.mask, v.fling_state)) | |
| 247 return false; | |
| 248 | |
| 249 double* valuators = xiev->valuators.values; | |
| 250 for (int i = 0; i <= v.max; ++i) { | |
| 251 if (XIMaskIsSet(xiev->valuators.mask, i)) { | |
| 252 // Convert values to unsigned ints representing ms before storing them, | |
| 253 // as that is how they were encoded before conversion to doubles. | |
| 254 if (v.fling_vx_dbl == i) { | |
| 255 *vx = natural_scroll_factor * *valuators; | |
| 256 } else if (v.fling_vx == i) { | |
| 257 *vx = natural_scroll_factor * | |
| 258 static_cast<double>(static_cast<int>(*valuators)) / 1000.0f; | |
| 259 } else if (v.fling_vy_dbl == i) { | |
| 260 *vy = natural_scroll_factor * *valuators; | |
| 261 } else if (v.fling_vy == i) { | |
| 262 *vy = natural_scroll_factor * | |
| 263 static_cast<double>(static_cast<int>(*valuators)) / 1000.0f; | |
| 264 } else if (v.fling_state == i) { | |
| 265 *is_cancel = !!static_cast<unsigned int>(*valuators); | |
| 266 } | |
| 267 valuators++; | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 return true; | |
| 272 } | |
| 273 | |
| 274 bool GetGestureTimes(const XEvent& xev, | |
| 275 double* start_time, | |
| 276 double* end_time) { | |
| 277 *start_time = 0; | |
| 278 *end_time = 0; | |
| 279 | |
| 280 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); | |
| 281 if (!cmt_devices_[xiev->deviceid]) | |
| 282 return false; | |
| 283 | |
| 284 Valuators v = device_to_valuators_[xiev->deviceid]; | |
| 285 if ((!XIMaskIsSet(xiev->valuators.mask, v.start_time) && | |
| 286 !XIMaskIsSet(xiev->valuators.mask, v.start_time_dbl)) || | |
| 287 (!XIMaskIsSet(xiev->valuators.mask, v.end_time) && | |
| 288 !XIMaskIsSet(xiev->valuators.mask, v.end_time_dbl))) | |
| 289 return false; | |
| 290 | |
| 291 double* valuators = xiev->valuators.values; | |
| 292 for (int i = 0; i <= v.max; ++i) { | |
| 293 if (XIMaskIsSet(xiev->valuators.mask, i)) { | |
| 294 if (v.start_time_dbl == i) { | |
| 295 *start_time = *valuators; | |
| 296 } else if (v.start_time == i) { | |
| 297 // Convert values to unsigned ints representing ms before storing | |
| 298 // them, as that is how they were encoded before conversion | |
| 299 // to doubles. | |
| 300 *start_time = | |
| 301 static_cast<double>( | |
| 302 static_cast<unsigned int>(*valuators)) / 1000; | |
| 303 } else if (v.end_time_dbl == i) { | |
| 304 *end_time = *valuators; | |
| 305 } else if (v.end_time == i) { | |
| 306 // Convert values to unsigned ints representing ms before storing | |
| 307 // them, as that is how they were encoded before conversion | |
| 308 // to doubles. | |
| 309 *end_time = | |
| 310 static_cast<double>( | |
| 311 static_cast<unsigned int>(*valuators)) / 1000; | |
| 312 } | |
| 313 valuators++; | |
| 314 } | |
| 315 } | |
| 316 | |
| 317 return true; | |
| 318 } | |
| 319 | |
| 320 private: | |
| 321 // Requirement for Singleton | |
| 322 friend struct DefaultSingletonTraits<CMTEventData>; | |
| 323 | |
| 324 struct Valuators { | |
| 325 int max; | |
| 326 int scroll_x; | |
| 327 int scroll_y; | |
| 328 int start_time; | |
| 329 int end_time; | |
| 330 int fling_vx; | |
| 331 int fling_vy; | |
| 332 int fling_state; | |
| 333 // *_dbl valuators take precedence over the fixed precision versions. | |
| 334 int start_time_dbl; | |
| 335 int end_time_dbl; | |
| 336 int fling_vx_dbl; | |
| 337 int fling_vy_dbl; | |
| 338 | |
| 339 Valuators() | |
| 340 : max(-1), | |
| 341 scroll_x(-1), | |
| 342 scroll_y(-1), | |
| 343 start_time(-1), | |
| 344 end_time(-1), | |
| 345 fling_vx(-1), | |
| 346 fling_vy(-1), | |
| 347 fling_state(-1), | |
| 348 start_time_dbl(-1), | |
| 349 end_time_dbl(-1), | |
| 350 fling_vx_dbl(-1), | |
| 351 fling_vy_dbl(-1) { | |
| 352 } | |
| 353 | |
| 354 }; | |
| 355 | |
| 356 CMTEventData() : natural_scroll_enabled_(false) { | |
| 357 UpdateDeviceList(ui::GetXDisplay()); | |
| 358 } | |
| 359 | |
| 360 ~CMTEventData() {} | |
| 361 | |
| 362 // A quick lookup table for determining if events from the pointer device | |
| 363 // should be processed. | |
| 364 static const int kMaxDeviceNum = 128; | |
| 365 bool natural_scroll_enabled_; | |
| 366 std::bitset<kMaxDeviceNum> cmt_devices_; | |
| 367 std::bitset<kMaxDeviceNum> touchpads_; | |
| 368 std::map<int, Valuators> device_to_valuators_; | |
| 369 | |
| 370 DISALLOW_COPY_AND_ASSIGN(CMTEventData); | |
| 371 }; | |
| 372 | |
| 373 // A class to track current modifier state on master device. Only track ctrl, | |
| 374 // alt, shift and caps lock keys currently. The tracked state can then be used | |
| 375 // by floating device. | |
| 376 class UI_EXPORT XModifierStateWatcher{ | |
| 377 public: | |
| 378 static XModifierStateWatcher* GetInstance() { | |
| 379 return Singleton<XModifierStateWatcher>::get(); | |
| 380 } | |
| 381 | |
| 382 void UpdateStateFromEvent(const base::NativeEvent& native_event) { | |
| 383 // Floating device can't access the modifer state from master device. | |
| 384 // We need to track the states of modifier keys in a singleton for | |
| 385 // floating devices such as touch screen. Issue 106426 is one example | |
| 386 // of why we need the modifier states for floating device. | |
| 387 state_ = native_event->xkey.state; | |
| 388 // master_state is the state before key press. We need to track the | |
| 389 // state after key press for floating device. Currently only ctrl, | |
| 390 // shift, alt and caps lock keys are tracked. | |
| 391 ui::KeyboardCode keyboard_code = ui::KeyboardCodeFromNative(native_event); | |
| 392 unsigned int mask = 0; | |
| 393 | |
| 394 switch (keyboard_code) { | |
| 395 case ui::VKEY_CONTROL: { | |
| 396 mask = ControlMask; | |
| 397 break; | |
| 398 } | |
| 399 case ui::VKEY_SHIFT: { | |
| 400 mask = ShiftMask; | |
| 401 break; | |
| 402 } | |
| 403 case ui::VKEY_MENU: { | |
| 404 mask = Mod1Mask; | |
| 405 break; | |
| 406 } | |
| 407 case ui::VKEY_CAPITAL: { | |
| 408 mask = LockMask; | |
| 409 break; | |
| 410 } | |
| 411 default: | |
| 412 break; | |
| 413 } | |
| 414 | |
| 415 if (native_event->type == KeyPress) | |
| 416 state_ |= mask; | |
| 417 else | |
| 418 state_ &= ~mask; | |
| 419 } | |
| 420 | |
| 421 // Returns the current modifer state in master device. It only contains the | |
| 422 // state of ctrl, shift, alt and caps lock keys. | |
| 423 unsigned int state() { return state_; } | |
| 424 | |
| 425 private: | |
| 426 friend struct DefaultSingletonTraits<XModifierStateWatcher>; | |
| 427 | |
| 428 XModifierStateWatcher() : state_(0) { } | |
| 429 | |
| 430 unsigned int state_; | |
| 431 | |
| 432 DISALLOW_COPY_AND_ASSIGN(XModifierStateWatcher); | |
| 433 }; | |
| 434 | |
| 435 int GetEventFlagsFromXState(unsigned int state) { | |
| 436 int flags = 0; | |
| 437 if (state & ControlMask) | |
| 438 flags |= ui::EF_CONTROL_DOWN; | |
| 439 if (state & ShiftMask) | |
| 440 flags |= ui::EF_SHIFT_DOWN; | |
| 441 if (state & Mod1Mask) | |
| 442 flags |= ui::EF_ALT_DOWN; | |
| 443 if (state & LockMask) | |
| 444 flags |= ui::EF_CAPS_LOCK_DOWN; | |
| 445 if (state & Button1Mask) | |
| 446 flags |= ui::EF_LEFT_MOUSE_BUTTON; | |
| 447 if (state & Button2Mask) | |
| 448 flags |= ui::EF_MIDDLE_MOUSE_BUTTON; | |
| 449 if (state & Button3Mask) | |
| 450 flags |= ui::EF_RIGHT_MOUSE_BUTTON; | |
| 451 return flags; | |
| 452 } | |
| 453 | |
| 454 // Get the event flag for the button in XButtonEvent. During a ButtonPress | |
| 455 // event, |state| in XButtonEvent does not include the button that has just been | |
| 456 // pressed. Instead |state| contains flags for the buttons (if any) that had | |
| 457 // already been pressed before the current button, and |button| stores the most | |
| 458 // current pressed button. So, if you press down left mouse button, and while | |
| 459 // pressing it down, press down the right mouse button, then for the latter | |
| 460 // event, |state| would have Button1Mask set but not Button3Mask, and |button| | |
| 461 // would be 3. | |
| 462 int GetEventFlagsForButton(int button) { | |
| 463 switch (button) { | |
| 464 case 1: | |
| 465 return ui::EF_LEFT_MOUSE_BUTTON; | |
| 466 case 2: | |
| 467 return ui::EF_MIDDLE_MOUSE_BUTTON; | |
| 468 case 3: | |
| 469 return ui::EF_RIGHT_MOUSE_BUTTON; | |
| 470 default: | |
| 471 return 0; | |
| 472 } | |
| 473 } | |
| 474 | |
| 475 int GetButtonMaskForX2Event(XIDeviceEvent* xievent) { | |
| 476 int buttonflags = 0; | |
| 477 for (int i = 0; i < 8 * xievent->buttons.mask_len; i++) { | |
| 478 if (XIMaskIsSet(xievent->buttons.mask, i)) { | |
| 479 int button = (xievent->sourceid == xievent->deviceid) ? | |
| 480 ui::GetMappedButton(i) : i; | |
| 481 buttonflags |= GetEventFlagsForButton(button); | |
| 482 } | |
| 483 } | |
| 484 return buttonflags; | |
| 485 } | |
| 486 | |
| 487 ui::EventType GetTouchEventType(const base::NativeEvent& native_event) { | |
| 488 XIDeviceEvent* event = | |
| 489 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 490 #if defined(USE_XI2_MT) | |
| 491 switch(event->evtype) { | |
| 492 case XI_TouchBegin: | |
| 493 return ui::ET_TOUCH_PRESSED; | |
| 494 case XI_TouchUpdate: | |
| 495 return ui::ET_TOUCH_MOVED; | |
| 496 case XI_TouchEnd: | |
| 497 return ui::ET_TOUCH_RELEASED; | |
| 498 } | |
| 499 | |
| 500 return ui::ET_UNKNOWN; | |
| 501 #else | |
| 502 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | |
| 503 | |
| 504 // If this device doesn't support multi-touch, then just use the normal | |
| 505 // pressed/release events to indicate touch start/end. With multi-touch, | |
| 506 // these events are sent only for the first (pressed) or last (released) | |
| 507 // touch point, and so we must infer start/end from motion events. | |
| 508 if (!factory->IsMultiTouchDevice(event->sourceid)) { | |
| 509 switch (event->evtype) { | |
| 510 case XI_ButtonPress: | |
| 511 return ui::ET_TOUCH_PRESSED; | |
| 512 case XI_ButtonRelease: | |
| 513 return ui::ET_TOUCH_RELEASED; | |
| 514 case XI_Motion: | |
| 515 if (GetButtonMaskForX2Event(event)) | |
| 516 return ui::ET_TOUCH_MOVED; | |
| 517 return ui::ET_UNKNOWN; | |
| 518 default: | |
| 519 NOTREACHED(); | |
| 520 } | |
| 521 } | |
| 522 | |
| 523 DCHECK_EQ(event->evtype, XI_Motion); | |
| 524 | |
| 525 // Note: We will not generate a _STATIONARY event here. It will be created, | |
| 526 // when necessary, by a RWHVV. | |
| 527 // TODO(sad): When should _CANCELLED be generated? | |
| 528 | |
| 529 float slot; | |
| 530 if (!factory->ExtractTouchParam(*native_event, ui::TouchFactory::TP_SLOT_ID, | |
| 531 &slot)) | |
| 532 return ui::ET_UNKNOWN; | |
| 533 | |
| 534 if (!factory->IsSlotUsed(slot)) { | |
| 535 // This is a new touch point. | |
| 536 return ui::ET_TOUCH_PRESSED; | |
| 537 } | |
| 538 | |
| 539 float tracking; | |
| 540 if (!factory->ExtractTouchParam(*native_event, | |
| 541 ui::TouchFactory::TP_TRACKING_ID, &tracking)) | |
| 542 return ui::ET_UNKNOWN; | |
| 543 | |
| 544 if (tracking == 0l) { | |
| 545 // The touch point has been released. | |
| 546 return ui::ET_TOUCH_RELEASED; | |
| 547 } | |
| 548 | |
| 549 return ui::ET_TOUCH_MOVED; | |
| 550 #endif // defined(USE_XI2_MT) | |
| 551 } | |
| 552 | |
| 553 float GetTouchParamFromXEvent(XEvent* xev, | |
| 554 ui::TouchFactory::TouchParam tp, | |
| 555 float default_value) { | |
| 556 ui::TouchFactory::GetInstance()->ExtractTouchParam(*xev, tp, &default_value); | |
| 557 return default_value; | |
| 558 } | |
| 559 | |
| 560 Atom GetNoopEventAtom() { | |
| 561 return XInternAtom( | |
| 562 base::MessagePumpX::GetDefaultXDisplay(), | |
| 563 "noop", False); | |
| 564 } | |
| 565 | |
| 566 #if defined(USE_XI2_MT) | |
| 567 gfx::Point CalibrateTouchCoordinates( | |
| 568 const XIDeviceEvent* xievent) { | |
| 569 int x = static_cast<int>(xievent->event_x); | |
| 570 int y = static_cast<int>(xievent->event_y); | |
| 571 if (!CommandLine::ForCurrentProcess()->HasSwitch( | |
| 572 switches::kEnableTouchCalibration)) | |
| 573 return gfx::Point(x, y); | |
| 574 // TODO(skuhne): Find a new home for these hardware dependent touch | |
| 575 // constants. | |
| 576 // Note: These values have been found to be correct for the device I was | |
| 577 // testing with. I have the feeling that the DPI resolution of the bezel is | |
| 578 // less then the dpi resolution over the visible part - which would explain | |
| 579 // why the small value (50) is so wide compared to the entire area. | |
| 580 gfx::Rect bounds = gfx::Screen::GetPrimaryMonitor().bounds_in_pixel(); | |
| 581 const int kLeftBorder = 50; | |
| 582 const int kRightBorder = 50; | |
| 583 const int kBottomBorder = 50; | |
| 584 const int kTopBorder = 0; | |
| 585 const int resolution_x = bounds.width(); | |
| 586 const int resolution_y = bounds.height(); | |
| 587 // The "grace area" (10% in this case) is to make it easier for the user to | |
| 588 // navigate to the corner. | |
| 589 const double kGraceAreaFraction = 0.1; | |
| 590 // Offset the x position to the real | |
| 591 x -= kLeftBorder; | |
| 592 // Check if we are in the grace area of the left side. | |
| 593 // Note: We might not want to do this when the gesture is locked? | |
| 594 if (x < 0 && x > -kLeftBorder * kGraceAreaFraction) | |
| 595 x = 0; | |
| 596 // Check if we are in the grace area of the right side. | |
| 597 // Note: We might not want to do this when the gesture is locked? | |
| 598 if (x > resolution_x - kLeftBorder && | |
| 599 x < resolution_x - kLeftBorder + kRightBorder * kGraceAreaFraction) | |
| 600 x = resolution_x - kLeftBorder; | |
| 601 // Scale the screen area back to the full resolution of the screen. | |
| 602 x = (x * resolution_x) / (resolution_x - (kRightBorder + kLeftBorder)); | |
| 603 // Offset the y position to the real | |
| 604 y -= kTopBorder; | |
| 605 // Check if we are in the grace area of the left side. | |
| 606 // Note: We might not want to do this when the gesture is locked? | |
| 607 if (y < 0 && y > -kTopBorder * kGraceAreaFraction) | |
| 608 y = 0; | |
| 609 // Check if we are in the grace area of the right side. | |
| 610 // Note: We might not want to do this when the gesture is locked? | |
| 611 if (y > resolution_y - kTopBorder && | |
| 612 y < resolution_y - kTopBorder + kBottomBorder * kGraceAreaFraction) | |
| 613 y = resolution_y - kTopBorder; | |
| 614 // Scale the screen area back to the full resolution of the screen. | |
| 615 y = (y * resolution_y) / (resolution_y - (kBottomBorder + kTopBorder)); | |
| 616 // Set the modified coordinate back to the event. | |
| 617 return gfx::Point(x, y); | |
| 618 } | |
| 619 #endif // defined(USE_XI2_MT) | |
| 620 | |
| 621 } // namespace | |
| 622 | |
| 623 namespace ui { | |
| 624 | |
| 625 void UpdateDeviceList() { | |
| 626 Display* display = GetXDisplay(); | |
| 627 CMTEventData::GetInstance()->UpdateDeviceList(display); | |
| 628 TouchFactory::GetInstance()->UpdateDeviceList(display); | |
| 629 } | |
| 630 | |
| 631 EventType EventTypeFromNative(const base::NativeEvent& native_event) { | |
| 632 switch (native_event->type) { | |
| 633 case KeyPress: | |
| 634 return ET_KEY_PRESSED; | |
| 635 case KeyRelease: | |
| 636 return ET_KEY_RELEASED; | |
| 637 case ButtonPress: | |
| 638 if (static_cast<int>(native_event->xbutton.button) >= kMinWheelButton && | |
| 639 static_cast<int>(native_event->xbutton.button) <= kMaxWheelButton) | |
| 640 return ET_MOUSEWHEEL; | |
| 641 return ET_MOUSE_PRESSED; | |
| 642 case ButtonRelease: | |
| 643 // Drop wheel events; we should've already scrolled on the press. | |
| 644 if (static_cast<int>(native_event->xbutton.button) >= kMinWheelButton && | |
| 645 static_cast<int>(native_event->xbutton.button) <= kMaxWheelButton) | |
| 646 return ET_UNKNOWN; | |
| 647 return ET_MOUSE_RELEASED; | |
| 648 case MotionNotify: | |
| 649 if (native_event->xmotion.state & | |
| 650 (Button1Mask | Button2Mask | Button3Mask)) | |
| 651 return ET_MOUSE_DRAGGED; | |
| 652 return ET_MOUSE_MOVED; | |
| 653 case EnterNotify: | |
| 654 return ET_MOUSE_ENTERED; | |
| 655 case LeaveNotify: | |
| 656 return ET_MOUSE_EXITED; | |
| 657 case GenericEvent: { | |
| 658 TouchFactory* factory = TouchFactory::GetInstance(); | |
| 659 if (!factory->ShouldProcessXI2Event(native_event)) | |
| 660 return ET_UNKNOWN; | |
| 661 | |
| 662 XIDeviceEvent* xievent = | |
| 663 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 664 | |
| 665 if (factory->IsTouchDevice(xievent->sourceid)) | |
| 666 return GetTouchEventType(native_event); | |
| 667 | |
| 668 switch (xievent->evtype) { | |
| 669 case XI_ButtonPress: { | |
| 670 int button = EventButtonFromNative(native_event); | |
| 671 if (button >= kMinWheelButton && button <= kMaxWheelButton) | |
| 672 return ET_MOUSEWHEEL; | |
| 673 return ET_MOUSE_PRESSED; | |
| 674 } | |
| 675 case XI_ButtonRelease: { | |
| 676 int button = EventButtonFromNative(native_event); | |
| 677 // Drop wheel events; we should've already scrolled on the press. | |
| 678 if (button >= kMinWheelButton && button <= kMaxWheelButton) | |
| 679 return ET_UNKNOWN; | |
| 680 return ET_MOUSE_RELEASED; | |
| 681 } | |
| 682 case XI_Motion: { | |
| 683 float vx, vy; | |
| 684 bool is_cancel; | |
| 685 if (GetFlingData(native_event, &vx, &vy, &is_cancel)) { | |
| 686 return is_cancel ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START; | |
| 687 } else if (GetScrollOffsets(native_event, NULL, NULL)) | |
| 688 return ET_SCROLL; | |
| 689 else if (GetButtonMaskForX2Event(xievent)) { | |
| 690 return ET_MOUSE_DRAGGED; | |
| 691 } else | |
| 692 return ET_MOUSE_MOVED; | |
| 693 } | |
| 694 } | |
| 695 } | |
| 696 default: | |
| 697 break; | |
| 698 } | |
| 699 return ET_UNKNOWN; | |
| 700 } | |
| 701 | |
| 702 int EventFlagsFromNative(const base::NativeEvent& native_event) { | |
| 703 switch (native_event->type) { | |
| 704 case KeyPress: | |
| 705 case KeyRelease: { | |
| 706 XModifierStateWatcher::GetInstance()->UpdateStateFromEvent(native_event); | |
| 707 return GetEventFlagsFromXState(native_event->xkey.state); | |
| 708 } | |
| 709 case ButtonPress: | |
| 710 case ButtonRelease: { | |
| 711 int flags = GetEventFlagsFromXState(native_event->xbutton.state); | |
| 712 const EventType type = EventTypeFromNative(native_event); | |
| 713 if (type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) | |
| 714 flags |= GetEventFlagsForButton(native_event->xbutton.button); | |
| 715 return flags; | |
| 716 } | |
| 717 case MotionNotify: | |
| 718 return GetEventFlagsFromXState(native_event->xmotion.state); | |
| 719 case GenericEvent: { | |
| 720 XIDeviceEvent* xievent = | |
| 721 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 722 | |
| 723 const bool touch = | |
| 724 TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid); | |
| 725 switch (xievent->evtype) { | |
| 726 case XI_ButtonPress: | |
| 727 case XI_ButtonRelease: { | |
| 728 int flags = GetButtonMaskForX2Event(xievent) | | |
| 729 GetEventFlagsFromXState(xievent->mods.effective); | |
| 730 if (touch) { | |
| 731 flags |= GetEventFlagsFromXState( | |
| 732 XModifierStateWatcher::GetInstance()->state()); | |
| 733 } | |
| 734 | |
| 735 const EventType type = EventTypeFromNative(native_event); | |
| 736 int button = EventButtonFromNative(native_event); | |
| 737 if ((type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) && !touch) | |
| 738 flags |= GetEventFlagsForButton(button); | |
| 739 return flags; | |
| 740 } | |
| 741 case XI_Motion: | |
| 742 return GetButtonMaskForX2Event(xievent) | | |
| 743 GetEventFlagsFromXState(xievent->mods.effective); | |
| 744 } | |
| 745 } | |
| 746 } | |
| 747 return 0; | |
| 748 } | |
| 749 | |
| 750 base::TimeDelta EventTimeFromNative(const base::NativeEvent& native_event) { | |
| 751 switch(native_event->type) { | |
| 752 case KeyPress: | |
| 753 case KeyRelease: | |
| 754 return base::TimeDelta::FromMilliseconds(native_event->xkey.time); | |
| 755 case ButtonPress: | |
| 756 case ButtonRelease: | |
| 757 return base::TimeDelta::FromMilliseconds(native_event->xbutton.time); | |
| 758 break; | |
| 759 case MotionNotify: | |
| 760 return base::TimeDelta::FromMilliseconds(native_event->xmotion.time); | |
| 761 break; | |
| 762 case GenericEvent: { | |
| 763 double start, end; | |
| 764 if (GetGestureTimes(native_event, &start, &end)) { | |
| 765 // If the driver supports gesture times, use them. | |
| 766 return base::TimeDelta::FromMicroseconds(end * 1000000); | |
| 767 } else { | |
| 768 XIDeviceEvent* xide = | |
| 769 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 770 return base::TimeDelta::FromMilliseconds(xide->time); | |
| 771 } | |
| 772 break; | |
| 773 } | |
| 774 } | |
| 775 NOTREACHED(); | |
| 776 return base::TimeDelta(); | |
| 777 } | |
| 778 | |
| 779 gfx::Point EventLocationFromNative(const base::NativeEvent& native_event) { | |
| 780 switch (native_event->type) { | |
| 781 case ButtonPress: | |
| 782 case ButtonRelease: | |
| 783 return gfx::Point(native_event->xbutton.x, native_event->xbutton.y); | |
| 784 case MotionNotify: | |
| 785 return gfx::Point(native_event->xmotion.x, native_event->xmotion.y); | |
| 786 case GenericEvent: { | |
| 787 XIDeviceEvent* xievent = | |
| 788 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 789 | |
| 790 #if defined(USE_XI2_MT) | |
| 791 // Touch event valuators aren't coordinates. | |
| 792 // Return the |event_x|/|event_y| directly as event's position. | |
| 793 if (xievent->evtype == XI_TouchBegin || | |
| 794 xievent->evtype == XI_TouchUpdate || | |
| 795 xievent->evtype == XI_TouchEnd) | |
| 796 // Note: Touch events are always touch screen events. | |
| 797 return CalibrateTouchCoordinates(xievent); | |
| 798 #endif | |
| 799 // Read the position from the valuators, because the location reported in | |
| 800 // event_x/event_y seems to be different (and doesn't match for events | |
| 801 // coming from slave device and master device) from the values in the | |
| 802 // valuators. See more on crbug.com/103981. The position in the valuators | |
| 803 // is in the global screen coordinates. But it is necessary to convert it | |
| 804 // into the window's coordinates. If the valuator is not set, that means | |
| 805 // the value hasn't changed, and so we can use the value from | |
| 806 // event_x/event_y (which are in the window's coordinates). | |
| 807 double* valuators = xievent->valuators.values; | |
| 808 | |
| 809 double x = xievent->event_x; | |
| 810 if (XIMaskIsSet(xievent->valuators.mask, 0)) | |
| 811 x = *valuators++ - (xievent->root_x - xievent->event_x); | |
| 812 | |
| 813 double y = xievent->event_y; | |
| 814 if (XIMaskIsSet(xievent->valuators.mask, 1)) | |
| 815 y = *valuators++ - (xievent->root_y - xievent->event_y); | |
| 816 | |
| 817 return gfx::Point(static_cast<int>(x), static_cast<int>(y)); | |
| 818 } | |
| 819 } | |
| 820 return gfx::Point(); | |
| 821 } | |
| 822 | |
| 823 int EventButtonFromNative(const base::NativeEvent& native_event) { | |
| 824 CHECK_EQ(GenericEvent, native_event->type); | |
| 825 XIDeviceEvent* xievent = | |
| 826 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 827 int button = xievent->detail; | |
| 828 | |
| 829 return (xievent->sourceid == xievent->deviceid) ? | |
| 830 ui::GetMappedButton(button) : button; | |
| 831 } | |
| 832 | |
| 833 KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) { | |
| 834 return KeyboardCodeFromXKeyEvent(native_event); | |
| 835 } | |
| 836 | |
| 837 bool IsMouseEvent(const base::NativeEvent& native_event) { | |
| 838 if (native_event->type == ButtonPress || | |
| 839 native_event->type == ButtonRelease || | |
| 840 native_event->type == MotionNotify) | |
| 841 return true; | |
| 842 if (native_event->type == GenericEvent) { | |
| 843 XIDeviceEvent* xievent = | |
| 844 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | |
| 845 return xievent->evtype == XI_ButtonPress || | |
| 846 xievent->evtype == XI_ButtonRelease || | |
| 847 xievent->evtype == XI_Motion; | |
| 848 } | |
| 849 return false; | |
| 850 } | |
| 851 | |
| 852 int GetMouseWheelOffset(const base::NativeEvent& native_event) { | |
| 853 int button = native_event->type == GenericEvent | |
| 854 ? EventButtonFromNative(native_event) : native_event->xbutton.button; | |
| 855 | |
| 856 switch (button) { | |
| 857 case 4: | |
| 858 return kWheelScrollAmount; | |
| 859 case 5: | |
| 860 return -kWheelScrollAmount; | |
| 861 default: | |
| 862 // TODO(derat): Do something for horizontal scrolls (buttons 6 and 7)? | |
| 863 return 0; | |
| 864 } | |
| 865 } | |
| 866 | |
| 867 int GetTouchId(const base::NativeEvent& xev) { | |
| 868 float slot = 0; | |
| 869 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); | |
| 870 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data); | |
| 871 if (!factory->IsMultiTouchDevice(xievent->sourceid)) { | |
| 872 // TODO(sad): Come up with a way to generate touch-ids for multi-touch | |
| 873 // events when touch-events are generated from a single-touch device. | |
| 874 return slot; | |
| 875 } | |
| 876 | |
| 877 #if defined(USE_XI2_MT) | |
| 878 float tracking_id; | |
| 879 if (!factory->ExtractTouchParam( | |
| 880 *xev, ui::TouchFactory::TP_TRACKING_ID, &tracking_id)) { | |
| 881 LOG(ERROR) << "Could not get the slot ID for the event. Using 0."; | |
| 882 } else { | |
| 883 slot = factory->GetSlotForTrackingID(tracking_id); | |
| 884 ui::EventType type = ui::EventTypeFromNative(xev); | |
| 885 if (type == ui::ET_TOUCH_CANCELLED || | |
| 886 type == ui::ET_TOUCH_RELEASED) { | |
| 887 factory->ReleaseSlotForTrackingID(tracking_id); | |
| 888 } | |
| 889 } | |
| 890 #else | |
| 891 if (!factory->ExtractTouchParam( | |
| 892 *xev, ui::TouchFactory::TP_SLOT_ID, &slot)) | |
| 893 LOG(ERROR) << "Could not get the slot ID for the event. Using 0."; | |
| 894 #endif | |
| 895 return slot; | |
| 896 } | |
| 897 | |
| 898 float GetTouchRadiusX(const base::NativeEvent& native_event) { | |
| 899 return GetTouchParamFromXEvent(native_event, | |
| 900 ui::TouchFactory::TP_TOUCH_MAJOR, 0.0) / 2.0; | |
| 901 } | |
| 902 | |
| 903 float GetTouchRadiusY(const base::NativeEvent& native_event) { | |
| 904 return GetTouchParamFromXEvent(native_event, | |
| 905 ui::TouchFactory::TP_TOUCH_MINOR, 0.0) / 2.0; | |
| 906 } | |
| 907 | |
| 908 float GetTouchAngle(const base::NativeEvent& native_event) { | |
| 909 return GetTouchParamFromXEvent(native_event, | |
| 910 ui::TouchFactory::TP_ORIENTATION, 0.0) / 2.0; | |
| 911 } | |
| 912 | |
| 913 float GetTouchForce(const base::NativeEvent& native_event) { | |
| 914 float force = 0.0; | |
| 915 force = GetTouchParamFromXEvent(native_event, ui::TouchFactory::TP_PRESSURE, | |
| 916 0.0); | |
| 917 unsigned int deviceid = | |
| 918 static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; | |
| 919 // Force is normalized to fall into [0, 1] | |
| 920 if (!ui::TouchFactory::GetInstance()->NormalizeTouchParam( | |
| 921 deviceid, ui::TouchFactory::TP_PRESSURE, &force)) | |
| 922 force = 0.0; | |
| 923 return force; | |
| 924 } | |
| 925 | |
| 926 bool GetScrollOffsets(const base::NativeEvent& native_event, | |
| 927 float* x_offset, | |
| 928 float* y_offset) { | |
| 929 return CMTEventData::GetInstance()->GetScrollOffsets( | |
| 930 *native_event, x_offset, y_offset); | |
| 931 } | |
| 932 | |
| 933 bool GetFlingData(const base::NativeEvent& native_event, | |
| 934 float* vx, | |
| 935 float* vy, | |
| 936 bool* is_cancel) { | |
| 937 return CMTEventData::GetInstance()->GetFlingData( | |
| 938 *native_event, vx, vy, is_cancel); | |
| 939 } | |
| 940 | |
| 941 bool GetGestureTimes(const base::NativeEvent& native_event, | |
| 942 double* start_time, | |
| 943 double* end_time) { | |
| 944 return CMTEventData::GetInstance()->GetGestureTimes( | |
| 945 *native_event, start_time, end_time); | |
| 946 } | |
| 947 | |
| 948 void SetNaturalScroll(bool enabled) { | |
| 949 CMTEventData::GetInstance()->set_natural_scroll_enabled(enabled); | |
| 950 } | |
| 951 | |
| 952 bool IsNaturalScrollEnabled() { | |
| 953 return CMTEventData::GetInstance()->natural_scroll_enabled(); | |
| 954 } | |
| 955 | |
| 956 bool IsTouchpadEvent(const base::NativeEvent& event) { | |
| 957 return CMTEventData::GetInstance()->IsTouchpadXInputEvent(event); | |
| 958 } | |
| 959 | |
| 960 bool IsNoopEvent(const base::NativeEvent& event) { | |
| 961 return (event->type == ClientMessage && | |
| 962 event->xclient.message_type == GetNoopEventAtom()); | |
| 963 } | |
| 964 | |
| 965 base::NativeEvent CreateNoopEvent() { | |
| 966 static XEvent* noop = NULL; | |
| 967 if (!noop) { | |
| 968 noop = new XEvent(); | |
| 969 memset(noop, 0, sizeof(XEvent)); | |
| 970 noop->xclient.type = ClientMessage; | |
| 971 noop->xclient.window = None; | |
| 972 noop->xclient.format = 8; | |
| 973 DCHECK(!noop->xclient.display); | |
| 974 } | |
| 975 // Make sure we use atom from current xdisplay, which may | |
| 976 // change during the test. | |
| 977 noop->xclient.message_type = GetNoopEventAtom(); | |
| 978 return noop; | |
| 979 } | |
| 980 | |
| 981 } // namespace ui | |
| OLD | NEW |