blob: 3a923661c7fca652509386c6525f9eae23695ce5 [file] [log] [blame]
// 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 "ui/events/ozone/evdev/tablet_event_converter_evdev.h"
#include <errno.h>
#include <linux/input.h>
#include <stddef.h>
#include "base/trace_event/trace_event.h"
#include "ui/events/event.h"
#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
namespace ui {
namespace {
// Convert tilt from [min, min + num_values) to [-90deg, +90deg)
float ScaleTilt(int value, int min_value, int num_values) {
return 180.f * (value - min_value) / num_values - 90.f;
}
EventPointerType GetToolType(int button_tool) {
if (button_tool == BTN_TOOL_RUBBER)
return EventPointerType::POINTER_TYPE_ERASER;
return EventPointerType::POINTER_TYPE_PEN;
}
} // namespace
TabletEventConverterEvdev::TabletEventConverterEvdev(
base::ScopedFD fd,
base::FilePath path,
int id,
CursorDelegateEvdev* cursor,
const EventDeviceInfo& info,
DeviceEventDispatcherEvdev* dispatcher)
: EventConverterEvdev(fd.get(),
path,
id,
info.device_type(),
info.name(),
info.phys(),
info.vendor_id(),
info.product_id()),
input_device_fd_(std::move(fd)),
controller_(FROM_HERE),
cursor_(cursor),
dispatcher_(dispatcher) {
x_abs_min_ = info.GetAbsMinimum(ABS_X);
x_abs_range_ = info.GetAbsMaximum(ABS_X) - x_abs_min_ + 1;
y_abs_min_ = info.GetAbsMinimum(ABS_Y);
y_abs_range_ = info.GetAbsMaximum(ABS_Y) - y_abs_min_ + 1;
tilt_x_min_ = info.GetAbsMinimum(ABS_TILT_X);
tilt_y_min_ = info.GetAbsMinimum(ABS_TILT_Y);
tilt_x_range_ = info.GetAbsMaximum(ABS_TILT_X) - tilt_x_min_ + 1;
tilt_y_range_ = info.GetAbsMaximum(ABS_TILT_Y) - tilt_y_min_ + 1;
pressure_max_ = info.GetAbsMaximum(ABS_PRESSURE);
if (info.HasKeyEvent(BTN_STYLUS) && !info.HasKeyEvent(BTN_STYLUS2))
one_side_btn_pen_ = true;
}
TabletEventConverterEvdev::~TabletEventConverterEvdev() {
}
void TabletEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
TRACE_EVENT1("evdev",
"TabletEventConverterEvdev::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));
}
void TabletEventConverterEvdev::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_KEY:
ConvertKeyEvent(input);
break;
case EV_ABS:
ConvertAbsEvent(input);
break;
case EV_SYN:
FlushEvents(input);
break;
}
}
}
void TabletEventConverterEvdev::ConvertKeyEvent(const input_event& input) {
// Only handle other events if we have a stylus in proximity
if (input.code >= BTN_TOOL_PEN && input.code <= BTN_TOOL_LENS) {
if (input.value == 1)
stylus_ = input.code;
else if (input.value == 0)
stylus_ = 0;
else
LOG(WARNING) << "Unexpected value: " << input.value
<< " for code: " << input.code;
}
if (input.code >= BTN_TOUCH && input.code <= BTN_STYLUS2) {
DispatchMouseButton(input);
return;
}
}
void TabletEventConverterEvdev::ConvertAbsEvent(const input_event& input) {
if (!cursor_)
return;
switch (input.code) {
case ABS_X:
x_abs_location_ = input.value;
abs_value_dirty_ = true;
break;
case ABS_Y:
y_abs_location_ = input.value;
abs_value_dirty_ = true;
break;
case ABS_TILT_X:
tilt_x_ = ScaleTilt(input.value, tilt_x_min_, tilt_x_range_);
abs_value_dirty_ = true;
break;
case ABS_TILT_Y:
tilt_y_ = ScaleTilt(input.value, tilt_y_min_, tilt_y_range_);
abs_value_dirty_ = true;
break;
case ABS_PRESSURE:
pressure_ = (float)input.value / pressure_max_;
abs_value_dirty_ = true;
break;
}
}
void TabletEventConverterEvdev::UpdateCursor() {
gfx::Rect confined_bounds = cursor_->GetCursorConfinedBounds();
int x =
((x_abs_location_ - x_abs_min_) * confined_bounds.width()) / x_abs_range_;
int y = ((y_abs_location_ - y_abs_min_) * confined_bounds.height()) /
y_abs_range_;
x += confined_bounds.x();
y += confined_bounds.y();
cursor_->MoveCursorTo(gfx::PointF(x, y));
}
void TabletEventConverterEvdev::DispatchMouseButton(const input_event& input) {
if (!cursor_)
return;
unsigned int button;
// These are the same as X11 behaviour
if (input.code == BTN_TOUCH) {
button = BTN_LEFT;
} else if (input.code == BTN_STYLUS2) {
button = BTN_RIGHT;
} else if (input.code == BTN_STYLUS) {
if (one_side_btn_pen_)
button = BTN_RIGHT;
else
button = BTN_MIDDLE;
} else {
return;
}
if (abs_value_dirty_) {
UpdateCursor();
abs_value_dirty_ = false;
}
bool down = input.value;
dispatcher_->DispatchMouseButtonEvent(MouseButtonEventParams(
input_device_.id, EF_NONE, cursor_->GetLocation(), button, down,
false /* allow_remap */,
PointerDetails(GetToolType(stylus_), /* pointer_id*/ 0,
/* radius_x */ 0.0f, /* radius_y */ 0.0f, pressure_,
/* twist */ 0.0f, tilt_x_, tilt_y_),
TimeTicksFromInputEvent(input)));
}
void TabletEventConverterEvdev::FlushEvents(const input_event& input) {
if (!cursor_)
return;
// Prevent propagation of invalid data on stylus lift off
if (stylus_ == 0) {
abs_value_dirty_ = false;
return;
}
if (!abs_value_dirty_)
return;
UpdateCursor();
dispatcher_->DispatchMouseMoveEvent(MouseMoveEventParams(
input_device_.id, EF_NONE, cursor_->GetLocation(),
PointerDetails(GetToolType(stylus_), /* pointer_id*/ 0,
/* radius_x */ 0.0f, /* radius_y */ 0.0f, pressure_,
/* twist */ 0.0f, tilt_x_, tilt_y_),
TimeTicksFromInputEvent(input)));
abs_value_dirty_ = false;
}
} // namespace ui