| OLD | NEW |
| 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/base/events.h" | 5 #include "ui/base/events.h" |
| 6 | 6 |
| 7 #include <X11/Xlib.h> | 7 #include <X11/Xlib.h> |
| 8 #include <X11/extensions/XInput.h> |
| 8 #include <X11/extensions/XInput2.h> | 9 #include <X11/extensions/XInput2.h> |
| 9 #include <string.h> | 10 #include <string.h> |
| 10 | 11 |
| 11 #include "base/logging.h" | 12 #include "base/logging.h" |
| 12 #include "ui/base/keycodes/keyboard_code_conversion_x.h" | 13 #include "ui/base/keycodes/keyboard_code_conversion_x.h" |
| 13 #include "ui/base/touch/touch_factory.h" | 14 #include "ui/base/touch/touch_factory.h" |
| 14 #include "ui/base/x/x11_util.h" | 15 #include "ui/base/x/x11_util.h" |
| 15 #include "ui/gfx/point.h" | 16 #include "ui/gfx/point.h" |
| 16 | 17 |
| 17 #if !defined(TOOLKIT_USES_GTK) | 18 #if !defined(TOOLKIT_USES_GTK) |
| 18 #include "base/message_pump_x.h" | 19 #include "base/message_pump_x.h" |
| 19 #endif | 20 #endif |
| 20 | 21 |
| 22 // Copied from xserver-properties.h |
| 23 #define AXIS_LABEL_PROP_REL_HWHEEL "Rel Horiz Wheel" |
| 24 #define AXIS_LABEL_PROP_REL_WHEEL "Rel Vert Wheel" |
| 25 |
| 21 namespace { | 26 namespace { |
| 22 | 27 |
| 23 // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+. | 28 // Scroll amount for each wheelscroll event. 53 is also the value used for GTK+. |
| 24 static const int kWheelScrollAmount = 53; | 29 static const int kWheelScrollAmount = 53; |
| 25 | 30 |
| 26 static const int kMinWheelButton = 4; | 31 static const int kMinWheelButton = 4; |
| 27 #if defined(OS_CHROMEOS) | 32 #if defined(OS_CHROMEOS) |
| 28 // TODO(davemoore) For now use the button to decide how much to scroll by. | 33 // TODO(davemoore) For now use the button to decide how much to scroll by. |
| 29 // When we go to XI2 scroll events this won't be necessary. If this doesn't | 34 // When we go to XI2 scroll events this won't be necessary. If this doesn't |
| 30 // happen for some reason we can better detect which devices are touchpads. | 35 // happen for some reason we can better detect which devices are touchpads. |
| 31 static const int kTouchpadScrollAmount = 3; | 36 static const int kTouchpadScrollAmount = 3; |
| 32 // Chrome OS also uses buttons 8 and 9 for scrolling. | 37 // Chrome OS also uses buttons 8 and 9 for scrolling. |
| 33 static const int kMaxWheelButton = 9; | 38 static const int kMaxWheelButton = 9; |
| 34 #else | 39 #else |
| 35 static const int kMaxWheelButton = 7; | 40 static const int kMaxWheelButton = 7; |
| 36 #endif | 41 #endif |
| 37 | 42 |
| 43 // A class to support the detection of scroll events, using X11 valuators. |
| 44 class UI_EXPORT ScrollEventData { |
| 45 public: |
| 46 // Returns the ScrollEventData singleton. |
| 47 static ScrollEventData* GetInstance() { |
| 48 return Singleton<ScrollEventData>::get(); |
| 49 } |
| 50 |
| 51 // Updates the list of devices. |
| 52 void UpdateDeviceList(Display* display) { |
| 53 scroll_devices_.reset(); |
| 54 device_to_valuators_.clear(); |
| 55 |
| 56 int count = 0; |
| 57 XDeviceInfo* dev_list = XListInputDevices(display, &count); |
| 58 Atom xi_touchpad = XInternAtom(display, XI_TOUCHPAD, false); |
| 59 for (int i = 0; i < count; ++i) { |
| 60 XDeviceInfo* dev = dev_list + i; |
| 61 if (dev->type == xi_touchpad) |
| 62 scroll_devices_[dev_list[i].id] = true; |
| 63 } |
| 64 if (dev_list) |
| 65 XFreeDeviceList(dev_list); |
| 66 |
| 67 XIDeviceInfo* info_list = XIQueryDevice(display, XIAllDevices, &count); |
| 68 Atom x_axis = XInternAtom(display, AXIS_LABEL_PROP_REL_HWHEEL, false); |
| 69 Atom y_axis = XInternAtom(display, AXIS_LABEL_PROP_REL_WHEEL, false); |
| 70 for (int i = 0; i < count; ++i) { |
| 71 XIDeviceInfo* info = info_list + i; |
| 72 |
| 73 if (!scroll_devices_[info->deviceid]) |
| 74 continue; |
| 75 |
| 76 if (info->use != XISlavePointer && info->use != XIFloatingSlave) { |
| 77 scroll_devices_[info->deviceid] = false; |
| 78 continue; |
| 79 } |
| 80 |
| 81 Valuators valuators = {-1, -1}; |
| 82 for (int j = 0; j < info->num_classes; ++j) { |
| 83 if (info->classes[j]->type != XIValuatorClass) |
| 84 continue; |
| 85 |
| 86 XIValuatorClassInfo* v = |
| 87 reinterpret_cast<XIValuatorClassInfo*>(info->classes[j]); |
| 88 if (v->label == x_axis) |
| 89 valuators.x_scroll = v->number; |
| 90 else if (v->label == y_axis) |
| 91 valuators.y_scroll = v->number; |
| 92 } |
| 93 if (valuators.x_scroll >= 0 && valuators.y_scroll >= 0) |
| 94 device_to_valuators_[info->deviceid] = valuators; |
| 95 else |
| 96 scroll_devices_[info->deviceid] = false; |
| 97 } |
| 98 } |
| 99 |
| 100 // Returns true if this is a scroll event (a motion event with the necessary |
| 101 // valuators. Also returns the offsets. |x_offset| and |y_offset| can be |
| 102 // NULL. |
| 103 bool GetScrollOffsets(const XEvent& xev, float* x_offset, float* y_offset) { |
| 104 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); |
| 105 |
| 106 if (x_offset) |
| 107 *x_offset = 0; |
| 108 if (y_offset) |
| 109 *y_offset = 0; |
| 110 |
| 111 if (!scroll_devices_[xiev->deviceid]) |
| 112 return false; |
| 113 |
| 114 int x_scroll = device_to_valuators_[xiev->deviceid].x_scroll; |
| 115 int y_scroll = device_to_valuators_[xiev->deviceid].y_scroll; |
| 116 |
| 117 bool has_x_offset = XIMaskIsSet(xiev->valuators.mask, x_scroll); |
| 118 bool has_y_offset = XIMaskIsSet(xiev->valuators.mask, y_scroll); |
| 119 bool is_scroll = has_x_offset || has_y_offset; |
| 120 |
| 121 if (!x_offset && !y_offset) |
| 122 return is_scroll; |
| 123 |
| 124 double* valuators = xiev->valuators.values; |
| 125 for (int i = 0; i < xiev->valuators.mask_len * 8; ++i) { |
| 126 if (XIMaskIsSet(xiev->valuators.mask, i)) { |
| 127 if (x_offset && x_scroll == i) |
| 128 *x_offset = -(*valuators); |
| 129 else if (y_offset && y_scroll == i) |
| 130 *y_offset = -(*valuators); |
| 131 valuators++; |
| 132 } |
| 133 } |
| 134 |
| 135 return is_scroll; |
| 136 } |
| 137 |
| 138 private: |
| 139 // Requirement for Singleton |
| 140 friend struct DefaultSingletonTraits<ScrollEventData>; |
| 141 |
| 142 struct Valuators { |
| 143 int x_scroll; |
| 144 int y_scroll; |
| 145 }; |
| 146 |
| 147 ScrollEventData() { |
| 148 UpdateDeviceList(ui::GetXDisplay()); |
| 149 } |
| 150 |
| 151 ~ScrollEventData() {} |
| 152 |
| 153 // A quick lookup table for determining if events from the pointer device |
| 154 // should be processed. |
| 155 static const int kMaxDeviceNum = 128; |
| 156 std::bitset<kMaxDeviceNum> scroll_devices_; |
| 157 std::map<int, Valuators> device_to_valuators_; |
| 158 |
| 159 DISALLOW_COPY_AND_ASSIGN(ScrollEventData); |
| 160 }; |
| 161 |
| 38 int GetEventFlagsFromXState(unsigned int state) { | 162 int GetEventFlagsFromXState(unsigned int state) { |
| 39 int flags = 0; | 163 int flags = 0; |
| 40 if (state & ControlMask) | 164 if (state & ControlMask) |
| 41 flags |= ui::EF_CONTROL_DOWN; | 165 flags |= ui::EF_CONTROL_DOWN; |
| 42 if (state & ShiftMask) | 166 if (state & ShiftMask) |
| 43 flags |= ui::EF_SHIFT_DOWN; | 167 flags |= ui::EF_SHIFT_DOWN; |
| 44 if (state & Mod1Mask) | 168 if (state & Mod1Mask) |
| 45 flags |= ui::EF_ALT_DOWN; | 169 flags |= ui::EF_ALT_DOWN; |
| 46 if (state & LockMask) | 170 if (state & LockMask) |
| 47 flags |= ui::EF_CAPS_LOCK_DOWN; | 171 flags |= ui::EF_CAPS_LOCK_DOWN; |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 185 return ET_MOUSE_MOVED; | 309 return ET_MOUSE_MOVED; |
| 186 case EnterNotify: | 310 case EnterNotify: |
| 187 return ET_MOUSE_ENTERED; | 311 return ET_MOUSE_ENTERED; |
| 188 case LeaveNotify: | 312 case LeaveNotify: |
| 189 return ET_MOUSE_EXITED; | 313 return ET_MOUSE_EXITED; |
| 190 case GenericEvent: { | 314 case GenericEvent: { |
| 191 XIDeviceEvent* xievent = | 315 XIDeviceEvent* xievent = |
| 192 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 316 static_cast<XIDeviceEvent*>(native_event->xcookie.data); |
| 193 if (TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid)) | 317 if (TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid)) |
| 194 return GetTouchEventType(native_event); | 318 return GetTouchEventType(native_event); |
| 195 int button = EventButtonFromNative(native_event); | |
| 196 switch (xievent->evtype) { | 319 switch (xievent->evtype) { |
| 197 case XI_ButtonPress: | 320 case XI_ButtonPress: |
| 198 if (button >= kMinWheelButton && | 321 case XI_ButtonRelease: { |
| 199 button <= kMaxWheelButton) | 322 int button = EventButtonFromNative(native_event); |
| 323 if (button >= kMinWheelButton && button <= kMaxWheelButton) |
| 200 return ET_MOUSEWHEEL; | 324 return ET_MOUSEWHEEL; |
| 201 return ET_MOUSE_PRESSED; | 325 return xievent->evtype == XI_ButtonPress ? |
| 202 case XI_ButtonRelease: | 326 ET_MOUSE_PRESSED : ET_MOUSE_RELEASED; |
| 203 if (button >= kMinWheelButton && | 327 } |
| 204 button <= kMaxWheelButton) | |
| 205 return ET_MOUSEWHEEL; | |
| 206 return ET_MOUSE_RELEASED; | |
| 207 case XI_Motion: | 328 case XI_Motion: |
| 208 return GetButtonMaskForX2Event(xievent) ? | 329 if (GetScrollOffsets(native_event, NULL, NULL)) |
| 209 ET_MOUSE_DRAGGED : ET_MOUSE_MOVED; | 330 return ET_SCROLL; |
| 331 else if (GetButtonMaskForX2Event(xievent)) |
| 332 return ET_MOUSE_DRAGGED; |
| 333 else |
| 334 return ET_MOUSE_MOVED; |
| 210 } | 335 } |
| 211 } | 336 } |
| 212 default: | 337 default: |
| 213 break; | 338 break; |
| 214 } | 339 } |
| 215 return ET_UNKNOWN; | 340 return ET_UNKNOWN; |
| 216 } | 341 } |
| 217 | 342 |
| 218 int EventFlagsFromNative(const base::NativeEvent& native_event) { | 343 int EventFlagsFromNative(const base::NativeEvent& native_event) { |
| 219 switch (native_event->type) { | 344 switch (native_event->type) { |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 398 0.0); | 523 0.0); |
| 399 unsigned int deviceid = | 524 unsigned int deviceid = |
| 400 static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; | 525 static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; |
| 401 // Force is normalized to fall into [0, 1] | 526 // Force is normalized to fall into [0, 1] |
| 402 if (!ui::TouchFactory::GetInstance()->NormalizeTouchParam( | 527 if (!ui::TouchFactory::GetInstance()->NormalizeTouchParam( |
| 403 deviceid, ui::TouchFactory::TP_PRESSURE, &force)) | 528 deviceid, ui::TouchFactory::TP_PRESSURE, &force)) |
| 404 force = 0.0; | 529 force = 0.0; |
| 405 return force; | 530 return force; |
| 406 } | 531 } |
| 407 | 532 |
| 533 bool GetScrollOffsets(const base::NativeEvent& native_event, |
| 534 float* x_offset, |
| 535 float* y_offset) { |
| 536 return ScrollEventData::GetInstance()->GetScrollOffsets( |
| 537 *native_event, x_offset, y_offset); |
| 538 } |
| 539 |
| 540 void UpdateDeviceList() { |
| 541 Display* display = GetXDisplay(); |
| 542 ScrollEventData::GetInstance()->UpdateDeviceList(display); |
| 543 TouchFactory::GetInstance()->UpdateDeviceList(display); |
| 544 } |
| 545 |
| 408 base::NativeEvent CreateNoopEvent() { | 546 base::NativeEvent CreateNoopEvent() { |
| 409 static XEvent* noop = NULL; | 547 static XEvent* noop = NULL; |
| 410 if (!noop) { | 548 if (!noop) { |
| 411 noop = new XEvent(); | 549 noop = new XEvent(); |
| 412 memset(noop, 0, sizeof(XEvent)); | 550 memset(noop, 0, sizeof(XEvent)); |
| 413 noop->xclient.type = ClientMessage; | 551 noop->xclient.type = ClientMessage; |
| 414 noop->xclient.window = None; | 552 noop->xclient.window = None; |
| 415 noop->xclient.format = 8; | 553 noop->xclient.format = 8; |
| 416 DCHECK(!noop->xclient.display); | 554 DCHECK(!noop->xclient.display); |
| 417 } | 555 } |
| 418 // TODO(oshima): Remove ifdef once gtk is removed from views. | 556 // TODO(oshima): Remove ifdef once gtk is removed from views. |
| 419 #if defined(TOOLKIT_USES_GTK) | 557 #if defined(TOOLKIT_USES_GTK) |
| 420 NOTREACHED(); | 558 NOTREACHED(); |
| 421 #else | 559 #else |
| 422 // Make sure we use atom from current xdisplay, which may | 560 // Make sure we use atom from current xdisplay, which may |
| 423 // change during the test. | 561 // change during the test. |
| 424 noop->xclient.message_type = XInternAtom( | 562 noop->xclient.message_type = XInternAtom( |
| 425 base::MessagePumpX::GetDefaultXDisplay(), | 563 base::MessagePumpX::GetDefaultXDisplay(), |
| 426 "noop", False); | 564 "noop", False); |
| 427 #endif | 565 #endif |
| 428 return noop; | 566 return noop; |
| 429 } | 567 } |
| 430 | 568 |
| 431 } // namespace ui | 569 } // namespace ui |
| OLD | NEW |