summaryrefslogtreecommitdiffstats
path: root/chromium/device/hid/input_service_linux.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromium/device/hid/input_service_linux.cc')
-rw-r--r--chromium/device/hid/input_service_linux.cc240
1 files changed, 240 insertions, 0 deletions
diff --git a/chromium/device/hid/input_service_linux.cc b/chromium/device/hid/input_service_linux.cc
new file mode 100644
index 00000000000..90132e91af2
--- /dev/null
+++ b/chromium/device/hid/input_service_linux.cc
@@ -0,0 +1,240 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include <libudev.h>
+
+#include "base/bind.h"
+#include "base/lazy_instance.h"
+#include "base/logging.h"
+#include "base/strings/string_number_conversions.h"
+#include "base/strings/string_util.h"
+#include "base/threading/thread_restrictions.h"
+#include "device/hid/input_service_linux.h"
+
+namespace device {
+
+namespace {
+
+const char kSubsystemHid[] = "hid";
+const char kSubsystemInput[] = "input";
+const char kTypeBluetooth[] = "bluetooth";
+const char kTypeUsb[] = "usb";
+const char kTypeSerio[] = "serio";
+const char kIdInputAccelerometer[] = "ID_INPUT_ACCELEROMETER";
+const char kIdInputJoystick[] = "ID_INPUT_JOYSTICK";
+const char kIdInputKey[] = "ID_INPUT_KEY";
+const char kIdInputKeyboard[] = "ID_INPUT_KEYBOARD";
+const char kIdInputMouse[] = "ID_INPUT_MOUSE";
+const char kIdInputTablet[] = "ID_INPUT_TABLET";
+const char kIdInputTouchpad[] = "ID_INPUT_TOUCHPAD";
+const char kIdInputTouchscreen[] = "ID_INPUT_TOUCHSCREEN";
+
+// The instance will be reset when message loop destroys.
+base::LazyInstance<scoped_ptr<InputServiceLinux> >::Leaky
+ g_input_service_linux_ptr = LAZY_INSTANCE_INITIALIZER;
+
+bool GetBoolProperty(udev_device* device, const char* key) {
+ CHECK(device);
+ CHECK(key);
+ const char* property = udev_device_get_property_value(device, key);
+ if (!property)
+ return false;
+ int value;
+ if (!base::StringToInt(property, &value)) {
+ LOG(ERROR) << "Not an integer value for " << key << " property";
+ return false;
+ }
+ return (value != 0);
+}
+
+InputServiceLinux::InputDeviceInfo::Type GetDeviceType(udev_device* device) {
+ if (udev_device_get_parent_with_subsystem_devtype(
+ device, kTypeBluetooth, NULL)) {
+ return InputServiceLinux::InputDeviceInfo::TYPE_BLUETOOTH;
+ }
+ if (udev_device_get_parent_with_subsystem_devtype(device, kTypeUsb, NULL))
+ return InputServiceLinux::InputDeviceInfo::TYPE_USB;
+ if (udev_device_get_parent_with_subsystem_devtype(device, kTypeSerio, NULL))
+ return InputServiceLinux::InputDeviceInfo::TYPE_SERIO;
+ return InputServiceLinux::InputDeviceInfo::TYPE_UNKNOWN;
+}
+
+std::string GetParentDeviceName(udev_device* device, const char* subsystem) {
+ udev_device* parent =
+ udev_device_get_parent_with_subsystem_devtype(device, subsystem, NULL);
+ if (!parent)
+ return std::string();
+ const char* name = udev_device_get_property_value(parent, "NAME");
+ if (!name)
+ return std::string();
+ std::string result;
+ base::TrimString(name, "\"", &result);
+ return result;
+}
+
+class InputServiceLinuxImpl : public InputServiceLinux,
+ public DeviceMonitorLinux::Observer {
+ public:
+ // Implements DeviceMonitorLinux::Observer:
+ virtual void OnDeviceAdded(udev_device* device) OVERRIDE;
+ virtual void OnDeviceRemoved(udev_device* device) OVERRIDE;
+
+ private:
+ friend class InputServiceLinux;
+
+ InputServiceLinuxImpl();
+ virtual ~InputServiceLinuxImpl();
+
+ DISALLOW_COPY_AND_ASSIGN(InputServiceLinuxImpl);
+};
+
+InputServiceLinuxImpl::InputServiceLinuxImpl() {
+ DeviceMonitorLinux::GetInstance()->AddObserver(this);
+ DeviceMonitorLinux::GetInstance()->Enumerate(base::Bind(
+ &InputServiceLinuxImpl::OnDeviceAdded, base::Unretained(this)));
+}
+
+InputServiceLinuxImpl::~InputServiceLinuxImpl() {
+ if (DeviceMonitorLinux::HasInstance())
+ DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
+}
+
+void InputServiceLinuxImpl::OnDeviceAdded(udev_device* device) {
+ DCHECK(CalledOnValidThread());
+ if (!device)
+ return;
+ const char* devnode = udev_device_get_devnode(device);
+ if (!devnode)
+ return;
+
+ InputDeviceInfo info;
+ info.id = devnode;
+
+ const char* subsystem = udev_device_get_subsystem(device);
+ if (!subsystem)
+ return;
+ if (strcmp(subsystem, kSubsystemHid) == 0) {
+ info.subsystem = InputServiceLinux::InputDeviceInfo::SUBSYSTEM_HID;
+ info.name = GetParentDeviceName(device, kSubsystemHid);
+ } else if (strcmp(subsystem, kSubsystemInput) == 0) {
+ info.subsystem = InputServiceLinux::InputDeviceInfo::SUBSYSTEM_INPUT;
+ info.name = GetParentDeviceName(device, kSubsystemInput);
+ } else {
+ return;
+ }
+
+ info.type = GetDeviceType(device);
+
+ info.is_accelerometer = GetBoolProperty(device, kIdInputAccelerometer);
+ info.is_joystick = GetBoolProperty(device, kIdInputJoystick);
+ info.is_key = GetBoolProperty(device, kIdInputKey);
+ info.is_keyboard = GetBoolProperty(device, kIdInputKeyboard);
+ info.is_mouse = GetBoolProperty(device, kIdInputMouse);
+ info.is_tablet = GetBoolProperty(device, kIdInputTablet);
+ info.is_touchpad = GetBoolProperty(device, kIdInputTouchpad);
+ info.is_touchscreen = GetBoolProperty(device, kIdInputTouchscreen);
+
+ AddDevice(info);
+}
+
+void InputServiceLinuxImpl::OnDeviceRemoved(udev_device* device) {
+ DCHECK(CalledOnValidThread());
+ if (!device)
+ return;
+ const char* devnode = udev_device_get_devnode(device);
+ if (devnode)
+ RemoveDevice(devnode);
+}
+
+} // namespace
+
+InputServiceLinux::InputDeviceInfo::InputDeviceInfo()
+ : subsystem(SUBSYSTEM_UNKNOWN),
+ type(TYPE_UNKNOWN),
+ is_accelerometer(false),
+ is_joystick(false),
+ is_key(false),
+ is_keyboard(false),
+ is_mouse(false),
+ is_tablet(false),
+ is_touchpad(false),
+ is_touchscreen(false) {}
+
+InputServiceLinux::InputServiceLinux() {
+ base::ThreadRestrictions::AssertIOAllowed();
+ base::MessageLoop::current()->AddDestructionObserver(this);
+}
+
+InputServiceLinux::~InputServiceLinux() {
+ DCHECK(CalledOnValidThread());
+ base::MessageLoop::current()->RemoveDestructionObserver(this);
+}
+
+// static
+InputServiceLinux* InputServiceLinux::GetInstance() {
+ if (!HasInstance())
+ g_input_service_linux_ptr.Get().reset(new InputServiceLinuxImpl());
+ return g_input_service_linux_ptr.Get().get();
+}
+
+// static
+bool InputServiceLinux::HasInstance() {
+ return g_input_service_linux_ptr.Get().get();
+}
+
+// static
+void InputServiceLinux::SetForTesting(InputServiceLinux* service) {
+ g_input_service_linux_ptr.Get().reset(service);
+}
+
+void InputServiceLinux::AddObserver(Observer* observer) {
+ DCHECK(CalledOnValidThread());
+ if (observer)
+ observers_.AddObserver(observer);
+}
+
+void InputServiceLinux::RemoveObserver(Observer* observer) {
+ DCHECK(CalledOnValidThread());
+ if (observer)
+ observers_.RemoveObserver(observer);
+}
+
+void InputServiceLinux::GetDevices(std::vector<InputDeviceInfo>* devices) {
+ DCHECK(CalledOnValidThread());
+ for (DeviceMap::iterator it = devices_.begin(), ie = devices_.end(); it != ie;
+ ++it) {
+ devices->push_back(it->second);
+ }
+}
+
+bool InputServiceLinux::GetDeviceInfo(const std::string& id,
+ InputDeviceInfo* info) const {
+ DCHECK(CalledOnValidThread());
+ DeviceMap::const_iterator it = devices_.find(id);
+ if (it == devices_.end())
+ return false;
+ *info = it->second;
+ return true;
+}
+
+void InputServiceLinux::WillDestroyCurrentMessageLoop() {
+ DCHECK(CalledOnValidThread());
+ g_input_service_linux_ptr.Get().reset(NULL);
+}
+
+void InputServiceLinux::AddDevice(const InputDeviceInfo& info) {
+ devices_[info.id] = info;
+ FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceAdded(info));
+}
+
+void InputServiceLinux::RemoveDevice(const std::string& id) {
+ devices_.erase(id);
+ FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceRemoved(id));
+}
+
+bool InputServiceLinux::CalledOnValidThread() const {
+ return thread_checker_.CalledOnValidThread();
+}
+
+} // namespace device