Chromium Code Reviews| 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" | |
|
Daniel Erat
2011/12/14 21:49:07
nit: just one space between symbol name and string
DaveMoore
2011/12/15 17:07:21
Done.
| |
| 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 int count; | |
|
Daniel Erat
2011/12/14 21:49:07
nit: initialize to 0
DaveMoore
2011/12/15 17:07:21
Done.
| |
| 54 scroll_devices_.reset(); | |
| 55 device_to_valuators_.clear(); | |
| 56 XDeviceInfo* dev_list = XListInputDevices(display, &count); | |
| 57 Atom xi_touchpad = XInternAtom(display, XI_TOUCHPAD, false); | |
| 58 for (int i = 0; i < count; ++i) { | |
| 59 XDeviceInfo* dev = dev_list + i; | |
| 60 if (dev->type == xi_touchpad) | |
| 61 scroll_devices_[dev_list[i].id] = true; | |
| 62 } | |
| 63 if (dev_list) | |
| 64 XFreeDeviceList(dev_list); | |
| 65 | |
| 66 XIDeviceInfo* info_list = XIQueryDevice(display, XIAllDevices, &count); | |
| 67 Atom x_axis = XInternAtom(display, AXIS_LABEL_PROP_REL_HWHEEL, false); | |
| 68 Atom y_axis = XInternAtom(display, AXIS_LABEL_PROP_REL_WHEEL, false); | |
| 69 for (int i = 0; i < count; ++i) { | |
| 70 XIDeviceInfo* info = info_list + i; | |
| 71 | |
| 72 if (!scroll_devices_[info->deviceid]) | |
| 73 continue; | |
| 74 | |
| 75 if (info->use != XISlavePointer && info->use != XIFloatingSlave) { | |
| 76 scroll_devices_[info->deviceid] = false; | |
| 77 continue; | |
| 78 } | |
| 79 | |
| 80 Valuators valuators = {-1, -1}; | |
| 81 for (int j = 0; j < info->num_classes; ++j) { | |
| 82 if (info->classes[j]->type != XIValuatorClass) | |
| 83 continue; | |
| 84 | |
| 85 XIValuatorClassInfo* v = | |
| 86 reinterpret_cast<XIValuatorClassInfo*>(info->classes[j]); | |
| 87 if (v->label == x_axis) | |
| 88 valuators.x_scroll = v->number; | |
| 89 else if (v->label == y_axis) | |
| 90 valuators.y_scroll = v->number; | |
| 91 } | |
| 92 if (valuators.x_scroll >= 0 && valuators.y_scroll >= 0) | |
| 93 device_to_valuators_[info->deviceid] = valuators; | |
| 94 else | |
| 95 scroll_devices_[info->deviceid] = false; | |
| 96 } | |
| 97 } | |
| 98 | |
| 99 // Returns true if this is a scroll event (a motion event with the necessary | |
| 100 // valuators. Also returns the offsets. |x_offset| and |y_offset| can be | |
|
Daniel Erat
2011/12/14 21:49:07
nit: add ')' after 'valuators'
either initialize
DaveMoore
2011/12/15 17:07:21
Done.
| |
| 101 // NULL. | |
| 102 bool GetScrollOffsets(const XEvent& xev, float* x_offset, float* y_offset) { | |
| 103 XIDeviceEvent* xiev = static_cast<XIDeviceEvent*>(xev.xcookie.data); | |
| 104 if (!scroll_devices_[xiev->deviceid]) | |
| 105 return false; | |
| 106 | |
| 107 int x_scroll = device_to_valuators_[xiev->deviceid].x_scroll; | |
| 108 int y_scroll = device_to_valuators_[xiev->deviceid].y_scroll; | |
| 109 | |
| 110 bool has_x_offset = XIMaskIsSet(xiev->valuators.mask, x_scroll); | |
| 111 bool has_y_offset = XIMaskIsSet(xiev->valuators.mask, y_scroll); | |
| 112 bool is_scroll = has_x_offset || has_y_offset; | |
| 113 | |
| 114 if (!x_offset && !y_offset) | |
| 115 return is_scroll; | |
| 116 | |
| 117 double* valuators = xiev->valuators.values; | |
| 118 for (int i = 0; i < xiev->valuators.mask_len * 8; ++i) { | |
| 119 if (XIMaskIsSet(xiev->valuators.mask, i)) { | |
| 120 if (x_offset && x_scroll == i) | |
| 121 *x_offset = -(*valuators); | |
| 122 else if (y_offset && y_scroll == i) | |
| 123 *y_offset = -(*valuators); | |
| 124 valuators++; | |
| 125 } | |
| 126 } | |
| 127 | |
| 128 return is_scroll; | |
| 129 } | |
| 130 | |
| 131 private: | |
| 132 // Requirement for Singleton | |
| 133 friend struct DefaultSingletonTraits<ScrollEventData>; | |
| 134 | |
| 135 struct Valuators { | |
| 136 int x_scroll; | |
| 137 int y_scroll; | |
| 138 }; | |
| 139 | |
| 140 ScrollEventData() { | |
| 141 UpdateDeviceList(ui::GetXDisplay()); | |
| 142 } | |
| 143 | |
| 144 ~ScrollEventData() {} | |
| 145 | |
| 146 // A quick lookup table for determining if events from the pointer device | |
| 147 // should be processed. | |
| 148 static const int kMaxDeviceNum = 128; | |
| 149 std::bitset<kMaxDeviceNum> scroll_devices_; | |
| 150 std::map<int, Valuators> device_to_valuators_; | |
| 151 | |
| 152 DISALLOW_COPY_AND_ASSIGN(ScrollEventData); | |
| 153 }; | |
| 154 | |
| 38 int GetEventFlagsFromXState(unsigned int state) { | 155 int GetEventFlagsFromXState(unsigned int state) { |
| 39 int flags = 0; | 156 int flags = 0; |
| 40 if (state & ControlMask) | 157 if (state & ControlMask) |
| 41 flags |= ui::EF_CONTROL_DOWN; | 158 flags |= ui::EF_CONTROL_DOWN; |
| 42 if (state & ShiftMask) | 159 if (state & ShiftMask) |
| 43 flags |= ui::EF_SHIFT_DOWN; | 160 flags |= ui::EF_SHIFT_DOWN; |
| 44 if (state & Mod1Mask) | 161 if (state & Mod1Mask) |
| 45 flags |= ui::EF_ALT_DOWN; | 162 flags |= ui::EF_ALT_DOWN; |
| 46 if (state & LockMask) | 163 if (state & LockMask) |
| 47 flags |= ui::EF_CAPS_LOCK_DOWN; | 164 flags |= ui::EF_CAPS_LOCK_DOWN; |
| (...skipping 137 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 185 return ET_MOUSE_MOVED; | 302 return ET_MOUSE_MOVED; |
| 186 case EnterNotify: | 303 case EnterNotify: |
| 187 return ET_MOUSE_ENTERED; | 304 return ET_MOUSE_ENTERED; |
| 188 case LeaveNotify: | 305 case LeaveNotify: |
| 189 return ET_MOUSE_EXITED; | 306 return ET_MOUSE_EXITED; |
| 190 case GenericEvent: { | 307 case GenericEvent: { |
| 191 XIDeviceEvent* xievent = | 308 XIDeviceEvent* xievent = |
| 192 static_cast<XIDeviceEvent*>(native_event->xcookie.data); | 309 static_cast<XIDeviceEvent*>(native_event->xcookie.data); |
| 193 if (TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid)) | 310 if (TouchFactory::GetInstance()->IsTouchDevice(xievent->sourceid)) |
| 194 return GetTouchEventType(native_event); | 311 return GetTouchEventType(native_event); |
| 195 int button = EventButtonFromNative(native_event); | |
| 196 switch (xievent->evtype) { | 312 switch (xievent->evtype) { |
| 197 case XI_ButtonPress: | 313 case XI_ButtonPress: |
| 198 if (button >= kMinWheelButton && | 314 case XI_ButtonRelease: { |
| 199 button <= kMaxWheelButton) | 315 int button = EventButtonFromNative(native_event); |
| 316 if (button >= kMinWheelButton && button <= kMaxWheelButton) | |
| 200 return ET_MOUSEWHEEL; | 317 return ET_MOUSEWHEEL; |
| 201 return ET_MOUSE_PRESSED; | 318 return xievent->evtype == XI_ButtonPress ? |
| 202 case XI_ButtonRelease: | 319 ET_MOUSE_PRESSED : ET_MOUSE_RELEASED; |
| 203 if (button >= kMinWheelButton && | 320 } |
| 204 button <= kMaxWheelButton) | |
| 205 return ET_MOUSEWHEEL; | |
| 206 return ET_MOUSE_RELEASED; | |
| 207 case XI_Motion: | 321 case XI_Motion: |
| 208 return GetButtonMaskForX2Event(xievent) ? | 322 if (GetScrollOffsets(native_event, NULL, NULL)) |
| 209 ET_MOUSE_DRAGGED : ET_MOUSE_MOVED; | 323 return ET_SCROLL; |
| 324 else if (GetButtonMaskForX2Event(xievent)) | |
| 325 return ET_MOUSE_DRAGGED; | |
| 326 else | |
| 327 return ET_MOUSE_MOVED; | |
| 210 } | 328 } |
| 211 } | 329 } |
| 212 default: | 330 default: |
| 213 break; | 331 break; |
| 214 } | 332 } |
| 215 return ET_UNKNOWN; | 333 return ET_UNKNOWN; |
| 216 } | 334 } |
| 217 | 335 |
| 218 int EventFlagsFromNative(const base::NativeEvent& native_event) { | 336 int EventFlagsFromNative(const base::NativeEvent& native_event) { |
| 219 switch (native_event->type) { | 337 switch (native_event->type) { |
| (...skipping 178 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 398 0.0); | 516 0.0); |
| 399 unsigned int deviceid = | 517 unsigned int deviceid = |
| 400 static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; | 518 static_cast<XIDeviceEvent*>(native_event->xcookie.data)->sourceid; |
| 401 // Force is normalized to fall into [0, 1] | 519 // Force is normalized to fall into [0, 1] |
| 402 if (!ui::TouchFactory::GetInstance()->NormalizeTouchParam( | 520 if (!ui::TouchFactory::GetInstance()->NormalizeTouchParam( |
| 403 deviceid, ui::TouchFactory::TP_PRESSURE, &force)) | 521 deviceid, ui::TouchFactory::TP_PRESSURE, &force)) |
| 404 force = 0.0; | 522 force = 0.0; |
| 405 return force; | 523 return force; |
| 406 } | 524 } |
| 407 | 525 |
| 526 bool GetScrollOffsets(const base::NativeEvent& native_event, | |
| 527 float* x_offset, | |
| 528 float* y_offset) { | |
| 529 return ScrollEventData::GetInstance()->GetScrollOffsets( | |
| 530 *native_event, x_offset, y_offset); | |
| 531 } | |
| 532 | |
| 533 void UpdateDeviceList() { | |
| 534 Display* display = GetXDisplay(); | |
| 535 ScrollEventData::GetInstance()->UpdateDeviceList(display); | |
| 536 TouchFactory::GetInstance()->UpdateDeviceList(display); | |
| 537 } | |
| 538 | |
| 408 base::NativeEvent CreateNoopEvent() { | 539 base::NativeEvent CreateNoopEvent() { |
| 409 static XEvent* noop = NULL; | 540 static XEvent* noop = NULL; |
| 410 if (!noop) { | 541 if (!noop) { |
| 411 noop = new XEvent(); | 542 noop = new XEvent(); |
| 412 memset(noop, 0, sizeof(XEvent)); | 543 memset(noop, 0, sizeof(XEvent)); |
| 413 noop->xclient.type = ClientMessage; | 544 noop->xclient.type = ClientMessage; |
| 414 noop->xclient.window = None; | 545 noop->xclient.window = None; |
| 415 noop->xclient.format = 8; | 546 noop->xclient.format = 8; |
| 416 DCHECK(!noop->xclient.display); | 547 DCHECK(!noop->xclient.display); |
| 417 } | 548 } |
| 418 // TODO(oshima): Remove ifdef once gtk is removed from views. | 549 // TODO(oshima): Remove ifdef once gtk is removed from views. |
| 419 #if defined(TOOLKIT_USES_GTK) | 550 #if defined(TOOLKIT_USES_GTK) |
| 420 NOTREACHED(); | 551 NOTREACHED(); |
| 421 #else | 552 #else |
| 422 // Make sure we use atom from current xdisplay, which may | 553 // Make sure we use atom from current xdisplay, which may |
| 423 // change during the test. | 554 // change during the test. |
| 424 noop->xclient.message_type = XInternAtom( | 555 noop->xclient.message_type = XInternAtom( |
| 425 base::MessagePumpX::GetDefaultXDisplay(), | 556 base::MessagePumpX::GetDefaultXDisplay(), |
| 426 "noop", False); | 557 "noop", False); |
| 427 #endif | 558 #endif |
| 428 return noop; | 559 return noop; |
| 429 } | 560 } |
| 430 | 561 |
| 431 } // namespace ui | 562 } // namespace ui |
| OLD | NEW |