#include "device/gamepad/udev_gamepad_linux.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "device/udev_linux/udev.h"
namespace device {
namespace {
bool DeviceIndexFromDevicePath(std::string_view path,
std::string_view prefix,
int* index) {
DCHECK(index);
if (!base::StartsWith(path, prefix))
return false;
std::string_view index_str = path;
index_str.remove_prefix(prefix.length());
return base::StringToInt(index_str, index);
}
std::string_view ToStringView(const char* str) {
return str ? std::string_view(str) : std::string_view();
}
}
const char UdevGamepadLinux::kInputSubsystem[] = "input";
const char UdevGamepadLinux::kHidrawSubsystem[] = "hidraw";
UdevGamepadLinux::UdevGamepadLinux(Type type,
int index,
std::string_view path,
std::string_view syspath_prefix)
: type(type), index(index), path(path), syspath_prefix(syspath_prefix) {}
std::unique_ptr<UdevGamepadLinux> UdevGamepadLinux::Create(udev_device* dev) {
using DeviceRootPair = std::pair<Type, const char*>;
static const std::vector<DeviceRootPair> device_roots = {
{Type::EVDEV, "/dev/input/event"},
{Type::JOYDEV, "/dev/input/js"},
{Type::HIDRAW, "/dev/hidraw"},
};
if (!dev)
return nullptr;
const auto node_path = ToStringView(device::udev_device_get_devnode(dev));
if (node_path.empty())
return nullptr;
const auto node_syspath = ToStringView(device::udev_device_get_syspath(dev));
if (node_syspath.empty())
return nullptr;
std::string_view parent_syspath;
udev_device* parent_dev =
device::udev_device_get_parent_with_subsystem_devtype(
dev, kInputSubsystem, nullptr);
if (parent_dev)
parent_syspath = ToStringView(device::udev_device_get_syspath(parent_dev));
for (const auto& entry : device_roots) {
const Type node_type = entry.first;
const std::string_view prefix = entry.second;
int index_value;
if (!DeviceIndexFromDevicePath(node_path, prefix, &index_value))
continue;
std::string_view syspath;
std::string_view subsystem;
if (node_type == Type::EVDEV || node_type == Type::JOYDEV) {
if (!device::udev_device_get_property_value(dev, "ID_INPUT_JOYSTICK"))
return nullptr;
syspath = parent_syspath;
subsystem = kInputSubsystem;
} else if (node_type == Type::HIDRAW) {
syspath = node_syspath;
subsystem = kHidrawSubsystem;
}
std::string_view syspath_prefix;
if (!syspath.empty()) {
size_t subsystem_start = syspath.find(subsystem);
if (subsystem_start == std::string::npos)
return nullptr;
syspath_prefix = syspath.substr(0, subsystem_start);
}
return std::make_unique<UdevGamepadLinux>(node_type, index_value, node_path,
syspath_prefix);
}
return nullptr;
}
}