diff options
author | Allan Sandfeld Jensen <allan.jensen@theqtcompany.com> | 2015-06-18 14:10:49 +0200 |
---|---|---|
committer | Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com> | 2015-06-18 13:53:24 +0000 |
commit | 813fbf95af77a531c57a8c497345ad2c61d475b3 (patch) | |
tree | 821b2c8de8365f21b6c9ba17a236fb3006a1d506 /chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc | |
parent | af6588f8d723931a298c995fa97259bb7f7deb55 (diff) | |
download | qtwebengine-chromium-813fbf95af77a531c57a8c497345ad2c61d475b3.tar.gz |
BASELINE: Update chromium to 44.0.2403.47
Change-Id: Ie056fedba95cf5e5c76b30c4b2c80fca4764aa2f
Reviewed-by: Oswald Buddenhagen <oswald.buddenhagen@theqtcompany.com>
Diffstat (limited to 'chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc')
-rw-r--r-- | chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc | 514 |
1 files changed, 394 insertions, 120 deletions
diff --git a/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc b/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc index 3ce944efc3e..20428977e5d 100644 --- a/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc +++ b/chromium/ui/events/platform/x11/x11_hotplug_event_handler.cc @@ -4,6 +4,7 @@ #include "ui/events/platform/x11/x11_hotplug_event_handler.h" +#include <X11/Xatom.h> #include <X11/extensions/XInput.h> #include <X11/extensions/XInput2.h> @@ -13,11 +14,17 @@ #include <string> #include <vector> +#include "base/bind.h" #include "base/command_line.h" +#include "base/location.h" #include "base/logging.h" #include "base/process/launch.h" +#include "base/single_thread_task_runner.h" #include "base/strings/string_util.h" #include "base/sys_info.h" +#include "base/thread_task_runner_handle.h" +#include "base/threading/worker_pool.h" +#include "ui/events/devices/device_data_manager.h" #include "ui/events/devices/device_hotplug_event_observer.h" #include "ui/events/devices/device_util_linux.h" #include "ui/events/devices/input_device.h" @@ -25,65 +32,190 @@ #include "ui/events/devices/touchscreen_device.h" #include "ui/gfx/x/x11_types.h" +#ifndef XI_PROP_PRODUCT_ID +#define XI_PROP_PRODUCT_ID "Device Product ID" +#endif + namespace ui { namespace { -// The name of the xinput device corresponding to the AT internal keyboard. -const char kATKeyboardName[] = "AT Translated Set 2 keyboard"; +// Names of all known internal devices that should not be considered as +// keyboards. +// TODO(rsadam@): Identify these devices using udev rules. (Crbug.com/420728.) +const char* kKnownInvalidKeyboardDeviceNames[] = {"Power Button", + "Sleep Button", + "Video Bus", + "gpio-keys.5", + "gpio-keys.12", + "ROCKCHIP-I2S Headset Jack"}; -// The prefix of xinput devices corresponding to CrOS EC internal keyboards. -const char kCrosEcKeyboardPrefix[] = "cros-ec"; +const char* kCachedAtomList[] = { + "Abs MT Position X", + "Abs MT Position Y", + XI_KEYBOARD, + XI_MOUSE, + XI_TOUCHPAD, + XI_TOUCHSCREEN, + XI_PROP_PRODUCT_ID, + NULL, +}; -// Returns true if |name| is the name of a known keyboard device. Note, this may -// return false negatives. -bool IsKnownKeyboard(const std::string& name) { - std::string lower = base::StringToLowerASCII(name); - return lower.find("keyboard") != std::string::npos; -} +enum DeviceType { + DEVICE_TYPE_KEYBOARD, + DEVICE_TYPE_MOUSE, + DEVICE_TYPE_TOUCHPAD, + DEVICE_TYPE_TOUCHSCREEN, + DEVICE_TYPE_OTHER +}; + +typedef base::Callback<void(const std::vector<KeyboardDevice>&)> + KeyboardDeviceCallback; + +typedef base::Callback<void(const std::vector<TouchscreenDevice>&)> + TouchscreenDeviceCallback; + +typedef base::Callback<void(const std::vector<InputDevice>&)> + InputDeviceCallback; + +// Used for updating the state on the UI thread once device information is +// parsed on helper threads. +struct UiCallbacks { + KeyboardDeviceCallback keyboard_callback; + TouchscreenDeviceCallback touchscreen_callback; + InputDeviceCallback mouse_callback; + InputDeviceCallback touchpad_callback; +}; + +// Stores a copy of the XIValuatorClassInfo values so X11 device processing can +// happen on a worker thread. This is needed since X11 structs are not copyable. +struct ValuatorClassInfo { + ValuatorClassInfo(const XIValuatorClassInfo& info) + : label(info.label), + max(info.max), + min(info.min), + mode(info.mode), + number(info.number) {} + + Atom label; + double max; + double min; + int mode; + int number; +}; + +// Stores a copy of the XITouchClassInfo values so X11 device processing can +// happen on a worker thread. This is needed since X11 structs are not copyable. +struct TouchClassInfo { + TouchClassInfo() : mode(0), num_touches(0) {} + + explicit TouchClassInfo(const XITouchClassInfo& info) + : mode(info.mode), num_touches(info.num_touches) {} + + int mode; + int num_touches; +}; + +struct DeviceInfo { + DeviceInfo(const XIDeviceInfo& device, + DeviceType type, + const base::FilePath& path, + uint16_t vendor, + uint16_t product) + : id(device.deviceid), + name(device.name), + vendor_id(vendor), + product_id(product), + use(device.use), + type(type), + path(path) { + for (int i = 0; i < device.num_classes; ++i) { + switch (device.classes[i]->type) { + case XIValuatorClass: + valuator_class_infos.push_back(ValuatorClassInfo( + *reinterpret_cast<XIValuatorClassInfo*>(device.classes[i]))); + break; + case XITouchClass: + // A device can have at most one XITouchClassInfo. Ref: + // http://manpages.ubuntu.com/manpages/saucy/man3/XIQueryDevice.3.html + DCHECK(!touch_class_info.mode); + touch_class_info = TouchClassInfo( + *reinterpret_cast<XITouchClassInfo*>(device.classes[i])); + break; + default: + break; + } + } + } + + // Unique device identifier. + int id; + + // Internal device name. + std::string name; + + // USB-style device identifiers. + uint16_t vendor_id; + uint16_t product_id; + + // Device type (ie: XIMasterPointer) + int use; + + // Specifies the type of the device. + DeviceType type; + + // Path to the actual device (ie: /dev/input/eventXX) + base::FilePath path; -// Returns true if |name| is the name of a known internal keyboard device. Note, + std::vector<ValuatorClassInfo> valuator_class_infos; + + TouchClassInfo touch_class_info; +}; + +// X11 display cache used on worker threads. This is filled on the UI thread and +// passed in to the worker threads. +struct DisplayState { + Atom mt_position_x; + Atom mt_position_y; +}; + +// Returns true if |name| is the name of a known invalid keyboard device. Note, // this may return false negatives. -bool IsInternalKeyboard(const std::string& name) { - // TODO(rsadam@): Come up with a more generic way of identifying internal - // keyboards. See crbug.com/420728. - if (name == kATKeyboardName) - return true; - return name.compare( - 0u, strlen(kCrosEcKeyboardPrefix), kCrosEcKeyboardPrefix) == 0; +bool IsKnownInvalidKeyboardDevice(const std::string& name) { + std::string trimmed(name); + base::TrimWhitespaceASCII(name, base::TRIM_TRAILING, &trimmed); + for (const char* device_name : kKnownInvalidKeyboardDeviceNames) { + if (trimmed == device_name) + return true; + } + return false; } // Returns true if |name| is the name of a known XTEST device. Note, this may // return false negatives. -bool IsTestKeyboard(const std::string& name) { +bool IsTestDevice(const std::string& name) { return name.find("XTEST") != std::string::npos; } -// We consider the touchscreen to be internal if it is an I2c device. -// With the device id, we can query X to get the device's dev input -// node eventXXX. Then we search all the dev input nodes registered -// by I2C devices to see if we can find eventXXX. -bool IsTouchscreenInternal(XDisplay* dpy, int device_id) { -#if !defined(CHROMEOS) - return false; -#else - if (!base::SysInfo::IsRunningOnChromeOS()) - return false; -#endif +base::FilePath GetDevicePath(XDisplay* dpy, const XIDeviceInfo& device) { + // Skip the main pointer and keyboard since XOpenDevice() generates a + // BadDevice error when passed these devices. + if (device.use == XIMasterPointer || device.use == XIMasterKeyboard) + return base::FilePath(); // Input device has a property "Device Node" pointing to its dev input node, // e.g. Device Node (250): "/dev/input/event8" Atom device_node = XInternAtom(dpy, "Device Node", False); if (device_node == None) - return false; + return base::FilePath(); Atom actual_type; int actual_format; unsigned long nitems, bytes_after; unsigned char* data; - XDevice* dev = XOpenDevice(dpy, device_id); + XDevice* dev = XOpenDevice(dpy, device.deviceid); if (!dev) - return false; + return base::FilePath(); if (XGetDeviceProperty(dpy, dev, @@ -98,124 +230,266 @@ bool IsTouchscreenInternal(XDisplay* dpy, int device_id) { &bytes_after, &data) != Success) { XCloseDevice(dpy, dev); - return false; + return base::FilePath(); } - base::FilePath dev_node_path(reinterpret_cast<char*>(data)); + + std::string path; + // Make sure the returned value is a string. + if (actual_type == XA_STRING && actual_format == 8) + path = reinterpret_cast<char*>(data); + XFree(data); XCloseDevice(dpy, dev); - return ui::IsTouchscreenInternal(dev_node_path); + return base::FilePath(path); } -} // namespace +// Helper used to parse keyboard information. When it is done it uses +// |reply_runner| and |callback| to update the state on the UI thread. +void HandleKeyboardDevicesInWorker( + const std::vector<DeviceInfo>& device_infos, + scoped_refptr<base::TaskRunner> reply_runner, + const KeyboardDeviceCallback& callback) { + std::vector<KeyboardDevice> devices; -X11HotplugEventHandler::X11HotplugEventHandler( - DeviceHotplugEventObserver* delegate) - : delegate_(delegate) { -} + for (const DeviceInfo& device_info : device_infos) { + if (device_info.type != DEVICE_TYPE_KEYBOARD) + continue; + if (device_info.use != XISlaveKeyboard) + continue; // Assume all keyboards are keyboard slaves + if (IsKnownInvalidKeyboardDevice(device_info.name)) + continue; // Skip invalid devices. + InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path); + KeyboardDevice keyboard(device_info.id, type, device_info.name); + devices.push_back(keyboard); + } -X11HotplugEventHandler::~X11HotplugEventHandler() { + reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices)); } -void X11HotplugEventHandler::OnHotplugEvent() { - const XIDeviceList& device_list = - DeviceListCacheX11::GetInstance()->GetXI2DeviceList(gfx::GetXDisplay()); - HandleTouchscreenDevices(device_list); - HandleKeyboardDevices(device_list); -} +// Helper used to parse mouse information. When it is done it uses +// |reply_runner| and |callback| to update the state on the UI thread. +void HandleMouseDevicesInWorker(const std::vector<DeviceInfo>& device_infos, + scoped_refptr<base::TaskRunner> reply_runner, + const InputDeviceCallback& callback) { + std::vector<InputDevice> devices; + for (const DeviceInfo& device_info : device_infos) { + if (device_info.type != DEVICE_TYPE_MOUSE || + device_info.use != XISlavePointer) { + continue; + } -void X11HotplugEventHandler::HandleKeyboardDevices( - const XIDeviceList& x11_devices) { - std::vector<KeyboardDevice> devices; + InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path); + devices.push_back(InputDevice(device_info.id, type, device_info.name)); + } - for (int i = 0; i < x11_devices.count; i++) { - if (!x11_devices[i].enabled || x11_devices[i].use != XISlaveKeyboard) - continue; // Assume all keyboards are keyboard slaves - std::string device_name(x11_devices[i].name); - base::TrimWhitespaceASCII(device_name, base::TRIM_TRAILING, &device_name); - if (IsTestKeyboard(device_name)) - continue; // Skip test devices. - InputDeviceType type; - if (IsInternalKeyboard(device_name)) { - type = InputDeviceType::INPUT_DEVICE_INTERNAL; - } else if (IsKnownKeyboard(device_name)) { - type = InputDeviceType::INPUT_DEVICE_EXTERNAL; - } else { - type = InputDeviceType::INPUT_DEVICE_UNKNOWN; + reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices)); +} + +// Helper used to parse touchpad information. When it is done it uses +// |reply_runner| and |callback| to update the state on the UI thread. +void HandleTouchpadDevicesInWorker(const std::vector<DeviceInfo>& device_infos, + scoped_refptr<base::TaskRunner> reply_runner, + const InputDeviceCallback& callback) { + std::vector<InputDevice> devices; + for (const DeviceInfo& device_info : device_infos) { + if (device_info.type != DEVICE_TYPE_TOUCHPAD || + device_info.use != XISlavePointer) { + continue; } - devices.push_back( - KeyboardDevice(x11_devices[i].deviceid, type, device_name)); + + InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path); + devices.push_back(InputDevice(device_info.id, type, device_info.name)); } - delegate_->OnKeyboardDevicesUpdated(devices); + + reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices)); } -void X11HotplugEventHandler::HandleTouchscreenDevices( - const XIDeviceList& x11_devices) { +// Helper used to parse touchscreen information. When it is done it uses +// |reply_runner| and |callback| to update the state on the UI thread. +void HandleTouchscreenDevicesInWorker( + const std::vector<DeviceInfo>& device_infos, + const DisplayState& display_state, + scoped_refptr<base::TaskRunner> reply_runner, + const TouchscreenDeviceCallback& callback) { std::vector<TouchscreenDevice> devices; - Display* display = gfx::GetXDisplay(); - Atom valuator_x = XInternAtom(display, "Abs MT Position X", False); - Atom valuator_y = XInternAtom(display, "Abs MT Position Y", False); - if (valuator_x == None || valuator_y == None) + if (display_state.mt_position_x == None || + display_state.mt_position_y == None) return; - std::set<int> no_match_touchscreen; - for (int i = 0; i < x11_devices.count; i++) { - if (!x11_devices[i].enabled || x11_devices[i].use != XIFloatingSlave) - continue; // Assume all touchscreens are floating slaves + for (const DeviceInfo& device_info : device_infos) { + if (device_info.type != DEVICE_TYPE_TOUCHSCREEN || + (device_info.use != XIFloatingSlave && + device_info.use != XISlavePointer)) { + continue; + } + + // Touchscreens should be direct touch devices. + if (device_info.touch_class_info.mode != XIDirectTouch) + continue; double max_x = -1.0; double max_y = -1.0; - bool is_direct_touch = false; - - for (int j = 0; j < x11_devices[i].num_classes; j++) { - XIAnyClassInfo* class_info = x11_devices[i].classes[j]; - - if (class_info->type == XIValuatorClass) { - XIValuatorClassInfo* valuator_info = - reinterpret_cast<XIValuatorClassInfo*>(class_info); - - if (valuator_x == valuator_info->label) { - // Ignore X axis valuator with unexpected properties - if (valuator_info->number == 0 && valuator_info->mode == Absolute && - valuator_info->min == 0.0) { - max_x = valuator_info->max; - } - } else if (valuator_y == valuator_info->label) { - // Ignore Y axis valuator with unexpected properties - if (valuator_info->number == 1 && valuator_info->mode == Absolute && - valuator_info->min == 0.0) { - max_y = valuator_info->max; - } + + for (const ValuatorClassInfo& valuator : device_info.valuator_class_infos) { + if (display_state.mt_position_x == valuator.label) { + // Ignore X axis valuator with unexpected properties + if (valuator.number == 0 && valuator.mode == Absolute && + valuator.min == 0.0) { + max_x = valuator.max; + } + } else if (display_state.mt_position_y == valuator.label) { + // Ignore Y axis valuator with unexpected properties + if (valuator.number == 1 && valuator.mode == Absolute && + valuator.min == 0.0) { + max_y = valuator.max; } } -#if defined(USE_XI2_MT) - if (class_info->type == XITouchClass) { - XITouchClassInfo* touch_info = - reinterpret_cast<XITouchClassInfo*>(class_info); - is_direct_touch = touch_info->mode == XIDirectTouch; - } -#endif } - // Touchscreens should have absolute X and Y axes, and be direct touch - // devices. - if (max_x > 0.0 && max_y > 0.0 && is_direct_touch) { - InputDeviceType type = - IsTouchscreenInternal(display, x11_devices[i].deviceid) - ? InputDeviceType::INPUT_DEVICE_INTERNAL - : InputDeviceType::INPUT_DEVICE_EXTERNAL; - std::string name(x11_devices[i].name); + // Touchscreens should have absolute X and Y axes. + if (max_x > 0.0 && max_y > 0.0) { + InputDeviceType type = GetInputDeviceTypeFromPath(device_info.path); // |max_x| and |max_y| are inclusive values, so we need to add 1 to get // the size. - devices.push_back(TouchscreenDevice( - x11_devices[i].deviceid, - type, - name, - gfx::Size(max_x + 1, max_y + 1))); + devices.push_back( + TouchscreenDevice(device_info.id, type, device_info.name, + gfx::Size(max_x + 1, max_y + 1), + device_info.touch_class_info.num_touches)); + } + } + + reply_runner->PostTask(FROM_HERE, base::Bind(callback, devices)); +} + +// Called on a worker thread to parse the device information. +void HandleHotplugEventInWorker( + const std::vector<DeviceInfo>& devices, + const DisplayState& display_state, + scoped_refptr<base::TaskRunner> reply_runner, + const UiCallbacks& callbacks) { + HandleTouchscreenDevicesInWorker( + devices, display_state, reply_runner, callbacks.touchscreen_callback); + HandleKeyboardDevicesInWorker( + devices, reply_runner, callbacks.keyboard_callback); + HandleMouseDevicesInWorker(devices, reply_runner, callbacks.mouse_callback); + HandleTouchpadDevicesInWorker(devices, reply_runner, + callbacks.touchpad_callback); +} + +DeviceHotplugEventObserver* GetHotplugEventObserver() { + return DeviceDataManager::GetInstance(); +} + +void OnKeyboardDevices(const std::vector<KeyboardDevice>& devices) { + GetHotplugEventObserver()->OnKeyboardDevicesUpdated(devices); +} + +void OnTouchscreenDevices(const std::vector<TouchscreenDevice>& devices) { + GetHotplugEventObserver()->OnTouchscreenDevicesUpdated(devices); +} + +void OnMouseDevices(const std::vector<InputDevice>& devices) { + GetHotplugEventObserver()->OnMouseDevicesUpdated(devices); +} + +void OnTouchpadDevices(const std::vector<InputDevice>& devices) { + GetHotplugEventObserver()->OnTouchpadDevicesUpdated(devices); +} + +} // namespace + +X11HotplugEventHandler::X11HotplugEventHandler() + : atom_cache_(gfx::GetXDisplay(), kCachedAtomList) { +} + +X11HotplugEventHandler::~X11HotplugEventHandler() { +} + +void X11HotplugEventHandler::OnHotplugEvent() { + Display* display = gfx::GetXDisplay(); + const XDeviceList& device_list_xi = + DeviceListCacheX11::GetInstance()->GetXDeviceList(display); + const XIDeviceList& device_list_xi2 = + DeviceListCacheX11::GetInstance()->GetXI2DeviceList(display); + + const int kMaxDeviceNum = 128; + DeviceType device_types[kMaxDeviceNum]; + for (int i = 0; i < kMaxDeviceNum; ++i) + device_types[i] = DEVICE_TYPE_OTHER; + + for (int i = 0; i < device_list_xi.count; ++i) { + int id = device_list_xi[i].id; + if (id < 0 || id >= kMaxDeviceNum) + continue; + + Atom type = device_list_xi[i].type; + if (type == atom_cache_.GetAtom(XI_KEYBOARD)) + device_types[id] = DEVICE_TYPE_KEYBOARD; + else if (type == atom_cache_.GetAtom(XI_MOUSE)) + device_types[id] = DEVICE_TYPE_MOUSE; + else if (type == atom_cache_.GetAtom(XI_TOUCHPAD)) + device_types[id] = DEVICE_TYPE_TOUCHPAD; + else if (type == atom_cache_.GetAtom(XI_TOUCHSCREEN)) + device_types[id] = DEVICE_TYPE_TOUCHSCREEN; + } + + std::vector<DeviceInfo> device_infos; + for (int i = 0; i < device_list_xi2.count; ++i) { + const XIDeviceInfo& device = device_list_xi2[i]; + if (!device.enabled || IsTestDevice(device.name)) + continue; + + DeviceType device_type = + (device.deviceid >= 0 && device.deviceid < kMaxDeviceNum) + ? device_types[device.deviceid] + : DEVICE_TYPE_OTHER; + + // Obtain the USB-style vendor and product identifiers. + // (On Linux, XI2 makes this available for all evdev devices. + uint32_t* product_info; + Atom type; + int format_return; + unsigned long num_items_return; + unsigned long bytes_after_return; + uint16_t vendor = 0; + uint16_t product = 0; + if (XIGetProperty(gfx::GetXDisplay(), device.deviceid, + atom_cache_.GetAtom(XI_PROP_PRODUCT_ID), 0, 2, 0, + XA_INTEGER, &type, &format_return, &num_items_return, + &bytes_after_return, + reinterpret_cast<unsigned char**>(&product_info)) == 0 && + product_info) { + if (num_items_return == 2) { + vendor = product_info[0]; + product = product_info[1]; + } + XFree(product_info); } + + device_infos.push_back(DeviceInfo( + device, device_type, GetDevicePath(display, device), vendor, product)); } - delegate_->OnTouchscreenDevicesUpdated(devices); + // X11 is not thread safe, so first get all the required state. + DisplayState display_state; + display_state.mt_position_x = atom_cache_.GetAtom("Abs MT Position X"); + display_state.mt_position_y = atom_cache_.GetAtom("Abs MT Position Y"); + + UiCallbacks callbacks; + callbacks.keyboard_callback = base::Bind(&OnKeyboardDevices); + callbacks.touchscreen_callback = base::Bind(&OnTouchscreenDevices); + callbacks.mouse_callback = base::Bind(&OnMouseDevices); + callbacks.touchpad_callback = base::Bind(&OnTouchpadDevices); + + // Parsing the device information may block, so delegate the operation to a + // worker thread. Once the device information is extracted the parsed devices + // will be returned via the callbacks. + base::WorkerPool::PostTask( + FROM_HERE, + base::Bind(&HandleHotplugEventInWorker, device_infos, display_state, + base::ThreadTaskRunnerHandle::Get(), callbacks), + true /* task_is_slow */); } } // namespace ui |