普通文本  |  241行  |  7.37 KB

// 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