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
3 // found in the LICENSE file.
5 #include "device/hid/input_service_linux.h"
8 #include "base/lazy_instance.h"
9 #include "base/logging.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "base/strings/string_util.h"
12 #include "base/threading/thread_restrictions.h"
13 #include "device/udev_linux/udev.h"
19 const char kSubsystemHid
[] = "hid";
20 const char kSubsystemInput
[] = "input";
21 const char kTypeBluetooth
[] = "bluetooth";
22 const char kTypeUsb
[] = "usb";
23 const char kTypeSerio
[] = "serio";
24 const char kIdInputAccelerometer
[] = "ID_INPUT_ACCELEROMETER";
25 const char kIdInputJoystick
[] = "ID_INPUT_JOYSTICK";
26 const char kIdInputKey
[] = "ID_INPUT_KEY";
27 const char kIdInputKeyboard
[] = "ID_INPUT_KEYBOARD";
28 const char kIdInputMouse
[] = "ID_INPUT_MOUSE";
29 const char kIdInputTablet
[] = "ID_INPUT_TABLET";
30 const char kIdInputTouchpad
[] = "ID_INPUT_TOUCHPAD";
31 const char kIdInputTouchscreen
[] = "ID_INPUT_TOUCHSCREEN";
33 // The instance will be reset when message loop destroys.
34 base::LazyInstance
<scoped_ptr
<InputServiceLinux
> >::Leaky
35 g_input_service_linux_ptr
= LAZY_INSTANCE_INITIALIZER
;
37 bool GetBoolProperty(udev_device
* device
, const char* key
) {
40 const char* property
= udev_device_get_property_value(device
, key
);
44 if (!base::StringToInt(property
, &value
)) {
45 LOG(ERROR
) << "Not an integer value for " << key
<< " property";
51 InputServiceLinux::InputDeviceInfo::Type
GetDeviceType(udev_device
* device
) {
52 if (udev_device_get_parent_with_subsystem_devtype(
53 device
, kTypeBluetooth
, NULL
)) {
54 return InputServiceLinux::InputDeviceInfo::TYPE_BLUETOOTH
;
56 if (udev_device_get_parent_with_subsystem_devtype(device
, kTypeUsb
, NULL
))
57 return InputServiceLinux::InputDeviceInfo::TYPE_USB
;
58 if (udev_device_get_parent_with_subsystem_devtype(device
, kTypeSerio
, NULL
))
59 return InputServiceLinux::InputDeviceInfo::TYPE_SERIO
;
60 return InputServiceLinux::InputDeviceInfo::TYPE_UNKNOWN
;
63 std::string
GetParentDeviceName(udev_device
* device
, const char* subsystem
) {
65 udev_device_get_parent_with_subsystem_devtype(device
, subsystem
, NULL
);
68 const char* name
= udev_device_get_property_value(parent
, "NAME");
72 base::TrimString(name
, "\"", &result
);
76 class InputServiceLinuxImpl
: public InputServiceLinux
,
77 public DeviceMonitorLinux::Observer
{
79 // Implements DeviceMonitorLinux::Observer:
80 void OnDeviceAdded(udev_device
* device
) override
;
81 void OnDeviceRemoved(udev_device
* device
) override
;
84 friend class InputServiceLinux
;
86 InputServiceLinuxImpl();
87 ~InputServiceLinuxImpl() override
;
89 DISALLOW_COPY_AND_ASSIGN(InputServiceLinuxImpl
);
92 InputServiceLinuxImpl::InputServiceLinuxImpl() {
93 DeviceMonitorLinux::GetInstance()->AddObserver(this);
94 DeviceMonitorLinux::GetInstance()->Enumerate(base::Bind(
95 &InputServiceLinuxImpl::OnDeviceAdded
, base::Unretained(this)));
98 InputServiceLinuxImpl::~InputServiceLinuxImpl() {
99 if (DeviceMonitorLinux::HasInstance())
100 DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
103 void InputServiceLinuxImpl::OnDeviceAdded(udev_device
* device
) {
104 DCHECK(CalledOnValidThread());
107 const char* devnode
= udev_device_get_devnode(device
);
111 InputDeviceInfo info
;
114 const char* subsystem
= udev_device_get_subsystem(device
);
117 if (strcmp(subsystem
, kSubsystemHid
) == 0) {
118 info
.subsystem
= InputServiceLinux::InputDeviceInfo::SUBSYSTEM_HID
;
119 info
.name
= GetParentDeviceName(device
, kSubsystemHid
);
120 } else if (strcmp(subsystem
, kSubsystemInput
) == 0) {
121 info
.subsystem
= InputServiceLinux::InputDeviceInfo::SUBSYSTEM_INPUT
;
122 info
.name
= GetParentDeviceName(device
, kSubsystemInput
);
127 info
.type
= GetDeviceType(device
);
129 info
.is_accelerometer
= GetBoolProperty(device
, kIdInputAccelerometer
);
130 info
.is_joystick
= GetBoolProperty(device
, kIdInputJoystick
);
131 info
.is_key
= GetBoolProperty(device
, kIdInputKey
);
132 info
.is_keyboard
= GetBoolProperty(device
, kIdInputKeyboard
);
133 info
.is_mouse
= GetBoolProperty(device
, kIdInputMouse
);
134 info
.is_tablet
= GetBoolProperty(device
, kIdInputTablet
);
135 info
.is_touchpad
= GetBoolProperty(device
, kIdInputTouchpad
);
136 info
.is_touchscreen
= GetBoolProperty(device
, kIdInputTouchscreen
);
141 void InputServiceLinuxImpl::OnDeviceRemoved(udev_device
* device
) {
142 DCHECK(CalledOnValidThread());
145 const char* devnode
= udev_device_get_devnode(device
);
147 RemoveDevice(devnode
);
152 InputServiceLinux::InputDeviceInfo::InputDeviceInfo()
153 : subsystem(SUBSYSTEM_UNKNOWN
),
155 is_accelerometer(false),
162 is_touchscreen(false) {}
164 InputServiceLinux::InputServiceLinux() {
165 base::ThreadRestrictions::AssertIOAllowed();
166 base::MessageLoop::current()->AddDestructionObserver(this);
169 InputServiceLinux::~InputServiceLinux() {
170 DCHECK(CalledOnValidThread());
171 base::MessageLoop::current()->RemoveDestructionObserver(this);
175 InputServiceLinux
* InputServiceLinux::GetInstance() {
177 g_input_service_linux_ptr
.Get().reset(new InputServiceLinuxImpl());
178 return g_input_service_linux_ptr
.Get().get();
182 bool InputServiceLinux::HasInstance() {
183 return g_input_service_linux_ptr
.Get().get();
187 void InputServiceLinux::SetForTesting(InputServiceLinux
* service
) {
188 g_input_service_linux_ptr
.Get().reset(service
);
191 void InputServiceLinux::AddObserver(Observer
* observer
) {
192 DCHECK(CalledOnValidThread());
194 observers_
.AddObserver(observer
);
197 void InputServiceLinux::RemoveObserver(Observer
* observer
) {
198 DCHECK(CalledOnValidThread());
200 observers_
.RemoveObserver(observer
);
203 void InputServiceLinux::GetDevices(std::vector
<InputDeviceInfo
>* devices
) {
204 DCHECK(CalledOnValidThread());
205 for (DeviceMap::iterator it
= devices_
.begin(), ie
= devices_
.end(); it
!= ie
;
207 devices
->push_back(it
->second
);
211 bool InputServiceLinux::GetDeviceInfo(const std::string
& id
,
212 InputDeviceInfo
* info
) const {
213 DCHECK(CalledOnValidThread());
214 DeviceMap::const_iterator it
= devices_
.find(id
);
215 if (it
== devices_
.end())
221 void InputServiceLinux::WillDestroyCurrentMessageLoop() {
222 DCHECK(CalledOnValidThread());
223 g_input_service_linux_ptr
.Get().reset(NULL
);
226 void InputServiceLinux::AddDevice(const InputDeviceInfo
& info
) {
227 devices_
[info
.id
] = info
;
228 FOR_EACH_OBSERVER(Observer
, observers_
, OnInputDeviceAdded(info
));
231 void InputServiceLinux::RemoveDevice(const std::string
& id
) {
233 FOR_EACH_OBSERVER(Observer
, observers_
, OnInputDeviceRemoved(id
));
236 bool InputServiceLinux::CalledOnValidThread() const {
237 return thread_checker_
.CalledOnValidThread();
240 } // namespace device