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 241