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 base::ThreadRestrictions::AssertIOAllowed();
94 base::MessageLoop::current()->AddDestructionObserver(this);
96 DeviceMonitorLinux::GetInstance()->AddObserver(this);
97 DeviceMonitorLinux::GetInstance()->Enumerate(base::Bind(
98 &InputServiceLinuxImpl::OnDeviceAdded
, base::Unretained(this)));
101 InputServiceLinuxImpl::~InputServiceLinuxImpl() {
102 if (DeviceMonitorLinux::HasInstance())
103 DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
104 base::MessageLoop::current()->RemoveDestructionObserver(this);
107 void InputServiceLinuxImpl::OnDeviceAdded(udev_device
* device
) {
108 DCHECK(CalledOnValidThread());
111 const char* devnode
= udev_device_get_devnode(device
);
115 InputDeviceInfo info
;
118 const char* subsystem
= udev_device_get_subsystem(device
);
121 if (strcmp(subsystem
, kSubsystemHid
) == 0) {
122 info
.subsystem
= InputServiceLinux::InputDeviceInfo::SUBSYSTEM_HID
;
123 info
.name
= GetParentDeviceName(device
, kSubsystemHid
);
124 } else if (strcmp(subsystem
, kSubsystemInput
) == 0) {
125 info
.subsystem
= InputServiceLinux::InputDeviceInfo::SUBSYSTEM_INPUT
;
126 info
.name
= GetParentDeviceName(device
, kSubsystemInput
);
131 info
.type
= GetDeviceType(device
);
133 info
.is_accelerometer
= GetBoolProperty(device
, kIdInputAccelerometer
);
134 info
.is_joystick
= GetBoolProperty(device
, kIdInputJoystick
);
135 info
.is_key
= GetBoolProperty(device
, kIdInputKey
);
136 info
.is_keyboard
= GetBoolProperty(device
, kIdInputKeyboard
);
137 info
.is_mouse
= GetBoolProperty(device
, kIdInputMouse
);
138 info
.is_tablet
= GetBoolProperty(device
, kIdInputTablet
);
139 info
.is_touchpad
= GetBoolProperty(device
, kIdInputTouchpad
);
140 info
.is_touchscreen
= GetBoolProperty(device
, kIdInputTouchscreen
);
145 void InputServiceLinuxImpl::OnDeviceRemoved(udev_device
* device
) {
146 DCHECK(CalledOnValidThread());
149 const char* devnode
= udev_device_get_devnode(device
);
151 RemoveDevice(devnode
);
156 InputServiceLinux::InputDeviceInfo::InputDeviceInfo()
157 : subsystem(SUBSYSTEM_UNKNOWN
),
159 is_accelerometer(false),
166 is_touchscreen(false) {}
168 InputServiceLinux::InputServiceLinux() {
171 InputServiceLinux::~InputServiceLinux() {
172 DCHECK(CalledOnValidThread());
176 InputServiceLinux
* InputServiceLinux::GetInstance() {
178 g_input_service_linux_ptr
.Get().reset(new InputServiceLinuxImpl());
179 return g_input_service_linux_ptr
.Get().get();
183 bool InputServiceLinux::HasInstance() {
184 return g_input_service_linux_ptr
.Get().get();
188 void InputServiceLinux::SetForTesting(InputServiceLinux
* service
) {
189 g_input_service_linux_ptr
.Get().reset(service
);
192 void InputServiceLinux::AddObserver(Observer
* observer
) {
193 DCHECK(CalledOnValidThread());
195 observers_
.AddObserver(observer
);
198 void InputServiceLinux::RemoveObserver(Observer
* observer
) {
199 DCHECK(CalledOnValidThread());
201 observers_
.RemoveObserver(observer
);
204 void InputServiceLinux::GetDevices(std::vector
<InputDeviceInfo
>* devices
) {
205 DCHECK(CalledOnValidThread());
206 for (DeviceMap::iterator it
= devices_
.begin(), ie
= devices_
.end(); it
!= ie
;
208 devices
->push_back(it
->second
);
212 bool InputServiceLinux::GetDeviceInfo(const std::string
& id
,
213 InputDeviceInfo
* info
) const {
214 DCHECK(CalledOnValidThread());
215 DeviceMap::const_iterator it
= devices_
.find(id
);
216 if (it
== devices_
.end())
222 void InputServiceLinux::WillDestroyCurrentMessageLoop() {
223 DCHECK(CalledOnValidThread());
224 g_input_service_linux_ptr
.Get().reset(NULL
);
227 void InputServiceLinux::AddDevice(const InputDeviceInfo
& info
) {
228 devices_
[info
.id
] = info
;
229 FOR_EACH_OBSERVER(Observer
, observers_
, OnInputDeviceAdded(info
));
232 void InputServiceLinux::RemoveDevice(const std::string
& id
) {
234 FOR_EACH_OBSERVER(Observer
, observers_
, OnInputDeviceRemoved(id
));
237 bool InputServiceLinux::CalledOnValidThread() const {
238 return thread_checker_
.CalledOnValidThread();
241 } // namespace device