1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "ui/events/ozone/evdev/tablet_event_converter_evdev.h"
6
7 #include <errno.h>
8 #include <linux/input.h>
9 #include <stddef.h>
10
11 #include "base/trace_event/trace_event.h"
12 #include "ui/events/event.h"
13 #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
14
15 namespace ui {
16
17 namespace {
18
19 // Convert tilt from [min, min + num_values) to [-90deg, +90deg)
ScaleTilt(int value,int min_value,int num_values)20 float ScaleTilt(int value, int min_value, int num_values) {
21 return 180.f * (value - min_value) / num_values - 90.f;
22 }
23
GetToolType(int button_tool)24 EventPointerType GetToolType(int button_tool) {
25 if (button_tool == BTN_TOOL_RUBBER)
26 return EventPointerType::POINTER_TYPE_ERASER;
27 return EventPointerType::POINTER_TYPE_PEN;
28 }
29
30 } // namespace
31
TabletEventConverterEvdev(base::ScopedFD fd,base::FilePath path,int id,CursorDelegateEvdev * cursor,const EventDeviceInfo & info,DeviceEventDispatcherEvdev * dispatcher)32 TabletEventConverterEvdev::TabletEventConverterEvdev(
33 base::ScopedFD fd,
34 base::FilePath path,
35 int id,
36 CursorDelegateEvdev* cursor,
37 const EventDeviceInfo& info,
38 DeviceEventDispatcherEvdev* dispatcher)
39 : EventConverterEvdev(fd.get(),
40 path,
41 id,
42 info.device_type(),
43 info.name(),
44 info.phys(),
45 info.vendor_id(),
46 info.product_id(),
47 info.version()),
48 input_device_fd_(std::move(fd)),
49 controller_(FROM_HERE),
50 cursor_(cursor),
51 dispatcher_(dispatcher) {
52 x_abs_min_ = info.GetAbsMinimum(ABS_X);
53 x_abs_range_ = info.GetAbsMaximum(ABS_X) - x_abs_min_ + 1;
54 y_abs_min_ = info.GetAbsMinimum(ABS_Y);
55 y_abs_range_ = info.GetAbsMaximum(ABS_Y) - y_abs_min_ + 1;
56 tilt_x_min_ = info.GetAbsMinimum(ABS_TILT_X);
57 tilt_y_min_ = info.GetAbsMinimum(ABS_TILT_Y);
58 tilt_x_range_ = info.GetAbsMaximum(ABS_TILT_X) - tilt_x_min_ + 1;
59 tilt_y_range_ = info.GetAbsMaximum(ABS_TILT_Y) - tilt_y_min_ + 1;
60 pressure_max_ = info.GetAbsMaximum(ABS_PRESSURE);
61
62 if (info.HasKeyEvent(BTN_STYLUS) && !info.HasKeyEvent(BTN_STYLUS2))
63 one_side_btn_pen_ = true;
64 }
65
~TabletEventConverterEvdev()66 TabletEventConverterEvdev::~TabletEventConverterEvdev() {
67 }
68
OnFileCanReadWithoutBlocking(int fd)69 void TabletEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
70 TRACE_EVENT1("evdev",
71 "TabletEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
72 fd);
73
74 input_event inputs[4];
75 ssize_t read_size = read(fd, inputs, sizeof(inputs));
76 if (read_size < 0) {
77 if (errno == EINTR || errno == EAGAIN)
78 return;
79 if (errno != ENODEV)
80 PLOG(ERROR) << "error reading device " << path_.value();
81 Stop();
82 return;
83 }
84
85 if (!IsEnabled())
86 return;
87
88 DCHECK_EQ(read_size % sizeof(*inputs), 0u);
89 ProcessEvents(inputs, read_size / sizeof(*inputs));
90 }
91
ProcessEvents(const input_event * inputs,int count)92 void TabletEventConverterEvdev::ProcessEvents(const input_event* inputs,
93 int count) {
94 for (int i = 0; i < count; ++i) {
95 const input_event& input = inputs[i];
96 switch (input.type) {
97 case EV_KEY:
98 ConvertKeyEvent(input);
99 break;
100 case EV_ABS:
101 ConvertAbsEvent(input);
102 break;
103 case EV_SYN:
104 FlushEvents(input);
105 break;
106 }
107 }
108 }
109
ConvertKeyEvent(const input_event & input)110 void TabletEventConverterEvdev::ConvertKeyEvent(const input_event& input) {
111 // Only handle other events if we have a stylus in proximity
112 if (input.code >= BTN_TOOL_PEN && input.code <= BTN_TOOL_LENS) {
113 if (input.value == 1)
114 stylus_ = input.code;
115 else if (input.value == 0)
116 stylus_ = 0;
117 else
118 LOG(WARNING) << "Unexpected value: " << input.value
119 << " for code: " << input.code;
120 }
121
122 if (input.code >= BTN_TOUCH && input.code <= BTN_STYLUS2) {
123 DispatchMouseButton(input);
124 return;
125 }
126 }
127
ConvertAbsEvent(const input_event & input)128 void TabletEventConverterEvdev::ConvertAbsEvent(const input_event& input) {
129 if (!cursor_)
130 return;
131
132 switch (input.code) {
133 case ABS_X:
134 x_abs_location_ = input.value;
135 abs_value_dirty_ = true;
136 break;
137 case ABS_Y:
138 y_abs_location_ = input.value;
139 abs_value_dirty_ = true;
140 break;
141 case ABS_TILT_X:
142 tilt_x_ = ScaleTilt(input.value, tilt_x_min_, tilt_x_range_);
143 abs_value_dirty_ = true;
144 break;
145 case ABS_TILT_Y:
146 tilt_y_ = ScaleTilt(input.value, tilt_y_min_, tilt_y_range_);
147 abs_value_dirty_ = true;
148 break;
149 case ABS_PRESSURE:
150 pressure_ = (float)input.value / pressure_max_;
151 abs_value_dirty_ = true;
152 break;
153 }
154 }
155
UpdateCursor()156 void TabletEventConverterEvdev::UpdateCursor() {
157 gfx::Rect confined_bounds = cursor_->GetCursorConfinedBounds();
158
159 int x =
160 ((x_abs_location_ - x_abs_min_) * confined_bounds.width()) / x_abs_range_;
161 int y = ((y_abs_location_ - y_abs_min_) * confined_bounds.height()) /
162 y_abs_range_;
163
164 x += confined_bounds.x();
165 y += confined_bounds.y();
166
167 cursor_->MoveCursorTo(gfx::PointF(x, y));
168 }
169
DispatchMouseButton(const input_event & input)170 void TabletEventConverterEvdev::DispatchMouseButton(const input_event& input) {
171 if (!cursor_)
172 return;
173
174 unsigned int button;
175 // These are the same as X11 behaviour
176 if (input.code == BTN_TOUCH) {
177 button = BTN_LEFT;
178 } else if (input.code == BTN_STYLUS2) {
179 button = BTN_RIGHT;
180 } else if (input.code == BTN_STYLUS) {
181 if (one_side_btn_pen_)
182 button = BTN_RIGHT;
183 else
184 button = BTN_MIDDLE;
185 } else {
186 return;
187 }
188
189 if (abs_value_dirty_) {
190 UpdateCursor();
191 abs_value_dirty_ = false;
192 }
193
194 bool down = input.value;
195
196 dispatcher_->DispatchMouseButtonEvent(MouseButtonEventParams(
197 input_device_.id, EF_NONE, cursor_->GetLocation(), button, down,
198 false /* allow_remap */,
199 PointerDetails(GetToolType(stylus_), /* pointer_id*/ 0,
200 /* radius_x */ 0.0f, /* radius_y */ 0.0f, pressure_,
201 /* twist */ 0.0f, tilt_x_, tilt_y_),
202 TimeTicksFromInputEvent(input)));
203 }
204
FlushEvents(const input_event & input)205 void TabletEventConverterEvdev::FlushEvents(const input_event& input) {
206 if (!cursor_)
207 return;
208
209 // Prevent propagation of invalid data on stylus lift off
210 if (stylus_ == 0) {
211 abs_value_dirty_ = false;
212 return;
213 }
214
215 if (!abs_value_dirty_)
216 return;
217
218 UpdateCursor();
219
220 dispatcher_->DispatchMouseMoveEvent(MouseMoveEventParams(
221 input_device_.id, EF_NONE, cursor_->GetLocation(),
222 PointerDetails(GetToolType(stylus_), /* pointer_id*/ 0,
223 /* radius_x */ 0.0f, /* radius_y */ 0.0f, pressure_,
224 /* twist */ 0.0f, tilt_x_, tilt_y_),
225 TimeTicksFromInputEvent(input)));
226
227 abs_value_dirty_ = false;
228 }
229
230 } // namespace ui
231