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