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 |