#include "ui/events/ozone/evdev/event_converter_evdev_impl.h"
#include <errno.h>
#include <linux/input.h>
#include <stddef.h>
#include "base/logging.h"
#include "base/trace_event/trace_event.h"
#include "build/chromeos_buildflags.h"
#include "ui/events/devices/stylus_state.h"
#include "ui/events/event.h"
#include "ui/events/event_utils.h"
#include "ui/events/keycodes/dom/keycode_converter.h"
#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
#include "ui/events/ozone/evdev/event_device_util.h"
#include "ui/events/ozone/evdev/numberpad_metrics.h"
#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "ui/events/ozone/evdev/numberpad_metrics.h"
#endif
namespace ui {
namespace {
const int kKeyReleaseValue = 0;
const int kKeyRepeatValue = 2;
const int kSwitchStylusInserted = SW_PEN_INSERTED;
}
EventConverterEvdevImpl::EventConverterEvdevImpl(
base::ScopedFD fd,
base::FilePath path,
int id,
const EventDeviceInfo& devinfo,
CursorDelegateEvdev* cursor,
DeviceEventDispatcherEvdev* dispatcher)
: EventConverterEvdev(fd.get(),
path,
id,
devinfo.device_type(),
devinfo.name(),
devinfo.phys(),
devinfo.vendor_id(),
devinfo.product_id(),
devinfo.version()),
input_device_fd_(std::move(fd)),
keyboard_type_(devinfo.GetKeyboardType()),
has_touchpad_(devinfo.HasTouchpad()),
has_numberpad_(devinfo.HasNumberpad()),
has_stylus_switch_(devinfo.HasStylusSwitch()),
has_caps_lock_led_(devinfo.HasLedEvent(LED_CAPSL)),
controller_(FROM_HERE),
cursor_(cursor),
dispatcher_(dispatcher) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (has_numberpad_)
NumberpadMetricsRecorder::GetInstance()->AddDevice(input_device_);
#endif
const auto key_bits = devinfo.GetKeyBits();
key_bits_.resize(EVDEV_BITS_TO_INT64(KEY_CNT));
for (int i = 0; i < KEY_CNT; i++) {
if (EvdevBitIsSet(key_bits.data(), i)) {
EvdevSetUint64Bit(key_bits_.data(), i);
}
}
}
EventConverterEvdevImpl::~EventConverterEvdevImpl() {
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (has_numberpad_)
NumberpadMetricsRecorder::GetInstance()->RemoveDevice(input_device_);
#endif
}
void EventConverterEvdevImpl::OnFileCanReadWithoutBlocking(int fd) {
TRACE_EVENT1("evdev", "EventConverterEvdevImpl::OnFileCanReadWithoutBlocking",
"fd", fd);
input_event inputs[4];
ssize_t read_size = read(fd, inputs, sizeof(inputs));
if (read_size < 0) {
if (errno == EINTR || errno == EAGAIN)
return;
if (errno != ENODEV)
PLOG(ERROR) << "error reading device " << path_.value();
Stop();
return;
}
if (!IsEnabled())
return;
DCHECK_EQ(read_size % sizeof(*inputs), 0u);
ProcessEvents(inputs, read_size / sizeof(*inputs));
}
KeyboardType EventConverterEvdevImpl::GetKeyboardType() const {
return keyboard_type_;
}
bool EventConverterEvdevImpl::HasKeyboard() const {
return keyboard_type_ == KeyboardType::VALID_KEYBOARD;
}
bool EventConverterEvdevImpl::HasTouchpad() const {
return has_touchpad_;
}
bool EventConverterEvdevImpl::HasCapsLockLed() const {
return has_caps_lock_led_;
}
bool EventConverterEvdevImpl::HasStylusSwitch() const {
return has_stylus_switch_;
}
void EventConverterEvdevImpl::SetKeyFilter(bool enable_filter,
std::vector<DomCode> allowed_keys) {
if (!enable_filter) {
blocked_keys_.reset();
return;
}
blocked_keys_.set();
for (const DomCode& code : allowed_keys) {
blocked_keys_.reset(KeycodeConverter::DomCodeToEvdevCode(code));
}
base::TimeTicks timestamp = ui::EventTimeForNow();
for (int key = 0; key < KEY_CNT; ++key) {
if (blocked_keys_.test(key))
OnKeyChange(key, false , timestamp);
}
}
void EventConverterEvdevImpl::OnDisabled() {
ReleaseKeys();
ReleaseMouseButtons();
}
std::vector<uint64_t> EventConverterEvdevImpl::GetKeyboardKeyBits() const {
return key_bits_;
}
ui::StylusState EventConverterEvdevImpl::GetStylusSwitchState() {
if (!HasStylusSwitch()) {
return ui::StylusState::REMOVED;
}
unsigned long array[EVDEV_BITS_TO_LONGS(SW_MAX)] = {0};
int result = ioctl(input_device_fd_.get(), EVIOCGSW(SW_MAX), array);
if (result == -1) {
return ui::StylusState::REMOVED;
}
return EvdevBitIsSet(array, kSwitchStylusInserted) ? ui::StylusState::INSERTED
: ui::StylusState::REMOVED;
}
void EventConverterEvdevImpl::ProcessEvents(const input_event* inputs,
int count) {
for (int i = 0; i < count; ++i) {
const input_event& input = inputs[i];
switch (input.type) {
case EV_MSC:
if (input.code == MSC_SCAN)
last_scan_code_ = input.value;
break;
case EV_KEY:
ConvertKeyEvent(input);
last_scan_code_ = 0;
break;
case EV_REL:
ConvertMouseMoveEvent(input);
break;
case EV_SYN:
if (input.code == SYN_DROPPED)
OnLostSync();
else if (input.code == SYN_REPORT)
FlushEvents(input);
last_scan_code_ = 0;
break;
case EV_SW:
if (input.code == kSwitchStylusInserted) {
dispatcher_->DispatchStylusStateChanged(
input.value ? ui::StylusState::INSERTED
: ui::StylusState::REMOVED);
}
break;
}
}
}
void EventConverterEvdevImpl::ConvertKeyEvent(const input_event& input) {
if (input.value == kKeyRepeatValue)
return;
if (input.code >= BTN_MOUSE && input.code < BTN_JOYSTICK) {
DispatchMouseButton(input);
return;
}
OnKeyChange(input.code, input.value != kKeyReleaseValue,
TimeTicksFromInputEvent(input));
}
void EventConverterEvdevImpl::ConvertMouseMoveEvent(const input_event& input) {
if (!cursor_)
return;
switch (input.code) {
case REL_X:
x_offset_ = input.value;
break;
case REL_Y:
y_offset_ = input.value;
break;
}
}
void EventConverterEvdevImpl::OnKeyChange(unsigned int key,
bool down,
const base::TimeTicks& timestamp) {
if (key > KEY_MAX)
return;
if (down == key_state_.test(key))
return;
if (down && blocked_keys_.test(key))
return;
key_state_.set(key, down);
GenerateKeyMetrics(key, down);
if (key_state_.count() == 1 && ((key >= KEY_1 && key <= KEY_EQUAL) ||
(key >= KEY_Q && key <= KEY_RIGHTBRACE) ||
(key >= KEY_A && key <= KEY_APOSTROPHE) ||
(key >= KEY_BACKSLASH && key <= KEY_SLASH))) {
bool was_suspected = IsSuspectedImposter();
SetSuspectedImposter(false);
if (was_suspected && received_valid_input_callback_) {
received_valid_input_callback_.Run(this);
}
}
dispatcher_->DispatchKeyEvent(
KeyEventParams(input_device_.id, ui::EF_NONE, key, last_scan_code_, down,
false , timestamp));
}
void EventConverterEvdevImpl::GenerateKeyMetrics(unsigned int key, bool down) {
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (!has_numberpad_)
return;
NumberpadMetricsRecorder::GetInstance()->ProcessKey(key, down, input_device_);
#endif
}
void EventConverterEvdevImpl::ReleaseKeys() {
base::TimeTicks timestamp = ui::EventTimeForNow();
for (int key = 0; key < KEY_CNT; ++key)
OnKeyChange(key, false , timestamp);
}
void EventConverterEvdevImpl::ReleaseMouseButtons() {
base::TimeTicks timestamp = ui::EventTimeForNow();
for (int code = BTN_MOUSE; code < BTN_JOYSTICK; ++code)
OnButtonChange(code, false , timestamp);
}
void EventConverterEvdevImpl::OnLostSync() {
LOG(WARNING) << "kernel dropped input events";
ReleaseKeys();
ReleaseMouseButtons();
}
void EventConverterEvdevImpl::DispatchMouseButton(const input_event& input) {
if (!cursor_)
return;
OnButtonChange(input.code, input.value, TimeTicksFromInputEvent(input));
}
void EventConverterEvdevImpl::OnButtonChange(int code,
bool down,
base::TimeTicks timestamp) {
if (code == BTN_SIDE)
code = BTN_BACK;
else if (code == BTN_EXTRA)
code = BTN_FORWARD;
int button_offset = code - BTN_MOUSE;
if (mouse_button_state_.test(button_offset) == down)
return;
mouse_button_state_.set(button_offset, down);
dispatcher_->DispatchMouseButtonEvent(MouseButtonEventParams(
input_device_.id, EF_NONE, cursor_->GetLocation(), code, down,
MouseButtonMapType::kMouse, PointerDetails(EventPointerType::kMouse),
timestamp));
}
void EventConverterEvdevImpl::SetReceivedValidInputCallback(
ReceivedValidInputCallback callback) {
received_valid_input_callback_ = callback;
}
void EventConverterEvdevImpl::FlushEvents(const input_event& input) {
if (!cursor_ || (x_offset_ == 0 && y_offset_ == 0))
return;
cursor_->MoveCursor(gfx::Vector2dF(x_offset_, y_offset_));
dispatcher_->DispatchMouseMoveEvent(MouseMoveEventParams(
input_device_.id, EF_NONE, cursor_->GetLocation(),
nullptr , PointerDetails(EventPointerType::kMouse),
TimeTicksFromInputEvent(input)));
x_offset_ = 0;
y_offset_ = 0;
}
}