blob: e88148d19dd29ff80b66a57838ca54d4bb1e4441 [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 "base/message_loop/message_loop.h"
#include "ui/events/event.h"
namespace ui {
TabletEventConverterEvdev::TabletEventConverterEvdev(
int fd,
base::FilePath path,
int id,
EventModifiersEvdev* modifiers,
CursorDelegateEvdev* cursor,
const EventDeviceInfo& info,
const EventDispatchCallback& callback)
: EventConverterEvdev(fd, path, id),
cursor_(cursor),
modifiers_(modifiers),
callback_(callback),
stylus_(0),
abs_value_dirty_(false) {
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;
}
TabletEventConverterEvdev::~TabletEventConverterEvdev() {
Stop();
close(fd_);
}
void TabletEventConverterEvdev::OnFileCanReadWithoutBlocking(int 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;
}
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();
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;
}
}
void TabletEventConverterEvdev::UpdateCursor() {
gfx::Rect display_bounds = cursor_->GetCursorDisplayBounds();
int x =
((x_abs_location_ - x_abs_min_) * display_bounds.width()) / x_abs_range_;
int y =
((y_abs_location_ - y_abs_min_) * display_bounds.height()) / y_abs_range_;
x += display_bounds.x();
y += display_bounds.y();
cursor_->MoveCursorTo(gfx::PointF(x, y));
}
void TabletEventConverterEvdev::DispatchMouseButton(const input_event& input) {
if (!cursor_)
return;
unsigned int modifier;
// These are the same as X11 behaviour
if (input.code == BTN_TOUCH)
modifier = EVDEV_MODIFIER_LEFT_MOUSE_BUTTON;
else if (input.code == BTN_STYLUS2)
modifier = EVDEV_MODIFIER_RIGHT_MOUSE_BUTTON;
else if (input.code == BTN_STYLUS)
modifier = EVDEV_MODIFIER_MIDDLE_MOUSE_BUTTON;
else
return;
if (abs_value_dirty_) {
UpdateCursor();
abs_value_dirty_ = false;
}
int flag = modifiers_->GetEventFlagFromModifier(modifier);
modifiers_->UpdateModifier(modifier, input.value);
callback_.Run(make_scoped_ptr(
new MouseEvent(input.value ? ET_MOUSE_PRESSED : ET_MOUSE_RELEASED,
cursor_->GetLocation(), cursor_->GetLocation(),
modifiers_->GetModifierFlags() | flag, flag)));
}
void TabletEventConverterEvdev::FlushEvents() {
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();
callback_.Run(make_scoped_ptr(
new MouseEvent(ui::ET_MOUSE_MOVED, cursor_->GetLocation(),
cursor_->GetLocation(), modifiers_->GetModifierFlags(),
/* changed_button_flags */ 0)));
abs_value_dirty_ = false;
}
} // namespace ui