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/event_constants.h" 6 7 #include <string.h> 8 #include <X11/extensions/XInput.h> 9 #include <X11/extensions/XInput2.h> 10 #include <X11/Xlib.h> 11 12 #include "base/command_line.h" 13 #include "base/logging.h" 14 #include "base/memory/singleton.h" 15 #include "base/message_loop/message_pump_aurax11.h" 16 #include "ui/base/events/event_utils.h" 17 #include "ui/base/keycodes/keyboard_code_conversion_x.h" 18 #include "ui/base/touch/touch_factory_x11.h" 19 #include "ui/base/x/device_data_manager.h" 20 #include "ui/base/x/device_list_cache_x.h" 21 #include "ui/base/x/x11_atom_cache.h" 22 #include "ui/base/x/x11_util.h" 23 #include "ui/gfx/display.h" 24 #include "ui/gfx/point.h" 25 #include "ui/gfx/rect.h" 26 #include "ui/gfx/screen.h" 27 28 namespace { 29 30 // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+. 31 const int kWheelScrollAmount = 53; 32 33 const int kMinWheelButton = 4; 34 const int kMaxWheelButton = 7; 35 36 // A workaround for some incorrect implemented input drivers: 37 // Ignore their mouse input valuators. 38 bool IgnoreMouseValuators() { 39 static bool initialized = false; 40 static bool ignore_valuators = true; 41 if (initialized) 42 return ignore_valuators; 43 ignore_valuators = 44 CommandLine::ForCurrentProcess()->HasSwitch("disable-mouse-valuators"); 45 initialized = true; 46 return ignore_valuators; 47 } 48 49 // A class to track current modifier state on master device. Only track ctrl, 50 // alt, shift and caps lock keys currently. The tracked state can then be used 51 // by floating device. 52 class XModifierStateWatcher{ 53 public: 54 static XModifierStateWatcher* GetInstance() { 55 return Singleton<XModifierStateWatcher>::get(); 56 } 57 58 void UpdateStateFromEvent(const base::NativeEvent& native_event) { 59 // Floating device can't access the modifer state from master device. 60 // We need to track the states of modifier keys in a singleton for 61 // floating devices such as touch screen. Issue 106426 is one example 62 // of why we need the modifier states for floating device. 63 state_ = native_event->xkey.state; 64 // master_state is the state before key press. We need to track the 65 // state after key press for floating device. Currently only ctrl, 66 // shift, alt and caps lock keys are tracked. 67 ui::KeyboardCode keyboard_code = ui::KeyboardCodeFromNative(native_event); 68 unsigned int mask = 0; 69 70 switch (keyboard_code) { 71 case ui::VKEY_CONTROL: { 72 mask = ControlMask; 73 break; 74 } 75 case ui::VKEY_SHIFT: { 76 mask = ShiftMask; 77 break; 78 } 79 case ui::VKEY_MENU: { 80 mask = Mod1Mask; 81 break; 82 } 83 case ui::VKEY_CAPITAL: { 84 mask = LockMask; 85 break; 86 } 87 default: 88 break; 89 } 90 91 if (native_event->type == KeyPress) 92 state_ |= mask; 93 else 94 state_ &= ~mask; 95 } 96 97 // Returns the current modifer state in master device. It only contains the 98 // state of ctrl, shift, alt and caps lock keys. 99 unsigned int state() { return state_; } 100 101 private: 102 friend struct DefaultSingletonTraits<XModifierStateWatcher>; 103 104 XModifierStateWatcher() : state_(0) { } 105 106 unsigned int state_; 107 108 DISALLOW_COPY_AND_ASSIGN(XModifierStateWatcher); 109 }; 110 111 #if defined(USE_XI2_MT) 112 // Detects if a touch event is a driver-generated 'special event'. 113 // A 'special event' is a touch event with maximum radius and pressure at 114 // location (0, 0). 115 // This needs to be done in a cleaner way: http://crbug.com/169256 116 bool TouchEventIsGeneratedHack(const base::NativeEvent& native_event) { 117 XIDeviceEvent* event = 118 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 119 CHECK(event->evtype == XI_TouchBegin || 120 event->evtype == XI_TouchUpdate || 121 event->evtype == XI_TouchEnd); 122 123 // Force is normalized to [0, 1]. 124 if (ui::GetTouchForce(native_event) < 1.0f) 125 return false; 126 127 if (ui::EventLocationFromNative(native_event) != gfx::Point()) 128 return false; 129 130 // Radius is in pixels, and the valuator is the diameter in pixels. 131 double radius = ui::GetTouchRadiusX(native_event), min, max; 132 unsigned int deviceid = 133 static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; 134 if (!ui::DeviceDataManager::GetInstance()->GetDataRange( 135 deviceid, ui::DeviceDataManager::DT_TOUCH_MAJOR, &min, &max)) { 136 return false; 137 } 138 139 return radius * 2 == max; 140 } 141 #endif 142 143 int GetEventFlagsFromXState(unsigned int state) { 144 int flags = 0; 145 if (state & ControlMask) 146 flags |= ui::EF_CONTROL_DOWN; 147 if (state & ShiftMask) 148 flags |= ui::EF_SHIFT_DOWN; 149 if (state & Mod1Mask) 150 flags |= ui::EF_ALT_DOWN; 151 if (state & LockMask) 152 flags |= ui::EF_CAPS_LOCK_DOWN; 153 if (state & Mod5Mask) 154 flags |= ui::EF_ALTGR_DOWN; 155 if (state & Button1Mask) 156 flags |= ui::EF_LEFT_MOUSE_BUTTON; 157 if (state & Button2Mask) 158 flags |= ui::EF_MIDDLE_MOUSE_BUTTON; 159 if (state & Button3Mask) 160 flags |= ui::EF_RIGHT_MOUSE_BUTTON; 161 return flags; 162 } 163 164 // Get the event flag for the button in XButtonEvent. During a ButtonPress 165 // event, |state| in XButtonEvent does not include the button that has just been 166 // pressed. Instead |state| contains flags for the buttons (if any) that had 167 // already been pressed before the current button, and |button| stores the most 168 // current pressed button. So, if you press down left mouse button, and while 169 // pressing it down, press down the right mouse button, then for the latter 170 // event, |state| would have Button1Mask set but not Button3Mask, and |button| 171 // would be 3. 172 int GetEventFlagsForButton(int button) { 173 switch (button) { 174 case 1: 175 return ui::EF_LEFT_MOUSE_BUTTON; 176 case 2: 177 return ui::EF_MIDDLE_MOUSE_BUTTON; 178 case 3: 179 return ui::EF_RIGHT_MOUSE_BUTTON; 180 default: 181 return 0; 182 } 183 } 184 185 int GetButtonMaskForX2Event(XIDeviceEvent* xievent) { 186 int buttonflags = 0; 187 for (int i = 0; i < 8 * xievent->buttons.mask_len; i++) { 188 if (XIMaskIsSet(xievent->buttons.mask, i)) { 189 int button = (xievent->sourceid == xievent->deviceid) ? 190 ui::GetMappedButton(i) : i; 191 buttonflags |= GetEventFlagsForButton(button); 192 } 193 } 194 return buttonflags; 195 } 196 197 ui::EventType GetTouchEventType(const base::NativeEvent& native_event) { 198 XIDeviceEvent* event = 199 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 200 #if defined(USE_XI2_MT) 201 switch(event->evtype) { 202 case XI_TouchBegin: 203 return TouchEventIsGeneratedHack(native_event) ? ui::ET_UNKNOWN : 204 ui::ET_TOUCH_PRESSED; 205 case XI_TouchUpdate: 206 return TouchEventIsGeneratedHack(native_event) ? ui::ET_UNKNOWN : 207 ui::ET_TOUCH_MOVED; 208 case XI_TouchEnd: 209 return TouchEventIsGeneratedHack(native_event) ? ui::ET_TOUCH_CANCELLED : 210 ui::ET_TOUCH_RELEASED; 211 } 212 213 return ui::ET_UNKNOWN; 214 #else 215 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); 216 217 // If this device doesn't support multi-touch, then just use the normal 218 // pressed/release events to indicate touch start/end. With multi-touch, 219 // these events are sent only for the first (pressed) or last (released) 220 // touch point, and so we must infer start/end from motion events. 221 if (!factory->IsMultiTouchDevice(event->sourceid)) { 222 switch (event->evtype) { 223 case XI_ButtonPress: 224 return ui::ET_TOUCH_PRESSED; 225 case XI_ButtonRelease: 226 return ui::ET_TOUCH_RELEASED; 227 case XI_Motion: 228 if (GetButtonMaskForX2Event(event)) 229 return ui::ET_TOUCH_MOVED; 230 return ui::ET_UNKNOWN; 231 default: 232 NOTREACHED(); 233 } 234 } 235 236 DCHECK_EQ(event->evtype, XI_Motion); 237 238 // Note: We will not generate a _STATIONARY event here. It will be created, 239 // when necessary, by a RWHVV. 240 // TODO(sad): When should _CANCELLED be generated? 241 242 ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance(); 243 244 double slot; 245 if (!manager->GetEventData( 246 *native_event, ui::DeviceDataManager::DT_TOUCH_SLOT_ID, &slot)) 247 return ui::ET_UNKNOWN; 248 249 if (!factory->IsSlotUsed(slot)) { 250 // This is a new touch point. 251 return ui::ET_TOUCH_PRESSED; 252 } 253 254 double tracking; 255 if (!manager->GetEventData( 256 *native_event, ui::DeviceDataManager::DT_TOUCH_TRACKING_ID, &tracking)) 257 return ui::ET_UNKNOWN; 258 259 if (tracking == 0l) { 260 // The touch point has been released. 261 return ui::ET_TOUCH_RELEASED; 262 } 263 264 return ui::ET_TOUCH_MOVED; 265 #endif // defined(USE_XI2_MT) 266 } 267 268 double GetTouchParamFromXEvent(XEvent* xev, 269 ui::DeviceDataManager::DataType val, 270 double default_value) { 271 ui::DeviceDataManager::GetInstance()->GetEventData( 272 *xev, val, &default_value); 273 return default_value; 274 } 275 276 Atom GetNoopEventAtom() { 277 return XInternAtom( 278 base::MessagePumpAuraX11::GetDefaultXDisplay(), 279 "noop", False); 280 } 281 282 } // namespace 283 284 namespace ui { 285 286 void UpdateDeviceList() { 287 Display* display = GetXDisplay(); 288 DeviceListCacheX::GetInstance()->UpdateDeviceList(display); 289 TouchFactory::GetInstance()->UpdateDeviceList(display); 290 DeviceDataManager::GetInstance()->UpdateDeviceList(display); 291 } 292 293 EventType EventTypeFromNative(const base::NativeEvent& native_event) { 294 switch (native_event->type) { 295 case KeyPress: 296 return ET_KEY_PRESSED; 297 case KeyRelease: 298 return ET_KEY_RELEASED; 299 case ButtonPress: 300 if (static_cast<int>(native_event->xbutton.button) >= kMinWheelButton && 301 static_cast<int>(native_event->xbutton.button) <= kMaxWheelButton) 302 return ET_MOUSEWHEEL; 303 return ET_MOUSE_PRESSED; 304 case ButtonRelease: 305 // Drop wheel events; we should've already scrolled on the press. 306 if (static_cast<int>(native_event->xbutton.button) >= kMinWheelButton && 307 static_cast<int>(native_event->xbutton.button) <= kMaxWheelButton) 308 return ET_UNKNOWN; 309 return ET_MOUSE_RELEASED; 310 case MotionNotify: 311 if (native_event->xmotion.state & 312 (Button1Mask | Button2Mask | Button3Mask)) 313 return ET_MOUSE_DRAGGED; 314 return ET_MOUSE_MOVED; 315 case EnterNotify: 316 // The standard on Windows is to send a MouseMove event when the mouse 317 // first enters a window instead of sending a special mouse enter event. 318 // To be consistent we follow the same style. 319 return ET_MOUSE_MOVED; 320 case LeaveNotify: 321 return ET_MOUSE_EXITED; 322 case GenericEvent: { 323 TouchFactory* factory = TouchFactory::GetInstance(); 324 if (!factory->ShouldProcessXI2Event(native_event)) 325 return ET_UNKNOWN; 326 327 XIDeviceEvent* xievent = 328 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 329 330 if (factory->IsTouchDevice(xievent->sourceid)) 331 return GetTouchEventType(native_event); 332 333 switch (xievent->evtype) { 334 case XI_ButtonPress: { 335 int button = EventButtonFromNative(native_event); 336 if (button >= kMinWheelButton && button <= kMaxWheelButton) 337 return ET_MOUSEWHEEL; 338 return ET_MOUSE_PRESSED; 339 } 340 case XI_ButtonRelease: { 341 int button = EventButtonFromNative(native_event); 342 // Drop wheel events; we should've already scrolled on the press. 343 if (button >= kMinWheelButton && button <= kMaxWheelButton) 344 return ET_UNKNOWN; 345 return ET_MOUSE_RELEASED; 346 } 347 case XI_Motion: { 348 bool is_cancel; 349 if (GetFlingData(native_event, NULL, NULL, NULL, NULL, &is_cancel)) { 350 return is_cancel ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START; 351 } else if (DeviceDataManager::GetInstance()->IsScrollEvent( 352 native_event)) { 353 return IsTouchpadEvent(native_event) ? ET_SCROLL : ET_MOUSEWHEEL; 354 } else if (DeviceDataManager::GetInstance()->IsCMTMetricsEvent( 355 native_event)) { 356 return ET_UMA_DATA; 357 } else if (GetButtonMaskForX2Event(xievent)) { 358 return ET_MOUSE_DRAGGED; 359 } else { 360 return ET_MOUSE_MOVED; 361 } 362 } 363 } 364 } 365 default: 366 break; 367 } 368 return ET_UNKNOWN; 369 } 370 371 int EventFlagsFromNative(const base::NativeEvent& native_event) { 372 switch (native_event->type) { 373 case KeyPress: 374 case KeyRelease: { 375 XModifierStateWatcher::GetInstance()->UpdateStateFromEvent(native_event); 376 return GetEventFlagsFromXState(native_event->xkey.state); 377 } 378 case ButtonPress: 379 case ButtonRelease: { 380 int flags = GetEventFlagsFromXState(native_event->xbutton.state); 381 const EventType type = EventTypeFromNative(native_event); 382 if (type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) 383 flags |= GetEventFlagsForButton(native_event->xbutton.button); 384 return flags; 385 } 386 case MotionNotify: 387 return GetEventFlagsFromXState(native_event->xmotion.state); 388 case GenericEvent: { 389 XIDeviceEvent* xievent = 390 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 391 392 switch (xievent->evtype) { 393 #if defined(USE_XI2_MT) 394 case XI_TouchBegin: 395 case XI_TouchUpdate: 396 case XI_TouchEnd: 397 return GetButtonMaskForX2Event(xievent) | 398 GetEventFlagsFromXState(xievent->mods.effective) | 399 GetEventFlagsFromXState( 400 XModifierStateWatcher::GetInstance()->state()); 401 break; 402 #endif 403 case XI_ButtonPress: 404 case XI_ButtonRelease: { 405 const bool touch = 406 TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid); 407 int flags = GetButtonMaskForX2Event(xievent) | 408 GetEventFlagsFromXState(xievent->mods.effective); 409 if (touch) { 410 flags |= GetEventFlagsFromXState( 411 XModifierStateWatcher::GetInstance()->state()); 412 } 413 414 const EventType type = EventTypeFromNative(native_event); 415 int button = EventButtonFromNative(native_event); 416 if ((type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED) && !touch) 417 flags |= GetEventFlagsForButton(button); 418 return flags; 419 } 420 case XI_Motion: 421 return GetButtonMaskForX2Event(xievent) | 422 GetEventFlagsFromXState(xievent->mods.effective); 423 } 424 } 425 } 426 return 0; 427 } 428 429 base::TimeDelta EventTimeFromNative(const base::NativeEvent& native_event) { 430 switch(native_event->type) { 431 case KeyPress: 432 case KeyRelease: 433 return base::TimeDelta::FromMilliseconds(native_event->xkey.time); 434 case ButtonPress: 435 case ButtonRelease: 436 return base::TimeDelta::FromMilliseconds(native_event->xbutton.time); 437 break; 438 case MotionNotify: 439 return base::TimeDelta::FromMilliseconds(native_event->xmotion.time); 440 break; 441 case EnterNotify: 442 case LeaveNotify: 443 return base::TimeDelta::FromMilliseconds(native_event->xcrossing.time); 444 break; 445 case GenericEvent: { 446 double start, end; 447 double touch_timestamp; 448 if (GetGestureTimes(native_event, &start, &end)) { 449 // If the driver supports gesture times, use them. 450 return base::TimeDelta::FromMicroseconds(end * 1000000); 451 } else if (DeviceDataManager::GetInstance()->GetEventData(*native_event, 452 DeviceDataManager::DT_TOUCH_RAW_TIMESTAMP, &touch_timestamp)) { 453 return base::TimeDelta::FromMicroseconds(touch_timestamp * 1000000); 454 } else { 455 XIDeviceEvent* xide = 456 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 457 return base::TimeDelta::FromMilliseconds(xide->time); 458 } 459 break; 460 } 461 } 462 NOTREACHED(); 463 return base::TimeDelta(); 464 } 465 466 gfx::Point EventLocationFromNative(const base::NativeEvent& native_event) { 467 switch (native_event->type) { 468 case EnterNotify: 469 case LeaveNotify: 470 return gfx::Point(native_event->xcrossing.x, native_event->xcrossing.y); 471 case ButtonPress: 472 case ButtonRelease: 473 return gfx::Point(native_event->xbutton.x, native_event->xbutton.y); 474 case MotionNotify: 475 return gfx::Point(native_event->xmotion.x, native_event->xmotion.y); 476 case GenericEvent: { 477 XIDeviceEvent* xievent = 478 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 479 480 #if defined(USE_XI2_MT) 481 // Touch event valuators aren't coordinates. 482 // Return the |event_x|/|event_y| directly as event's position. 483 if (xievent->evtype == XI_TouchBegin || 484 xievent->evtype == XI_TouchUpdate || 485 xievent->evtype == XI_TouchEnd) 486 // Note: Touch events are always touch screen events. 487 return gfx::Point(static_cast<int>(xievent->event_x), 488 static_cast<int>(xievent->event_y)); 489 #endif 490 if (IgnoreMouseValuators()) { 491 return gfx::Point(static_cast<int>(xievent->event_x), 492 static_cast<int>(xievent->event_y)); 493 } 494 // Read the position from the valuators, because the location reported in 495 // event_x/event_y seems to be different (and doesn't match for events 496 // coming from slave device and master device) from the values in the 497 // valuators. See more on crbug.com/103981. The position in the valuators 498 // is in the global screen coordinates. But it is necessary to convert it 499 // into the window's coordinates. If the valuator is not set, that means 500 // the value hasn't changed, and so we can use the value from 501 // event_x/event_y (which are in the window's coordinates). 502 double* valuators = xievent->valuators.values; 503 504 double x = xievent->event_x; 505 if (XIMaskIsSet(xievent->valuators.mask, 0)) 506 x = *valuators++ - (xievent->root_x - xievent->event_x); 507 508 double y = xievent->event_y; 509 if (XIMaskIsSet(xievent->valuators.mask, 1)) 510 y = *valuators++ - (xievent->root_y - xievent->event_y); 511 512 return gfx::Point(static_cast<int>(x), static_cast<int>(y)); 513 } 514 } 515 return gfx::Point(); 516 } 517 518 gfx::Point EventSystemLocationFromNative( 519 const base::NativeEvent& native_event) { 520 switch (native_event->type) { 521 case EnterNotify: 522 case LeaveNotify: { 523 return gfx::Point(native_event->xcrossing.x_root, 524 native_event->xcrossing.y_root); 525 } 526 case ButtonPress: 527 case ButtonRelease: { 528 return gfx::Point(native_event->xbutton.x_root, 529 native_event->xbutton.y_root); 530 } 531 case MotionNotify: { 532 return gfx::Point(native_event->xmotion.x_root, 533 native_event->xmotion.y_root); 534 } 535 case GenericEvent: { 536 XIDeviceEvent* xievent = 537 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 538 return gfx::Point(xievent->root_x, xievent->root_y); 539 } 540 } 541 542 return gfx::Point(); 543 } 544 545 int EventButtonFromNative(const base::NativeEvent& native_event) { 546 CHECK_EQ(GenericEvent, native_event->type); 547 XIDeviceEvent* xievent = 548 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 549 int button = xievent->detail; 550 551 return (xievent->sourceid == xievent->deviceid) ? 552 ui::GetMappedButton(button) : button; 553 } 554 555 KeyboardCode KeyboardCodeFromNative(const base::NativeEvent& native_event) { 556 return KeyboardCodeFromXKeyEvent(native_event); 557 } 558 559 bool IsMouseEvent(const base::NativeEvent& native_event) { 560 if (native_event->type == EnterNotify || 561 native_event->type == LeaveNotify || 562 native_event->type == ButtonPress || 563 native_event->type == ButtonRelease || 564 native_event->type == MotionNotify) 565 return true; 566 if (native_event->type == GenericEvent) { 567 XIDeviceEvent* xievent = 568 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 569 return xievent->evtype == XI_ButtonPress || 570 xievent->evtype == XI_ButtonRelease || 571 xievent->evtype == XI_Motion; 572 } 573 return false; 574 } 575 576 int GetChangedMouseButtonFlagsFromNative( 577 const base::NativeEvent& native_event) { 578 switch (native_event->type) { 579 case ButtonPress: 580 case ButtonRelease: 581 return GetEventFlagsFromXState(native_event->xbutton.state); 582 case GenericEvent: { 583 XIDeviceEvent* xievent = 584 static_cast<XIDeviceEvent*>(native_event->xcookie.data); 585 switch (xievent->evtype) { 586 case XI_ButtonPress: 587 case XI_ButtonRelease: 588 return GetEventFlagsForButton(EventButtonFromNative(native_event)); 589 default: 590 break; 591 } 592 } 593 default: 594 break; 595 } 596 return 0; 597 } 598 599 gfx::Vector2d GetMouseWheelOffset(const base::NativeEvent& native_event) { 600 float x_offset, y_offset; 601 if (GetScrollOffsets( 602 native_event, &x_offset, &y_offset, NULL, NULL, NULL)) { 603 return gfx::Vector2d(static_cast<int>(x_offset), 604 static_cast<int>(y_offset)); 605 } 606 607 int button = native_event->type == GenericEvent ? 608 EventButtonFromNative(native_event) : native_event->xbutton.button; 609 610 switch (button) { 611 case 4: 612 return gfx::Vector2d(0, kWheelScrollAmount); 613 case 5: 614 return gfx::Vector2d(0, -kWheelScrollAmount); 615 default: 616 // TODO(derat): Do something for horizontal scrolls (buttons 6 and 7)? 617 return gfx::Vector2d(); 618 } 619 } 620 621 void ClearTouchIdIfReleased(const base::NativeEvent& xev) { 622 #if defined(USE_XI2_MT) 623 ui::EventType type = ui::EventTypeFromNative(xev); 624 if (type == ui::ET_TOUCH_CANCELLED || 625 type == ui::ET_TOUCH_RELEASED) { 626 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); 627 ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance(); 628 double tracking_id; 629 if (manager->GetEventData( 630 *xev, ui::DeviceDataManager::DT_TOUCH_TRACKING_ID, &tracking_id)) { 631 factory->ReleaseSlotForTrackingID(tracking_id); 632 } 633 } 634 #endif 635 } 636 637 int GetTouchId(const base::NativeEvent& xev) { 638 double slot = 0; 639 ui::TouchFactory* factory = ui::TouchFactory::GetInstance(); 640 XIDeviceEvent* xievent = static_cast<XIDeviceEvent*>(xev->xcookie.data); 641 if (!factory->IsMultiTouchDevice(xievent->sourceid)) { 642 // TODO(sad): Come up with a way to generate touch-ids for multi-touch 643 // events when touch-events are generated from a single-touch device. 644 return slot; 645 } 646 647 ui::DeviceDataManager* manager = ui::DeviceDataManager::GetInstance(); 648 649 #if defined(USE_XI2_MT) 650 double tracking_id; 651 if (!manager->GetEventData( 652 *xev, ui::DeviceDataManager::DT_TOUCH_TRACKING_ID, &tracking_id)) { 653 LOG(ERROR) << "Could not get the tracking ID for the event. Using 0."; 654 } else { 655 slot = factory->GetSlotForTrackingID(tracking_id); 656 } 657 #else 658 if (!manager->GetEventData( 659 *xev, ui::DeviceDataManager::DT_TOUCH_SLOT_ID, &slot)) 660 LOG(ERROR) << "Could not get the slot ID for the event. Using 0."; 661 #endif 662 return slot; 663 } 664 665 float GetTouchRadiusX(const base::NativeEvent& native_event) { 666 return GetTouchParamFromXEvent(native_event, 667 ui::DeviceDataManager::DT_TOUCH_MAJOR, 0.0) / 2.0; 668 } 669 670 float GetTouchRadiusY(const base::NativeEvent& native_event) { 671 return GetTouchParamFromXEvent(native_event, 672 ui::DeviceDataManager::DT_TOUCH_MINOR, 0.0) / 2.0; 673 } 674 675 float GetTouchAngle(const base::NativeEvent& native_event) { 676 return GetTouchParamFromXEvent(native_event, 677 ui::DeviceDataManager::DT_TOUCH_ORIENTATION, 0.0) / 2.0; 678 } 679 680 float GetTouchForce(const base::NativeEvent& native_event) { 681 double force = 0.0; 682 force = GetTouchParamFromXEvent(native_event, 683 ui::DeviceDataManager::DT_TOUCH_PRESSURE, 0.0); 684 unsigned int deviceid = 685 static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; 686 // Force is normalized to fall into [0, 1] 687 if (!ui::DeviceDataManager::GetInstance()->NormalizeData( 688 deviceid, ui::DeviceDataManager::DT_TOUCH_PRESSURE, &force)) 689 force = 0.0; 690 return force; 691 } 692 693 bool GetScrollOffsets(const base::NativeEvent& native_event, 694 float* x_offset, 695 float* y_offset, 696 float* x_offset_ordinal, 697 float* y_offset_ordinal, 698 int* finger_count) { 699 if (!DeviceDataManager::GetInstance()->IsScrollEvent(native_event)) 700 return false; 701 702 // Temp values to prevent passing NULLs to DeviceDataManager. 703 float x_offset_, y_offset_; 704 float x_offset_ordinal_, y_offset_ordinal_; 705 int finger_count_; 706 if (!x_offset) 707 x_offset = &x_offset_; 708 if (!y_offset) 709 y_offset = &y_offset_; 710 if (!x_offset_ordinal) 711 x_offset_ordinal = &x_offset_ordinal_; 712 if (!y_offset_ordinal) 713 y_offset_ordinal = &y_offset_ordinal_; 714 if (!finger_count) 715 finger_count = &finger_count_; 716 717 DeviceDataManager::GetInstance()->GetScrollOffsets( 718 native_event, 719 x_offset, y_offset, 720 x_offset_ordinal, y_offset_ordinal, 721 finger_count); 722 return true; 723 } 724 725 bool GetFlingData(const base::NativeEvent& native_event, 726 float* vx, 727 float* vy, 728 float* vx_ordinal, 729 float* vy_ordinal, 730 bool* is_cancel) { 731 if (!DeviceDataManager::GetInstance()->IsFlingEvent(native_event)) 732 return false; 733 734 float vx_, vy_; 735 float vx_ordinal_, vy_ordinal_; 736 bool is_cancel_; 737 if (!vx) 738 vx = &vx_; 739 if (!vy) 740 vy = &vy_; 741 if (!vx_ordinal) 742 vx_ordinal = &vx_ordinal_; 743 if (!vy_ordinal) 744 vy_ordinal = &vy_ordinal_; 745 if (!is_cancel) 746 is_cancel = &is_cancel_; 747 748 DeviceDataManager::GetInstance()->GetFlingData( 749 native_event, vx, vy, vx_ordinal, vy_ordinal, is_cancel); 750 return true; 751 } 752 753 bool GetGestureTimes(const base::NativeEvent& native_event, 754 double* start_time, 755 double* end_time) { 756 if (!DeviceDataManager::GetInstance()->HasGestureTimes(native_event)) 757 return false; 758 759 double start_time_, end_time_; 760 if (!start_time) 761 start_time = &start_time_; 762 if (!end_time) 763 end_time = &end_time_; 764 765 DeviceDataManager::GetInstance()->GetGestureTimes( 766 native_event, start_time, end_time); 767 return true; 768 } 769 770 void SetNaturalScroll(bool enabled) { 771 DeviceDataManager::GetInstance()->set_natural_scroll_enabled(enabled); 772 } 773 774 bool IsNaturalScrollEnabled() { 775 return DeviceDataManager::GetInstance()->natural_scroll_enabled(); 776 } 777 778 bool IsTouchpadEvent(const base::NativeEvent& event) { 779 return DeviceDataManager::GetInstance()->IsTouchpadXInputEvent(event); 780 } 781 782 bool IsNoopEvent(const base::NativeEvent& event) { 783 return (event->type == ClientMessage && 784 event->xclient.message_type == GetNoopEventAtom()); 785 } 786 787 base::NativeEvent CreateNoopEvent() { 788 static XEvent* noop = NULL; 789 if (!noop) { 790 noop = new XEvent(); 791 memset(noop, 0, sizeof(XEvent)); 792 noop->xclient.type = ClientMessage; 793 noop->xclient.window = None; 794 noop->xclient.format = 8; 795 DCHECK(!noop->xclient.display); 796 } 797 // Make sure we use atom from current xdisplay, which may 798 // change during the test. 799 noop->xclient.message_type = GetNoopEventAtom(); 800 return noop; 801 } 802 803 } // namespace ui 804