Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / device / hid / input_service_linux.cc
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.
4
5 #include <libudev.h>
6
7 #include "base/bind.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/hid/input_service_linux.h"
14
15 namespace device {
16
17 namespace {
18
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";
32
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;
36
37 bool GetBoolProperty(udev_device* device, const char* key) {
38   CHECK(device);
39   CHECK(key);
40   const char* property = udev_device_get_property_value(device, key);
41   if (!property)
42     return false;
43   int value;
44   if (!base::StringToInt(property, &value)) {
45     LOG(ERROR) << "Not an integer value for " << key << " property";
46     return false;
47   }
48   return (value != 0);
49 }
50
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;
55   }
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;
61 }
62
63 std::string GetParentDeviceName(udev_device* device, const char* subsystem) {
64   udev_device* parent =
65       udev_device_get_parent_with_subsystem_devtype(device, subsystem, NULL);
66   if (!parent)
67     return std::string();
68   const char* name = udev_device_get_property_value(parent, "NAME");
69   if (!name)
70     return std::string();
71   std::string result;
72   base::TrimString(name, "\"", &result);
73   return result;
74 }
75
76 class InputServiceLinuxImpl : public InputServiceLinux,
77                               public DeviceMonitorLinux::Observer {
78  public:
79   // Implements DeviceMonitorLinux::Observer:
80   virtual void OnDeviceAdded(udev_device* device) OVERRIDE;
81   virtual void OnDeviceRemoved(udev_device* device) OVERRIDE;
82
83  private:
84   friend class InputServiceLinux;
85
86   InputServiceLinuxImpl();
87   virtual ~InputServiceLinuxImpl();
88
89   DISALLOW_COPY_AND_ASSIGN(InputServiceLinuxImpl);
90 };
91
92 InputServiceLinuxImpl::InputServiceLinuxImpl() {
93   DeviceMonitorLinux::GetInstance()->AddObserver(this);
94   DeviceMonitorLinux::GetInstance()->Enumerate(base::Bind(
95       &InputServiceLinuxImpl::OnDeviceAdded, base::Unretained(this)));
96 }
97
98 InputServiceLinuxImpl::~InputServiceLinuxImpl() {
99   if (DeviceMonitorLinux::HasInstance())
100     DeviceMonitorLinux::GetInstance()->RemoveObserver(this);
101 }
102
103 void InputServiceLinuxImpl::OnDeviceAdded(udev_device* device) {
104   DCHECK(CalledOnValidThread());
105   if (!device)
106     return;
107   const char* devnode = udev_device_get_devnode(device);
108   if (!devnode)
109     return;
110
111   InputDeviceInfo info;
112   info.id = devnode;
113
114   const char* subsystem = udev_device_get_subsystem(device);
115   if (!subsystem)
116     return;
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);
123   } else {
124     return;
125   }
126
127   info.type = GetDeviceType(device);
128
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);
137
138   AddDevice(info);
139 }
140
141 void InputServiceLinuxImpl::OnDeviceRemoved(udev_device* device) {
142   DCHECK(CalledOnValidThread());
143   if (!device)
144     return;
145   const char* devnode = udev_device_get_devnode(device);
146   if (devnode)
147     RemoveDevice(devnode);
148 }
149
150 }  // namespace
151
152 InputServiceLinux::InputDeviceInfo::InputDeviceInfo()
153     : subsystem(SUBSYSTEM_UNKNOWN),
154       type(TYPE_UNKNOWN),
155       is_accelerometer(false),
156       is_joystick(false),
157       is_key(false),
158       is_keyboard(false),
159       is_mouse(false),
160       is_tablet(false),
161       is_touchpad(false),
162       is_touchscreen(false) {}
163
164 InputServiceLinux::InputServiceLinux() {
165   base::ThreadRestrictions::AssertIOAllowed();
166   base::MessageLoop::current()->AddDestructionObserver(this);
167 }
168
169 InputServiceLinux::~InputServiceLinux() {
170   DCHECK(CalledOnValidThread());
171   base::MessageLoop::current()->RemoveDestructionObserver(this);
172 }
173
174 // static
175 InputServiceLinux* InputServiceLinux::GetInstance() {
176   if (!HasInstance())
177     g_input_service_linux_ptr.Get().reset(new InputServiceLinuxImpl());
178   return g_input_service_linux_ptr.Get().get();
179 }
180
181 // static
182 bool InputServiceLinux::HasInstance() {
183   return g_input_service_linux_ptr.Get().get();
184 }
185
186 // static
187 void InputServiceLinux::SetForTesting(InputServiceLinux* service) {
188   g_input_service_linux_ptr.Get().reset(service);
189 }
190
191 void InputServiceLinux::AddObserver(Observer* observer) {
192   DCHECK(CalledOnValidThread());
193   if (observer)
194     observers_.AddObserver(observer);
195 }
196
197 void InputServiceLinux::RemoveObserver(Observer* observer) {
198   DCHECK(CalledOnValidThread());
199   if (observer)
200     observers_.RemoveObserver(observer);
201 }
202
203 void InputServiceLinux::GetDevices(std::vector<InputDeviceInfo>* devices) {
204   DCHECK(CalledOnValidThread());
205   for (DeviceMap::iterator it = devices_.begin(), ie = devices_.end(); it != ie;
206        ++it) {
207     devices->push_back(it->second);
208   }
209 }
210
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())
216     return false;
217   *info = it->second;
218   return true;
219 }
220
221 void InputServiceLinux::WillDestroyCurrentMessageLoop() {
222   DCHECK(CalledOnValidThread());
223   g_input_service_linux_ptr.Get().reset(NULL);
224 }
225
226 void InputServiceLinux::AddDevice(const InputDeviceInfo& info) {
227   devices_[info.id] = info;
228   FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceAdded(info));
229 }
230
231 void InputServiceLinux::RemoveDevice(const std::string& id) {
232   devices_.erase(id);
233   FOR_EACH_OBSERVER(Observer, observers_, OnInputDeviceRemoved(id));
234 }
235
236 bool InputServiceLinux::CalledOnValidThread() const {
237   return thread_checker_.CalledOnValidThread();
238 }
239
240 }  // namespace device