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/cursorfont.h> 8 #include <X11/extensions/XInput.h> 9 #include <X11/extensions/XInput2.h> 10 #include <X11/extensions/XIproto.h> 11 12 #include "base/basictypes.h" 13 #include "base/command_line.h" 14 #include "base/compiler_specific.h" 15 #include "base/logging.h" 16 #include "base/memory/singleton.h" 17 #include "base/message_loop/message_loop.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/strings/string_split.h" 20 #include "ui/events/event_switches.h" 21 #include "ui/events/x/device_data_manager.h" 22 #include "ui/events/x/device_list_cache_x.h" 23 #include "ui/gfx/x/x11_types.h" 24 25 namespace ui { 26 27 TouchFactory::TouchFactory() 28 : pointer_device_lookup_(), 29 touch_device_available_(false), 30 touch_events_disabled_(false), 31 touch_device_list_(), 32 max_touch_points_(-1), 33 id_generator_(0) { 34 if (!DeviceDataManager::GetInstance()->IsXInput2Available()) 35 return; 36 37 XDisplay* display = gfx::GetXDisplay(); 38 UpdateDeviceList(display); 39 40 CommandLine* cmdline = CommandLine::ForCurrentProcess(); 41 touch_events_disabled_ = cmdline->HasSwitch(switches::kTouchEvents) && 42 cmdline->GetSwitchValueASCII(switches::kTouchEvents) == 43 switches::kTouchEventsDisabled; 44 } 45 46 TouchFactory::~TouchFactory() { 47 } 48 49 // static 50 TouchFactory* TouchFactory::GetInstance() { 51 return Singleton<TouchFactory>::get(); 52 } 53 54 // static 55 void TouchFactory::SetTouchDeviceListFromCommandLine() { 56 // Get a list of pointer-devices that should be treated as touch-devices. 57 // This is primarily used for testing/debugging touch-event processing when a 58 // touch-device isn't available. 59 std::string touch_devices = 60 CommandLine::ForCurrentProcess()->GetSwitchValueASCII( 61 switches::kTouchDevices); 62 63 if (!touch_devices.empty()) { 64 std::vector<std::string> devs; 65 std::vector<unsigned int> device_ids; 66 unsigned int devid; 67 base::SplitString(touch_devices, ',', &devs); 68 for (std::vector<std::string>::iterator iter = devs.begin(); 69 iter != devs.end(); ++iter) { 70 if (base::StringToInt(*iter, reinterpret_cast<int*>(&devid))) 71 device_ids.push_back(devid); 72 else 73 DLOG(WARNING) << "Invalid touch-device id: " << *iter; 74 } 75 ui::TouchFactory::GetInstance()->SetTouchDeviceList(device_ids); 76 } 77 } 78 79 void TouchFactory::UpdateDeviceList(Display* display) { 80 // Detect touch devices. 81 touch_device_available_ = false; 82 touch_device_lookup_.reset(); 83 touch_device_list_.clear(); 84 max_touch_points_ = -1; 85 86 #if !defined(USE_XI2_MT) 87 // NOTE: The new API for retrieving the list of devices (XIQueryDevice) does 88 // not provide enough information to detect a touch device. As a result, the 89 // old version of query function (XListInputDevices) is used instead. 90 // If XInput2 is not supported, this will return null (with count of -1) so 91 // we assume there cannot be any touch devices. 92 // With XI2.1 or older, we allow only single touch devices. 93 XDeviceList dev_list = 94 DeviceListCacheX::GetInstance()->GetXDeviceList(display); 95 Atom xi_touchscreen = XInternAtom(display, XI_TOUCHSCREEN, false); 96 for (int i = 0; i < dev_list.count; i++) { 97 if (dev_list[i].type == xi_touchscreen) { 98 touch_device_lookup_[dev_list[i].id] = true; 99 touch_device_list_[dev_list[i].id] = false; 100 touch_device_available_ = true; 101 } 102 } 103 #endif 104 105 if (!DeviceDataManager::GetInstance()->IsXInput2Available()) 106 return; 107 108 // Instead of asking X for the list of devices all the time, let's maintain a 109 // list of pointer devices we care about. 110 // It should not be necessary to select for slave devices. XInput2 provides 111 // enough information to the event callback to decide which slave device 112 // triggered the event, thus decide whether the 'pointer event' is a 113 // 'mouse event' or a 'touch event'. 114 // However, on some desktops, some events from a master pointer are 115 // not delivered to the client. So we select for slave devices instead. 116 // If the touch device has 'GrabDevice' set and 'SendCoreEvents' unset (which 117 // is possible), then the device is detected as a floating device, and a 118 // floating device is not connected to a master device. So it is necessary to 119 // also select on the floating devices. 120 pointer_device_lookup_.reset(); 121 XIDeviceList xi_dev_list = 122 DeviceListCacheX::GetInstance()->GetXI2DeviceList(display); 123 for (int i = 0; i < xi_dev_list.count; i++) { 124 XIDeviceInfo* devinfo = xi_dev_list.devices + i; 125 if (devinfo->use == XIFloatingSlave || devinfo->use == XIMasterPointer) { 126 #if defined(USE_XI2_MT) 127 for (int k = 0; k < devinfo->num_classes; ++k) { 128 XIAnyClassInfo* xiclassinfo = devinfo->classes[k]; 129 if (xiclassinfo->type == XITouchClass) { 130 XITouchClassInfo* tci = 131 reinterpret_cast<XITouchClassInfo *>(xiclassinfo); 132 // Only care direct touch device (such as touch screen) right now 133 if (tci->mode == XIDirectTouch) { 134 touch_device_lookup_[devinfo->deviceid] = true; 135 touch_device_list_[devinfo->deviceid] = true; 136 touch_device_available_ = true; 137 if (tci->num_touches > 0 && tci->num_touches > max_touch_points_) 138 max_touch_points_ = tci->num_touches; 139 } 140 } 141 } 142 #endif 143 pointer_device_lookup_[devinfo->deviceid] = true; 144 } 145 } 146 } 147 148 bool TouchFactory::ShouldProcessXI2Event(XEvent* xev) { 149 DCHECK_EQ(GenericEvent, xev->type); 150 XIEvent* event = static_cast<XIEvent*>(xev->xcookie.data); 151 XIDeviceEvent* xiev = reinterpret_cast<XIDeviceEvent*>(event); 152 153 #if defined(USE_XI2_MT) 154 if (event->evtype == XI_TouchBegin || 155 event->evtype == XI_TouchUpdate || 156 event->evtype == XI_TouchEnd) { 157 return !touch_events_disabled_ && IsTouchDevice(xiev->deviceid); 158 } 159 #endif 160 if (event->evtype != XI_ButtonPress && 161 event->evtype != XI_ButtonRelease && 162 event->evtype != XI_Motion) 163 return true; 164 165 if (!pointer_device_lookup_[xiev->deviceid]) 166 return false; 167 168 return IsTouchDevice(xiev->deviceid) ? !touch_events_disabled_ : true; 169 } 170 171 void TouchFactory::SetupXI2ForXWindow(Window window) { 172 // Setup mask for mouse events. It is possible that a device is loaded/plugged 173 // in after we have setup XInput2 on a window. In such cases, we need to 174 // either resetup XInput2 for the window, so that we get events from the new 175 // device, or we need to listen to events from all devices, and then filter 176 // the events from uninteresting devices. We do the latter because that's 177 // simpler. 178 179 XDisplay* display = gfx::GetXDisplay(); 180 181 unsigned char mask[XIMaskLen(XI_LASTEVENT)]; 182 memset(mask, 0, sizeof(mask)); 183 184 #if defined(USE_XI2_MT) 185 XISetMask(mask, XI_TouchBegin); 186 XISetMask(mask, XI_TouchUpdate); 187 XISetMask(mask, XI_TouchEnd); 188 #endif 189 XISetMask(mask, XI_ButtonPress); 190 XISetMask(mask, XI_ButtonRelease); 191 XISetMask(mask, XI_Motion); 192 193 XIEventMask evmask; 194 evmask.deviceid = XIAllDevices; 195 evmask.mask_len = sizeof(mask); 196 evmask.mask = mask; 197 XISelectEvents(display, window, &evmask, 1); 198 XFlush(display); 199 } 200 201 void TouchFactory::SetTouchDeviceList( 202 const std::vector<unsigned int>& devices) { 203 touch_device_lookup_.reset(); 204 touch_device_list_.clear(); 205 for (std::vector<unsigned int>::const_iterator iter = devices.begin(); 206 iter != devices.end(); ++iter) { 207 DCHECK(*iter < touch_device_lookup_.size()); 208 touch_device_lookup_[*iter] = true; 209 touch_device_list_[*iter] = false; 210 } 211 } 212 213 bool TouchFactory::IsTouchDevice(unsigned deviceid) const { 214 return deviceid < touch_device_lookup_.size() ? 215 touch_device_lookup_[deviceid] : false; 216 } 217 218 bool TouchFactory::IsMultiTouchDevice(unsigned int deviceid) const { 219 return (deviceid < touch_device_lookup_.size() && 220 touch_device_lookup_[deviceid]) ? 221 touch_device_list_.find(deviceid)->second : 222 false; 223 } 224 225 bool TouchFactory::QuerySlotForTrackingID(uint32 tracking_id, int* slot) { 226 if (!id_generator_.HasGeneratedIDFor(tracking_id)) 227 return false; 228 *slot = static_cast<int>(id_generator_.GetGeneratedID(tracking_id)); 229 return true; 230 } 231 232 int TouchFactory::GetSlotForTrackingID(uint32 tracking_id) { 233 return id_generator_.GetGeneratedID(tracking_id); 234 } 235 236 void TouchFactory::ReleaseSlotForTrackingID(uint32 tracking_id) { 237 id_generator_.ReleaseNumber(tracking_id); 238 } 239 240 bool TouchFactory::IsTouchDevicePresent() { 241 return !touch_events_disabled_ && touch_device_available_; 242 } 243 244 int TouchFactory::GetMaxTouchPoints() const { 245 return max_touch_points_; 246 } 247 248 void TouchFactory::SetTouchDeviceForTest( 249 const std::vector<unsigned int>& devices) { 250 touch_device_lookup_.reset(); 251 touch_device_list_.clear(); 252 for (std::vector<unsigned int>::const_iterator iter = devices.begin(); 253 iter != devices.end(); ++iter) { 254 DCHECK(*iter < touch_device_lookup_.size()); 255 touch_device_lookup_[*iter] = true; 256 touch_device_list_[*iter] = true; 257 } 258 touch_device_available_ = true; 259 touch_events_disabled_ = false; 260 } 261 262 void TouchFactory::SetPointerDeviceForTest( 263 const std::vector<unsigned int>& devices) { 264 pointer_device_lookup_.reset(); 265 for (std::vector<unsigned int>::const_iterator iter = devices.begin(); 266 iter != devices.end(); ++iter) { 267 pointer_device_lookup_[*iter] = true; 268 } 269 } 270 271 } // namespace ui 272