Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(20)

Side by Side Diff: ui/events/platform/x11/x11_hotplug_event_handler.cc

Issue 688163004: Move X11 device processing to worker threads (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@fix-blocking-touchscreen
Patch Set: Copy device information Created 6 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/events/platform/x11/x11_hotplug_event_handler.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2014 The Chromium Authors. All rights reserved. 1 // Copyright 2014 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/events/platform/x11/x11_hotplug_event_handler.h" 5 #include "ui/events/platform/x11/x11_hotplug_event_handler.h"
6 6
7 #include <X11/Xatom.h>
7 #include <X11/extensions/XInput.h> 8 #include <X11/extensions/XInput.h>
8 #include <X11/extensions/XInput2.h> 9 #include <X11/extensions/XInput2.h>
9 10
10 #include <algorithm> 11 #include <algorithm>
11 #include <cmath> 12 #include <cmath>
12 #include <set> 13 #include <set>
13 #include <string> 14 #include <string>
14 #include <vector> 15 #include <vector>
15 16
17 #include "base/bind.h"
16 #include "base/command_line.h" 18 #include "base/command_line.h"
19 #include "base/location.h"
17 #include "base/logging.h" 20 #include "base/logging.h"
18 #include "base/process/launch.h" 21 #include "base/process/launch.h"
22 #include "base/single_thread_task_runner.h"
19 #include "base/strings/string_util.h" 23 #include "base/strings/string_util.h"
20 #include "base/sys_info.h" 24 #include "base/sys_info.h"
25 #include "base/thread_task_runner_handle.h"
26 #include "base/threading/worker_pool.h"
27 #include "ui/events/devices/device_data_manager.h"
21 #include "ui/events/devices/device_hotplug_event_observer.h" 28 #include "ui/events/devices/device_hotplug_event_observer.h"
22 #include "ui/events/devices/device_util_linux.h" 29 #include "ui/events/devices/device_util_linux.h"
23 #include "ui/events/devices/input_device.h" 30 #include "ui/events/devices/input_device.h"
24 #include "ui/events/devices/keyboard_device.h" 31 #include "ui/events/devices/keyboard_device.h"
25 #include "ui/events/devices/touchscreen_device.h" 32 #include "ui/events/devices/touchscreen_device.h"
26 #include "ui/gfx/x/x11_types.h" 33 #include "ui/gfx/x/x11_types.h"
27 34
28 namespace ui { 35 namespace ui {
29 36
30 namespace { 37 namespace {
31 38
32 // The name of the xinput device corresponding to the AT internal keyboard. 39 // The name of the xinput device corresponding to the AT internal keyboard.
33 const char kATKeyboardName[] = "AT Translated Set 2 keyboard"; 40 const char kATKeyboardName[] = "AT Translated Set 2 keyboard";
34 41
35 // The prefix of xinput devices corresponding to CrOS EC internal keyboards. 42 // The prefix of xinput devices corresponding to CrOS EC internal keyboards.
36 const char kCrosEcKeyboardPrefix[] = "cros-ec"; 43 const char kCrosEcKeyboardPrefix[] = "cros-ec";
37 44
45 typedef base::Callback<void(const std::vector<KeyboardDevice>&)>
46 KeyboardDeviceCallback;
47
48 typedef base::Callback<void(const std::vector<TouchscreenDevice>&)>
49 TouchscreenDeviceCallback;
50
51 // Used for updating the state on the UI thread once device information is
52 // parsed on helper threads.
53 struct UiCallbacks {
54 KeyboardDeviceCallback keyboard_callback;
55 TouchscreenDeviceCallback touchscreen_callback;
56 };
57
58 struct ValuatorClassInfo {
sadrul 2014/11/11 18:29:51 Add a comment explaining that this mirrors XIValua
dnicoara 2014/11/11 20:49:05 Done.
59 ValuatorClassInfo(const XIValuatorClassInfo& info)
60 : label(info.label),
61 max(info.max),
62 min(info.min),
63 mode(info.mode),
64 number(info.number) {}
65
66 Atom label;
67 double max;
68 double min;
69 int mode;
70 int number;
71 };
72
73 struct TouchClassInfo {
74 TouchClassInfo(const XITouchClassInfo& info)
75 : mode(info.mode) {}
76
77 int mode;
78 };
79
80 struct DeviceInfo {
81 DeviceInfo(const XIDeviceInfo& device, const base::FilePath& path)
82 : id(device.deviceid),
83 name(device.name),
84 use(device.use),
85 enabled(device.enabled),
86 path(path) {
87 for (int i = 0; i < device.num_classes; ++i) {
88 switch (device.classes[i]->type) {
89 case XIValuatorClass:
90 valuator_class_infos.push_back(ValuatorClassInfo(
91 *reinterpret_cast<XIValuatorClassInfo*>(device.classes[i])));
92 break;
93 #if defined(USE_XI2_MT)
94 case XITouchClass:
95 touch_class_infos.push_back(TouchClassInfo(
96 *reinterpret_cast<XITouchClassInfo*>(device.classes[i])));
97 break;
98 #endif
99 default:
100 break;
101 }
102 }
103 }
104
105 // Unique device identifier.
106 int id;
107
108 // Internal device name.
109 std::string name;
110
111 // Device type (ie: XIMasterPointer)
112 int use;
113
114 // Specifies if the device is enabled and can send events.
115 bool enabled;
116
117 // Path to the actual device (ie: /dev/input/eventXX)
118 base::FilePath path;
119
120 std::vector<ValuatorClassInfo> valuator_class_infos;
121
122 #if defined(USE_XI2_MT)
123 std::vector<TouchClassInfo> touch_class_infos;
124 #endif
125 };
126
127 // X11 display cache used on worker threads. This is filled on the UI thread and
128 // passed in to the worker threads.
129 struct DisplayState {
130 Atom mt_position_x;
131 Atom mt_position_y;
132 };
133
38 // Returns true if |name| is the name of a known keyboard device. Note, this may 134 // Returns true if |name| is the name of a known keyboard device. Note, this may
39 // return false negatives. 135 // return false negatives.
40 bool IsKnownKeyboard(const std::string& name) { 136 bool IsKnownKeyboard(const std::string& name) {
41 std::string lower = base::StringToLowerASCII(name); 137 std::string lower = base::StringToLowerASCII(name);
42 return lower.find("keyboard") != std::string::npos; 138 return lower.find("keyboard") != std::string::npos;
43 } 139 }
44 140
45 // Returns true if |name| is the name of a known internal keyboard device. Note, 141 // Returns true if |name| is the name of a known internal keyboard device. Note,
46 // this may return false negatives. 142 // this may return false negatives.
47 bool IsInternalKeyboard(const std::string& name) { 143 bool IsInternalKeyboard(const std::string& name) {
48 // TODO(rsadam@): Come up with a more generic way of identifying internal 144 // TODO(rsadam@): Come up with a more generic way of identifying internal
49 // keyboards. See crbug.com/420728. 145 // keyboards. See crbug.com/420728.
50 if (name == kATKeyboardName) 146 if (name == kATKeyboardName)
51 return true; 147 return true;
52 return name.compare( 148 return name.compare(
53 0u, strlen(kCrosEcKeyboardPrefix), kCrosEcKeyboardPrefix) == 0; 149 0u, strlen(kCrosEcKeyboardPrefix), kCrosEcKeyboardPrefix) == 0;
54 } 150 }
55 151
56 // Returns true if |name| is the name of a known XTEST device. Note, this may 152 // Returns true if |name| is the name of a known XTEST device. Note, this may
57 // return false negatives. 153 // return false negatives.
58 bool IsTestKeyboard(const std::string& name) { 154 bool IsTestKeyboard(const std::string& name) {
59 return name.find("XTEST") != std::string::npos; 155 return name.find("XTEST") != std::string::npos;
60 } 156 }
61 157
62 // We consider the touchscreen to be internal if it is an I2c device. 158 base::FilePath GetDevicePath(XDisplay* dpy, int device_id) {
63 // With the device id, we can query X to get the device's dev input 159 #if !defined(OS_CHROMEOS)
64 // node eventXXX. Then we search all the dev input nodes registered 160 return base::FilePath();
65 // by I2C devices to see if we can find eventXXX.
66 bool IsTouchscreenInternal(XDisplay* dpy, int device_id) {
67 #if !defined(CHROMEOS)
68 return false;
69 #else 161 #else
70 if (!base::SysInfo::IsRunningOnChromeOS()) 162 if (!base::SysInfo::IsRunningOnChromeOS())
71 return false; 163 return base::FilePath();
72 #endif 164 #endif
73 165
74 // Input device has a property "Device Node" pointing to its dev input node, 166 // Input device has a property "Device Node" pointing to its dev input node,
75 // e.g. Device Node (250): "/dev/input/event8" 167 // e.g. Device Node (250): "/dev/input/event8"
76 Atom device_node = XInternAtom(dpy, "Device Node", False); 168 Atom device_node = XInternAtom(dpy, "Device Node", False);
77 if (device_node == None) 169 if (device_node == None)
78 return false; 170 return base::FilePath();
79 171
80 Atom actual_type; 172 Atom actual_type;
81 int actual_format; 173 int actual_format;
82 unsigned long nitems, bytes_after; 174 unsigned long nitems, bytes_after;
83 unsigned char* data; 175 unsigned char* data;
84 XDevice* dev = XOpenDevice(dpy, device_id); 176 XDevice* dev = XOpenDevice(dpy, device_id);
85 if (!dev) 177 if (!dev)
86 return false; 178 return base::FilePath();
87 179
88 if (XGetDeviceProperty(dpy, 180 if (XGetDeviceProperty(dpy,
89 dev, 181 dev,
90 device_node, 182 device_node,
91 0, 183 0,
92 1000, 184 1000,
93 False, 185 False,
94 AnyPropertyType, 186 AnyPropertyType,
95 &actual_type, 187 &actual_type,
96 &actual_format, 188 &actual_format,
97 &nitems, 189 &nitems,
98 &bytes_after, 190 &bytes_after,
99 &data) != Success) { 191 &data) != Success) {
100 XCloseDevice(dpy, dev); 192 XCloseDevice(dpy, dev);
101 return false; 193 return base::FilePath();
102 } 194 }
103 base::FilePath dev_node_path(reinterpret_cast<char*>(data)); 195
196 std::string path;
197 // Make sure the returned value is a string.
198 if (actual_type == XA_STRING && actual_format == 8)
199 path = reinterpret_cast<char*>(data);
200
104 XFree(data); 201 XFree(data);
105 XCloseDevice(dpy, dev); 202 XCloseDevice(dpy, dev);
106 203
107 return ui::IsTouchscreenInternal(dev_node_path); 204 return base::FilePath(path);
108 } 205 }
109 206
110 } // namespace 207 // Helper used to parse keyboard information. When it is done it uses
111 208 // |reply_runner| and |callback| to update the state on the UI thread.
112 X11HotplugEventHandler::X11HotplugEventHandler( 209 void HandleKeyboardDevices(const std::vector<DeviceInfo>& device_infos,
113 DeviceHotplugEventObserver* delegate) 210 scoped_refptr<base::TaskRunner> reply_runner,
114 : delegate_(delegate) { 211 const KeyboardDeviceCallback& callback) {
115 }
116
117 X11HotplugEventHandler::~X11HotplugEventHandler() {
118 }
119
120 void X11HotplugEventHandler::OnHotplugEvent() {
121 const XIDeviceList& device_list =
122 DeviceListCacheX11::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay());
123 HandleTouchscreenDevices(device_list);
124 HandleKeyboardDevices(device_list);
125 }
126
127 void X11HotplugEventHandler::HandleKeyboardDevices(
128 const XIDeviceList& x11_devices) {
129 std::vector<KeyboardDevice> devices; 212 std::vector<KeyboardDevice> devices;
130 213
131 for (int i = 0; i < x11_devices.count; i++) { 214 for (auto device_info : device_infos) {
sadrul 2014/11/11 18:29:51 I think you want 'const auto&'?
dnicoara 2014/11/11 20:49:06 Done, you're right, I need to learn the details fo
132 if (!x11_devices[i].enabled || x11_devices[i].use != XISlaveKeyboard) 215 if (!device_info.enabled || device_info.use != XISlaveKeyboard)
133 continue; // Assume all keyboards are keyboard slaves 216 continue; // Assume all keyboards are keyboard slaves
134 std::string device_name(x11_devices[i].name); 217 std::string device_name(device_info.name);
135 base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name); 218 base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name);
136 if (IsTestKeyboard(device_name)) 219 if (IsTestKeyboard(device_name))
137 continue; // Skip test devices. 220 continue; // Skip test devices.
138 InputDeviceType type; 221 InputDeviceType type;
139 if (IsInternalKeyboard(device_name)) { 222 if (IsInternalKeyboard(device_name)) {
140 type = InputDeviceType::INPUT_DEVICE_INTERNAL; 223 type = InputDeviceType::INPUT_DEVICE_INTERNAL;
141 } else if (IsKnownKeyboard(device_name)) { 224 } else if (IsKnownKeyboard(device_name)) {
142 type = InputDeviceType::INPUT_DEVICE_EXTERNAL; 225 type = InputDeviceType::INPUT_DEVICE_EXTERNAL;
143 } else { 226 } else {
144 type = InputDeviceType::INPUT_DEVICE_UNKNOWN; 227 type = InputDeviceType::INPUT_DEVICE_UNKNOWN;
145 } 228 }
146 devices.push_back( 229 devices.push_back(
147 KeyboardDevice(x11_devices[i].deviceid, type, device_name)); 230 KeyboardDevice(device_info.id, type, device_name));
148 } 231 }
149 delegate_->OnKeyboardDevicesUpdated(devices); 232
233 reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
150 } 234 }
151 235
152 void X11HotplugEventHandler::HandleTouchscreenDevices( 236 // Helper used to parse touchscreen information. When it is done it uses
153 const XIDeviceList& x11_devices) { 237 // |reply_runner| and |callback| to update the state on the UI thread.
238 void HandleTouchscreenDevices(const std::vector<DeviceInfo>& device_infos,
239 const DisplayState& display_state,
240 scoped_refptr<base::TaskRunner> reply_runner,
241 const TouchscreenDeviceCallback& callback) {
154 std::vector<TouchscreenDevice> devices; 242 std::vector<TouchscreenDevice> devices;
155 Display* display = gfx::GetXDisplay(); 243 if (display_state.mt_position_x == None ||
156 Atom valuator_x = XInternAtom(display, "Abs MT Position X", False); 244 display_state.mt_position_y == None)
157 Atom valuator_y = XInternAtom(display, "Abs MT Position Y", False);
158 if (valuator_x == None || valuator_y == None)
159 return; 245 return;
160 246
161 std::set<int> no_match_touchscreen; 247 std::set<int> no_match_touchscreen;
162 for (int i = 0; i < x11_devices.count; i++) { 248 for (DeviceInfo device_info : device_infos) {
sadrul 2014/11/11 18:29:51 const & Use auto in both here and above, or in ne
dnicoara 2014/11/11 20:49:05 Done. I've used the actual type for clarity. I do
163 if (!x11_devices[i].enabled || x11_devices[i].use != XIFloatingSlave) 249 if (!device_info.enabled || device_info.use != XIFloatingSlave)
164 continue; // Assume all touchscreens are floating slaves 250 continue; // Assume all touchscreens are floating slaves
165 251
166 double max_x = -1.0; 252 double max_x = -1.0;
167 double max_y = -1.0; 253 double max_y = -1.0;
168 bool is_direct_touch = false; 254 bool is_direct_touch = false;
169 255
170 for (int j = 0; j < x11_devices[i].num_classes; j++) { 256 for (ValuatorClassInfo valuator : device_info.valuator_class_infos) {
171 XIAnyClassInfo* class_info = x11_devices[i].classes[j]; 257 if (display_state.mt_position_x == valuator.label) {
172 258 // Ignore X axis valuator with unexpected properties
173 if (class_info->type == XIValuatorClass) { 259 if (valuator.number == 0 && valuator.mode == Absolute &&
174 XIValuatorClassInfo* valuator_info = 260 valuator.min == 0.0) {
175 reinterpret_cast<XIValuatorClassInfo*>(class_info); 261 max_x = valuator.max;
176 262 }
177 if (valuator_x == valuator_info->label) { 263 } else if (display_state.mt_position_y == valuator.label) {
178 // Ignore X axis valuator with unexpected properties 264 // Ignore Y axis valuator with unexpected properties
179 if (valuator_info->number == 0 && valuator_info->mode == Absolute && 265 if (valuator.number == 1 && valuator.mode == Absolute &&
180 valuator_info->min == 0.0) { 266 valuator.min == 0.0) {
181 max_x = valuator_info->max; 267 max_y = valuator.max;
182 }
183 } else if (valuator_y == valuator_info->label) {
184 // Ignore Y axis valuator with unexpected properties
185 if (valuator_info->number == 1 && valuator_info->mode == Absolute &&
186 valuator_info->min == 0.0) {
187 max_y = valuator_info->max;
188 }
189 } 268 }
190 } 269 }
270 }
271
191 #if defined(USE_XI2_MT) 272 #if defined(USE_XI2_MT)
192 if (class_info->type == XITouchClass) { 273 for (TouchClassInfo info : device_info.touch_class_infos) {
193 XITouchClassInfo* touch_info = 274 is_direct_touch = info.mode == XIDirectTouch;
194 reinterpret_cast<XITouchClassInfo*>(class_info); 275 }
195 is_direct_touch = touch_info->mode == XIDirectTouch;
196 }
197 #endif 276 #endif
198 }
199 277
200 // Touchscreens should have absolute X and Y axes, and be direct touch 278 // Touchscreens should have absolute X and Y axes, and be direct touch
201 // devices. 279 // devices.
202 if (max_x > 0.0 && max_y > 0.0 && is_direct_touch) { 280 if (max_x > 0.0 && max_y > 0.0 && is_direct_touch) {
203 InputDeviceType type = 281 InputDeviceType type = IsTouchscreenInternal(device_info.path)
204 IsTouchscreenInternal(display, x11_devices[i].deviceid)
205 ? InputDeviceType::INPUT_DEVICE_INTERNAL 282 ? InputDeviceType::INPUT_DEVICE_INTERNAL
206 : InputDeviceType::INPUT_DEVICE_EXTERNAL; 283 : InputDeviceType::INPUT_DEVICE_EXTERNAL;
207 std::string name(x11_devices[i].name);
208 // |max_x| and |max_y| are inclusive values, so we need to add 1 to get 284 // |max_x| and |max_y| are inclusive values, so we need to add 1 to get
209 // the size. 285 // the size.
210 devices.push_back(TouchscreenDevice( 286 devices.push_back(TouchscreenDevice(
211 x11_devices[i].deviceid, 287 device_info.id,
212 type, 288 type,
213 name, 289 device_info.name,
214 gfx::Size(max_x + 1, max_y + 1))); 290 gfx::Size(max_x + 1, max_y + 1)));
215 } 291 }
216 } 292 }
217 293
218 delegate_->OnTouchscreenDevicesUpdated(devices); 294 reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices));
295 }
296
297 // Called on a worker thread to parse the device information.
298 void HandleHotplugEvent(const std::vector<DeviceInfo>& devices,
sadrul 2014/11/11 18:29:51 Do you mind adding 'InWorker' in the function name
dnicoara 2014/11/11 20:49:06 Done.
299 const DisplayState& display_state,
300 scoped_refptr<base::TaskRunner> reply_runner,
301 const UiCallbacks& callbacks) {
302 HandleTouchscreenDevices(
303 devices, display_state, reply_runner, callbacks.touchscreen_callback);
304 HandleKeyboardDevices(devices, reply_runner, callbacks.keyboard_callback);
305 }
306
307 DeviceHotplugEventObserver* GetHotplugEventObserver() {
308 return DeviceDataManager::GetInstance();
309 }
310
311 void OnKeyboardDevices(const std::vector<KeyboardDevice>& devices) {
312 GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices);
313 }
314
315 void OnTouchscreenDevices(const std::vector<TouchscreenDevice>& devices) {
316 GetHotplugEventObserver()->OnTouchscreenDevicesUpdated(devices);
317 }
318
319 } // namespace
320
321 X11HotplugEventHandler::X11HotplugEventHandler() {
322 }
323
324 X11HotplugEventHandler::~X11HotplugEventHandler() {
325 }
326
327 void X11HotplugEventHandler::OnHotplugEvent() {
328 const XIDeviceList& device_list =
329 DeviceListCacheX11::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay());
330 Display* display = gfx::GetXDisplay();
331
332 std::vector<DeviceInfo> device_infos;
333 for (int i = 0; i < device_list.count; ++i)
334 device_infos.push_back(DeviceInfo(
335 device_list[i], GetDevicePath(display, device_list[i].deviceid)));
336
337 // X11 is not thread safe, so first get all the required state.
338 DisplayState display_state;
339 display_state.mt_position_x =
340 XInternAtom(display, "Abs MT Position X", False);
341 display_state.mt_position_y =
342 XInternAtom(display, "Abs MT Position Y", False);
sadrul 2014/11/11 18:29:51 Consider using ui::X11AtomCache
dnicoara 2014/11/11 20:49:06 Done. Thanks for pointing that out.
343
344 UiCallbacks callbacks;
345 callbacks.keyboard_callback = base::Bind(&OnKeyboardDevices);
346 callbacks.touchscreen_callback = base::Bind(&OnTouchscreenDevices);
347
348 // Parsing the device information may block, so delegate the operation to a
349 // worker thread. Once the device information is extracted the parsed devices
350 // will be returned via the callbacks.
351 base::WorkerPool::PostTask(FROM_HERE,
352 base::Bind(&HandleHotplugEvent,
353 device_infos,
354 display_state,
355 base::ThreadTaskRunnerHandle::Get(),
356 callbacks),
357 true /* task_is_slow */);
219 } 358 }
220 359
221 } // namespace ui 360 } // namespace ui
OLDNEW
« no previous file with comments | « ui/events/platform/x11/x11_hotplug_event_handler.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698