blob: 323962f79cb1f4fdb2362b68e7a14fbafd7aeae0 [file] [log] [blame]
// Copyright 2021 The Chromium OS 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 "runtime_probe/utils/input_device.h"
#include <limits>
#include <pcrecpp.h>
#include <base/logging.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_split.h>
#include <base/strings/string_util.h>
namespace runtime_probe {
namespace {
constexpr auto kNameEntryPrefix = "Name=";
constexpr auto kSysfsEntryPrefix = "Sysfs=";
const pcrecpp::RE kEventPatternRe(R"(event[\d]+)");
constexpr auto kBitsPerBitmap = sizeof(long) * CHAR_BIT; // NOLINT(runtime/int)
// Reference:
// https://chromium.googlesource.com/chromiumos/third_party/kernel/+/v4.14/include/uapi/linux/input-event-codes.h
constexpr auto kAbsMtSlot = 0x2f;
constexpr auto kBtnMouse = 0x110;
constexpr auto kBtnToolPen = 0x140;
constexpr auto kBtnTouch = 0x14a;
constexpr auto kBtnStylus = 0x14b;
constexpr auto kBtnStylus2 = 0x14c;
} // namespace
std::unique_ptr<InputDeviceImpl> InputDeviceImpl::From(
const std::vector<std::string>& lines) {
auto input_device = std::make_unique<InputDeviceImpl>();
// Example lines:
// I: Bus=1234 Vendor=5678 Product=90ab Version=cdef
// N: Name="XXXX"
// P: Phys=XXXX
// S: Sysfs=/devices/XXXX
// H: Handlers=event5
// B: EV=b
// B: KEY=e520 10000 0 0 0 0
// B: ABS=663800013000003
for (const auto& line : lines) {
if (line.length() < 3) {
DCHECK_EQ(line.length(), 0);
continue;
}
auto content = base::StringPiece(line).substr(3);
base::StringPairs keyVals;
switch (line[0]) {
case 'I': {
if (!base::SplitStringIntoKeyValuePairs(content, '=', ' ', &keyVals)) {
DVLOG(1) << "Failed to parse input devices (" << line[0] << ").";
return nullptr;
}
for (const auto& [key, value] : keyVals) {
if (key == "Bus")
input_device->bus = value;
else if (key == "Vendor")
input_device->vendor = value;
else if (key == "Product")
input_device->product = value;
else if (key == "Version")
input_device->version = value;
}
break;
}
case 'N': {
if (!base::StartsWith(content, kNameEntryPrefix)) {
DVLOG(1) << "Failed to parse input devices (" << line[0] << ").";
return nullptr;
}
auto value = content.substr(strlen(kNameEntryPrefix));
base::TrimString(value, "\"", &input_device->name);
break;
}
case 'S': {
if (!base::StartsWith(content, kSysfsEntryPrefix)) {
DVLOG(1) << "Failed to parse input devices (" << line[0] << ").";
return nullptr;
}
input_device->sysfs =
std::string(content.substr(strlen(kSysfsEntryPrefix)));
break;
}
case 'H': {
if (!base::SplitStringIntoKeyValuePairs(content, '=', '\n', &keyVals)) {
DVLOG(1) << "Failed to parse input devices (" << line[0] << ").";
return nullptr;
}
const auto& value = keyVals[0].second;
const auto& handlers = base::SplitString(
value, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
for (const auto& handler : handlers) {
if (kEventPatternRe.FullMatch(handler)) {
input_device->event = handler;
break;
}
}
break;
}
case 'B': {
if (!base::SplitStringIntoKeyValuePairs(content, '=', '\n', &keyVals)) {
LOG(ERROR) << "Failed to parse input devices (" << line[0] << ").";
return nullptr;
}
const auto& [key, value] = keyVals[0];
// The bitmaps are represented as several hexadecimal numbers joined by
// whitespaces. Each hexadecimal number is a long int, which has
// different range of values under 32-bit and 64-bit system. Therefore,
// we use sizeof(long) to handle such variation.
const auto& flags = base::SplitStringPiece(
value, " ", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
for (const auto& flag : flags) {
uint64_t output;
CHECK(base::HexStringToUInt64(flag, &output));
if (key == "KEY") {
auto& bits = input_device->ev_key;
bits = (bits << kBitsPerBitmap) |
std::remove_reference<decltype(bits)>::type(output);
} else if (key == "ABS") {
auto& bits = input_device->ev_abs;
bits = (bits << kBitsPerBitmap) |
std::remove_reference<decltype(bits)>::type(output);
} else if (key == "SW") {
auto& bits = input_device->ev_sw;
bits = (bits << kBitsPerBitmap) |
std::remove_reference<decltype(bits)>::type(output);
}
}
break;
}
default: {
break;
}
}
}
return input_device;
}
bool InputDeviceImpl::IsStylusDevice() const {
return ev_key[kBtnStylus] || ev_key[kBtnStylus2] || ev_key[kBtnToolPen];
}
bool InputDeviceImpl::IsTouchpadDevice() const {
return ev_key[kBtnTouch] && ev_key[kBtnMouse];
}
bool InputDeviceImpl::IsTouchscreenDevice() const {
return !IsTouchpadDevice() && ev_abs[kAbsMtSlot];
}
std::string InputDeviceImpl::type() const {
if (IsStylusDevice()) {
return InputDeviceImpl::Type::STYLUS;
} else if (IsTouchpadDevice()) {
return InputDeviceImpl::Type::TOUCHPAD;
} else if (IsTouchscreenDevice()) {
return InputDeviceImpl::Type::TOUCHSCREEN;
} else {
return InputDeviceImpl::Type::UNKNOWN;
}
}
} // namespace runtime_probe