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/events/x/touch_factory_x11.h" 6 7 #include <X11/Xatom.h> 8 #include <X11/cursorfont.h> 9 #include <X11/extensions/XInput.h> 10 #include <X11/extensions/XInput2.h> 11 #include <X11/extensions/XIproto.h> 12 13 #include "base/basictypes.h" 14 #include "base/command_line.h" 15 #include "base/compiler_specific.h" 16 #include "base/logging.h" 17 #include "base/memory/singleton.h" 18 #include "base/message_loop/message_loop.h" 19 #include "base/strings/string_number_conversions.h" 20 #include "base/strings/string_split.h" 21 #include "base/sys_info.h" 22 #include "ui/events/event_switches.h" 23 #include "ui/events/x/device_data_manager_x11.h" 24 #include "ui/events/x/device_list_cache_x.h" 25 #include "ui/gfx/x/x11_types.h" 26 27 namespace ui { 28 29 TouchFactory::TouchFactory() 30 : pointer_device_lookup_(), 31 touch_device_available_(false), 32 touch_events_disabled_(false), 33 touch_device_list_(), 34 max_touch_points_(-1), 35 virtual_core_keyboard_device_(-1), 36 id_generator_(0) { 37 if (!DeviceDataManagerX11::GetInstance()->IsXInput2Available()) 38 return; 39 40 XDisplay* display = gfx::GetXDisplay(); 41 UpdateDeviceList(display); 42 43 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 44 touch_events_disabled_ = cmdline->HasSwitch(switches::kTouchEvents) && 45 cmdline->GetSwitchValueASCII(switches::kTouchEvents) == 46 switches::kTouchEventsDisabled; 47 } 48 49 TouchFactory::~TouchFactory() { 50 } 51 52 // static 53 TouchFactory* TouchFactory::GetInstance() { 54 return Singleton<TouchFactory>::get(); 55 } 56 57 // static 58 void TouchFactory::SetTouchDeviceListFromCommandLine() { 59 // Get a list of pointer-devices that should be treated as touch-devices. 60 // This is primarily used for testing/debugging touch-event processing when a 61 // touch-device isn't available. 62 std::string touch_devices = 63 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 64 switches::kTouchDevices); 65 66 if (!touch_devices.empty()) { 67 std::vector<std::string> devs; 68 std::vector<unsigned int> device_ids; 69 unsigned int devid; 70 base::SplitString(touch_devices, ',', &devs); 71 for (std::vector<std::string>::iterator iter = devs.begin(); 72 iter != devs.end(); ++iter) { 73 if (base::StringToInt(*iter, reinterpret_cast<int*>(&devid))) 74 device_ids.push_back(devid); 75 else 76 DLOG(WARNING) << "Invalid touch-device id: " << *iter; 77 } 78 ui::TouchFactory::GetInstance()->SetTouchDeviceList(device_ids); 79 } 80 } 81 82 void TouchFactory::UpdateDeviceList(Display* display) { 83 // Detect touch devices. 84 touch_device_available_ = false; 85 touch_device_lookup_.reset(); 86 touch_device_list_.clear(); 87 touchscreen_ids_.clear(); 88 max_touch_points_ = -1; 89 90 #if !defined(USE_XI2_MT) 91 // NOTE: The new API for retrieving the list of devices (XIQueryDevice) does 92 // not provide enough information to detect a touch device. As a result, the 93 // old version of query function (XListInputDevices) is used instead. 94 // If XInput2 is not supported, this will return null (with count of -1) so 95 // we assume there cannot be any touch devices. 96 // With XI2.1 or older, we allow only single touch devices. 97 XDeviceList dev_list = 98 DeviceListCacheX::GetInstance()->GetXDeviceList(display); 99 Atom xi_touchscreen = XInternAtom(display, XI_TOUCHSCREEN, false); 100 for (int i = 0; i < dev_list.count; i++) { 101 if (dev_list[i].type == xi_touchscreen) { 102 touch_device_lookup_[dev_list[i].id] = true; 103 touch_device_list_[dev_list[i].id] = false; 104 touch_device_available_ = true; 105 } 106 } 107 #endif 108 109 if (!DeviceDataManagerX11::GetInstance()->IsXInput2Available()) 110 return; 111 112 // Instead of asking X for the list of devices all the time, let's maintain a 113 // list of pointer devices we care about. 114 // It should not be necessary to select for slave devices. XInput2 provides 115 // enough information to the event callback to decide which slave device 116 // triggered the event, thus decide whether the 'pointer event' is a 117 // 'mouse event' or a 'touch event'. 118 // However, on some desktops, some events from a master pointer are 119 // not delivered to the client. So we select for slave devices instead. 120 // If the touch device has 'GrabDevice' set and 'SendCoreEvents' unset (which 121 // is possible), then the device is detected as a floating device, and a 122 // floating device is not connected to a master device. So it is necessary to 123 // also select on the floating devices. 124 pointer_device_lookup_.reset(); 125 XIDeviceList xi_dev_list = 126 DeviceListCacheX::GetInstance()->GetXI2DeviceList(display); 127 for (int i = 0; i < xi_dev_list.count; i++) { 128 XIDeviceInfo* devinfo = xi_dev_list.devices + i; 129 if (devinfo->use == XIFloatingSlave || devinfo->use == XIMasterPointer) { 130 #if defined(USE_XI2_MT) 131 for (int k = 0; k < devinfo->num_classes; ++k) { 132 XIAnyClassInfo* xiclassinfo = devinfo->classes[k]; 133 if (xiclassinfo->type == XITouchClass) { 134 XITouchClassInfo* tci = 135 reinterpret_cast<XITouchClassInfo*>(xiclassinfo); 136 // Only care direct touch device (such as touch screen) right now 137 if (tci->mode == XIDirectTouch) { 138 touch_device_lookup_[devinfo->deviceid] = true; 139 touch_device_list_[devinfo->deviceid] = true; 140 touch_device_available_ = true; 141 if (tci->num_touches > 0 && tci->num_touches > max_touch_points_) 142 max_touch_points_ = tci->num_touches; 143 } 144 } 145 } 146 #endif 147 pointer_device_lookup_[devinfo->deviceid] = true; 148 } else if (devinfo->use == XIMasterKeyboard) { 149 virtual_core_keyboard_device_ = devinfo->deviceid; 150 } 151 152 #if defined(USE_XI2_MT) 153 if (devinfo->use == XIFloatingSlave || devinfo->use == XISlavePointer) { 154 for (int k = 0; k < devinfo->num_classes; ++k) { 155 XIAnyClassInfo* xiclassinfo = devinfo->classes[k]; 156 if (xiclassinfo->type == XITouchClass) { 157 XITouchClassInfo* tci = 158 reinterpret_cast<XITouchClassInfo*>(xiclassinfo); 159 // Only care direct touch device (such as touch screen) right now 160 if (tci->mode == XIDirectTouch) 161 CacheTouchscreenIds(display, devinfo->deviceid); 162 } 163 } 164 } 165 #endif 166 } 167 } 168 169 bool TouchFactory::ShouldProcessXI2Event(XEvent* xev) { 170 DCHECK_EQ(GenericEvent, xev->type); 171 XIEvent* event = static_cast<XIEvent*>(xev->xcookie.data); 172 XIDeviceEvent* xiev = reinterpret_cast<XIDeviceEvent*>(event); 173 174 #if defined(USE_XI2_MT) 175 if (event->evtype == XI_TouchBegin || 176 event->evtype == XI_TouchUpdate || 177 event->evtype == XI_TouchEnd) { 178 return !touch_events_disabled_ && IsTouchDevice(xiev->deviceid); 179 } 180 #endif 181 // Make sure only key-events from the virtual core keyboard are processed. 182 if (event->evtype == XI_KeyPress || event->evtype == XI_KeyRelease) { 183 return (virtual_core_keyboard_device_ < 0) || 184 (virtual_core_keyboard_device_ == xiev->deviceid); 185 } 186 187 if (event->evtype != XI_ButtonPress && 188 event->evtype != XI_ButtonRelease && 189 event->evtype != XI_Motion) 190 return true; 191 192 if (!pointer_device_lookup_[xiev->deviceid]) 193 return false; 194 195 return IsTouchDevice(xiev->deviceid) ? !touch_events_disabled_ : true; 196 } 197 198 void TouchFactory::SetupXI2ForXWindow(Window window) { 199 // Setup mask for mouse events. It is possible that a device is loaded/plugged 200 // in after we have setup XInput2 on a window. In such cases, we need to 201 // either resetup XInput2 for the window, so that we get events from the new 202 // device, or we need to listen to events from all devices, and then filter 203 // the events from uninteresting devices. We do the latter because that's 204 // simpler. 205 206 XDisplay* display = gfx::GetXDisplay(); 207 208 unsigned char mask[XIMaskLen(XI_LASTEVENT)]; 209 memset(mask, 0, sizeof(mask)); 210 211 #if defined(USE_XI2_MT) 212 XISetMask(mask, XI_TouchBegin); 213 XISetMask(mask, XI_TouchUpdate); 214 XISetMask(mask, XI_TouchEnd); 215 #endif 216 XISetMask(mask, XI_ButtonPress); 217 XISetMask(mask, XI_ButtonRelease); 218 XISetMask(mask, XI_Motion); 219 #if defined(OS_CHROMEOS) 220 if (base::SysInfo::IsRunningOnChromeOS()) { 221 XISetMask(mask, XI_KeyPress); 222 XISetMask(mask, XI_KeyRelease); 223 } 224 #endif 225 226 XIEventMask evmask; 227 evmask.deviceid = XIAllDevices; 228 evmask.mask_len = sizeof(mask); 229 evmask.mask = mask; 230 XISelectEvents(display, window, &evmask, 1); 231 XFlush(display); 232 } 233 234 void TouchFactory::SetTouchDeviceList( 235 const std::vector<unsigned int>& devices) { 236 touch_device_lookup_.reset(); 237 touch_device_list_.clear(); 238 for (std::vector<unsigned int>::const_iterator iter = devices.begin(); 239 iter != devices.end(); ++iter) { 240 DCHECK(*iter < touch_device_lookup_.size()); 241 touch_device_lookup_[*iter] = true; 242 touch_device_list_[*iter] = false; 243 } 244 } 245 246 bool TouchFactory::IsTouchDevice(unsigned deviceid) const { 247 return deviceid < touch_device_lookup_.size() ? 248 touch_device_lookup_[deviceid] : false; 249 } 250 251 bool TouchFactory::IsMultiTouchDevice(unsigned int deviceid) const { 252 return (deviceid < touch_device_lookup_.size() && 253 touch_device_lookup_[deviceid]) ? 254 touch_device_list_.find(deviceid)->second : 255 false; 256 } 257 258 bool TouchFactory::QuerySlotForTrackingID(uint32 tracking_id, int* slot) { 259 if (!id_generator_.HasGeneratedIDFor(tracking_id)) 260 return false; 261 *slot = static_cast<int>(id_generator_.GetGeneratedID(tracking_id)); 262 return true; 263 } 264 265 int TouchFactory::GetSlotForTrackingID(uint32 tracking_id) { 266 return id_generator_.GetGeneratedID(tracking_id); 267 } 268 269 void TouchFactory::AcquireSlotForTrackingID(uint32 tracking_id) { 270 tracking_id_refcounts_[tracking_id]++; 271 } 272 273 void TouchFactory::ReleaseSlotForTrackingID(uint32 tracking_id) { 274 tracking_id_refcounts_[tracking_id]--; 275 if (tracking_id_refcounts_[tracking_id] == 0) 276 id_generator_.ReleaseNumber(tracking_id); 277 } 278 279 bool TouchFactory::IsTouchDevicePresent() { 280 return !touch_events_disabled_ && touch_device_available_; 281 } 282 283 int TouchFactory::GetMaxTouchPoints() const { 284 return max_touch_points_; 285 } 286 287 void TouchFactory::ResetForTest() { 288 pointer_device_lookup_.reset(); 289 touch_device_lookup_.reset(); 290 touch_device_available_ = false; 291 touch_events_disabled_ = false; 292 touch_device_list_.clear(); 293 touchscreen_ids_.clear(); 294 tracking_id_refcounts_.clear(); 295 max_touch_points_ = -1; 296 id_generator_.ResetForTest(); 297 } 298 299 void TouchFactory::SetTouchDeviceForTest( 300 const std::vector<unsigned int>& devices) { 301 touch_device_lookup_.reset(); 302 touch_device_list_.clear(); 303 for (std::vector<unsigned int>::const_iterator iter = devices.begin(); 304 iter != devices.end(); ++iter) { 305 DCHECK(*iter < touch_device_lookup_.size()); 306 touch_device_lookup_[*iter] = true; 307 touch_device_list_[*iter] = true; 308 } 309 touch_device_available_ = true; 310 touch_events_disabled_ = false; 311 } 312 313 void TouchFactory::SetPointerDeviceForTest( 314 const std::vector<unsigned int>& devices) { 315 pointer_device_lookup_.reset(); 316 for (std::vector<unsigned int>::const_iterator iter = devices.begin(); 317 iter != devices.end(); ++iter) { 318 pointer_device_lookup_[*iter] = true; 319 } 320 } 321 322 void TouchFactory::CacheTouchscreenIds(Display* display, int device_id) { 323 XDevice* device = XOpenDevice(display, device_id); 324 if (!device) 325 return; 326 327 Atom actual_type_return; 328 int actual_format_return; 329 unsigned long nitems_return; 330 unsigned long bytes_after_return; 331 unsigned char *prop_return; 332 333 const char kDeviceProductIdString[] = "Device Product ID"; 334 Atom device_product_id_atom = 335 XInternAtom(display, kDeviceProductIdString, false); 336 337 if (device_product_id_atom != None && 338 XGetDeviceProperty(display, device, device_product_id_atom, 0, 2, 339 False, XA_INTEGER, &actual_type_return, 340 &actual_format_return, &nitems_return, 341 &bytes_after_return, &prop_return) == Success) { 342 if (actual_type_return == XA_INTEGER && 343 actual_format_return == 32 && 344 nitems_return == 2) { 345 // An actual_format_return of 32 implies that the returned data is an 346 // array of longs. See the description of |prop_return| in `man 347 // XGetDeviceProperty` for details. 348 long* ptr = reinterpret_cast<long*>(prop_return); 349 350 // Internal displays will have a vid and pid of 0. Ignore them. 351 // ptr[0] is the vid, and ptr[1] is the pid. 352 if (ptr[0] || ptr[1]) 353 touchscreen_ids_.insert(std::make_pair(ptr[0], ptr[1])); 354 } 355 XFree(prop_return); 356 } 357 358 XCloseDevice(display, device); 359 } 360 361 } // namespace ui 362