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.
5 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
9 #include <linux/input.h>
17 #include "base/bind.h"
18 #include "base/callback.h"
19 #include "base/command_line.h"
20 #include "base/logging.h"
21 #include "base/memory/scoped_vector.h"
22 #include "base/message_loop/message_loop.h"
23 #include "base/strings/string_number_conversions.h"
24 #include "base/strings/string_split.h"
25 #include "base/strings/string_util.h"
26 #include "base/strings/stringprintf.h"
27 #include "base/trace_event/trace_event.h"
28 #include "ui/events/devices/device_data_manager.h"
29 #include "ui/events/devices/device_util_linux.h"
30 #include "ui/events/event.h"
31 #include "ui/events/event_constants.h"
32 #include "ui/events/event_switches.h"
33 #include "ui/events/event_utils.h"
34 #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
35 #include "ui/events/ozone/evdev/touch_evdev_types.h"
36 #include "ui/events/ozone/evdev/touch_noise/touch_noise_finder.h"
37 #include "ui/ozone/public/input_controller.h"
38 #include "ui/ozone/public/ozone_platform.h"
42 const int kMaxTrackingId
= 0xffff; // TRKID_MAX in kernel.
44 struct TouchCalibration
{
51 void GetTouchCalibration(TouchCalibration
* cal
) {
52 std::vector
<std::string
> parts
= base::SplitString(
53 base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
54 switches::kTouchCalibration
),
55 ",", base::KEEP_WHITESPACE
, base::SPLIT_WANT_NONEMPTY
);
56 if (parts
.size() >= 4) {
57 if (!base::StringToInt(parts
[0], &cal
->bezel_left
))
58 LOG(ERROR
) << "Incorrect left border calibration value passed.";
59 if (!base::StringToInt(parts
[1], &cal
->bezel_right
))
60 LOG(ERROR
) << "Incorrect right border calibration value passed.";
61 if (!base::StringToInt(parts
[2], &cal
->bezel_top
))
62 LOG(ERROR
) << "Incorrect top border calibration value passed.";
63 if (!base::StringToInt(parts
[3], &cal
->bezel_bottom
))
64 LOG(ERROR
) << "Incorrect bottom border calibration value passed.";
68 int32_t AbsCodeToMtCode(int32_t code
) {
71 return ABS_MT_POSITION_X
;
73 return ABS_MT_POSITION_Y
;
75 return ABS_MT_PRESSURE
;
77 return ABS_MT_DISTANCE
;
83 const int kTrackingIdForUnusedSlot
= -1;
89 TouchEventConverterEvdev::TouchEventConverterEvdev(
93 const EventDeviceInfo
& devinfo
,
94 DeviceEventDispatcherEvdev
* dispatcher
)
95 : EventConverterEvdev(fd
,
98 devinfo
.device_type(),
101 devinfo
.product_id()),
102 dispatcher_(dispatcher
) {
103 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
104 switches::kExtraTouchNoiseFiltering
)) {
105 touch_noise_finder_
.reset(new TouchNoiseFinder
);
107 touch_evdev_debug_buffer_
.Initialize(devinfo
);
110 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
113 void TouchEventConverterEvdev::Initialize(const EventDeviceInfo
& info
) {
114 has_mt_
= info
.HasMultitouch();
117 pressure_min_
= info
.GetAbsMinimum(ABS_MT_PRESSURE
);
118 pressure_max_
= info
.GetAbsMaximum(ABS_MT_PRESSURE
);
119 x_min_tuxels_
= info
.GetAbsMinimum(ABS_MT_POSITION_X
);
120 x_num_tuxels_
= info
.GetAbsMaximum(ABS_MT_POSITION_X
) - x_min_tuxels_
+ 1;
121 y_min_tuxels_
= info
.GetAbsMinimum(ABS_MT_POSITION_Y
);
122 y_num_tuxels_
= info
.GetAbsMaximum(ABS_MT_POSITION_Y
) - y_min_tuxels_
+ 1;
124 std::min
<int>(info
.GetAbsMaximum(ABS_MT_SLOT
) + 1, kNumTouchEvdevSlots
);
125 current_slot_
= info
.GetAbsValue(ABS_MT_SLOT
);
127 pressure_min_
= info
.GetAbsMinimum(ABS_PRESSURE
);
128 pressure_max_
= info
.GetAbsMaximum(ABS_PRESSURE
);
129 x_min_tuxels_
= info
.GetAbsMinimum(ABS_X
);
130 x_num_tuxels_
= info
.GetAbsMaximum(ABS_X
) - x_min_tuxels_
+ 1;
131 y_min_tuxels_
= info
.GetAbsMinimum(ABS_Y
);
132 y_num_tuxels_
= info
.GetAbsMaximum(ABS_Y
) - y_min_tuxels_
+ 1;
137 quirk_left_mouse_button_
=
138 !has_mt_
&& !info
.HasKeyEvent(BTN_TOUCH
) && info
.HasKeyEvent(BTN_LEFT
);
140 // Apply --touch-calibration.
141 if (type() == INPUT_DEVICE_INTERNAL
) {
142 TouchCalibration cal
= {};
143 GetTouchCalibration(&cal
);
144 x_min_tuxels_
+= cal
.bezel_left
;
145 x_num_tuxels_
-= cal
.bezel_left
+ cal
.bezel_right
;
146 y_min_tuxels_
+= cal
.bezel_top
;
147 y_num_tuxels_
-= cal
.bezel_top
+ cal
.bezel_bottom
;
149 VLOG(1) << "applying touch calibration: "
150 << base::StringPrintf("[%d, %d, %d, %d]", cal
.bezel_left
,
151 cal
.bezel_right
, cal
.bezel_top
,
155 events_
.resize(touch_points_
);
158 for (size_t i
= 0; i
< events_
.size(); ++i
) {
159 events_
[i
].x
= info
.GetAbsMtSlotValueWithDefault(ABS_MT_POSITION_X
, i
, 0);
160 events_
[i
].y
= info
.GetAbsMtSlotValueWithDefault(ABS_MT_POSITION_Y
, i
, 0);
161 events_
[i
].tracking_id
= info
.GetAbsMtSlotValueWithDefault(
162 ABS_MT_TRACKING_ID
, i
, kTrackingIdForUnusedSlot
);
163 events_
[i
].touching
= (events_
[i
].tracking_id
>= 0);
166 // Dirty the slot so we'll update the consumer at the first opportunity.
167 // We can't dispatch here as this is currently called on the worker pool.
168 // TODO(spang): Move initialization off worker pool.
169 events_
[i
].altered
= true;
172 events_
[i
].radius_x
=
173 info
.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MAJOR
, i
, 0) / 2.0f
;
174 events_
[i
].radius_y
=
175 info
.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MINOR
, i
, 0) / 2.0f
;
176 events_
[i
].pressure
= ScalePressure(
177 info
.GetAbsMtSlotValueWithDefault(ABS_MT_PRESSURE
, i
, 0));
180 // TODO(spang): Add key state to EventDeviceInfo to allow initial contact.
181 // (and make sure to take into account quirk_left_mouse_button_)
184 events_
[0].tracking_id
= kTrackingIdForUnusedSlot
;
185 events_
[0].touching
= false;
187 events_
[0].radius_x
= 0;
188 events_
[0].radius_y
= 0;
189 events_
[0].pressure
= 0;
193 void TouchEventConverterEvdev::Reinitialize() {
194 EventDeviceInfo info
;
195 if (!info
.Initialize(fd_
, path_
)) {
196 LOG(ERROR
) << "Failed to synchronize state for touch device: "
205 bool TouchEventConverterEvdev::HasTouchscreen() const {
209 gfx::Size
TouchEventConverterEvdev::GetTouchscreenSize() const {
210 return gfx::Size(x_num_tuxels_
, y_num_tuxels_
);
213 int TouchEventConverterEvdev::GetTouchPoints() const {
214 return touch_points_
;
217 void TouchEventConverterEvdev::OnEnabled() {
218 ReportEvents(EventTimeForNow());
221 void TouchEventConverterEvdev::OnDisabled() {
225 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd
) {
226 TRACE_EVENT1("evdev",
227 "TouchEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
230 input_event inputs
[kNumTouchEvdevSlots
* 6 + 1];
231 ssize_t read_size
= read(fd
, inputs
, sizeof(inputs
));
233 if (errno
== EINTR
|| errno
== EAGAIN
)
236 PLOG(ERROR
) << "error reading device " << path_
.value();
242 dropped_events_
= true;
246 for (unsigned i
= 0; i
< read_size
/ sizeof(*inputs
); i
++) {
248 // Emulate the device as an MT device with only 1 slot by inserting extra
249 // MT protocol events in the stream.
250 EmulateMultitouchEvent(inputs
[i
]);
253 ProcessMultitouchEvent(inputs
[i
]);
257 void TouchEventConverterEvdev::DumpTouchEventLog(const char* filename
) {
258 touch_evdev_debug_buffer_
.DumpLog(filename
);
261 void TouchEventConverterEvdev::SetTouchEventLoggingEnabled(bool enabled
) {
262 touch_logging_enabled_
= enabled
;
265 void TouchEventConverterEvdev::ProcessMultitouchEvent(
266 const input_event
& input
) {
267 if (touch_logging_enabled_
)
268 touch_evdev_debug_buffer_
.ProcessEvent(current_slot_
, &input
);
270 if (input
.type
== EV_SYN
) {
272 } else if (dropped_events_
) {
273 // Do nothing. This branch indicates we have lost sync with the driver.
274 } else if (input
.type
== EV_ABS
) {
275 if (events_
.size() <= current_slot_
) {
276 LOG(ERROR
) << "current_slot_ (" << current_slot_
277 << ") >= events_.size() (" << events_
.size() << ")";
281 } else if (input
.type
== EV_KEY
) {
283 } else if (input
.type
== EV_MSC
) {
286 NOTIMPLEMENTED() << "invalid type: " << input
.type
;
290 void TouchEventConverterEvdev::EmulateMultitouchEvent(
291 const input_event
& event
) {
292 input_event emulated_event
= event
;
294 if (event
.type
== EV_ABS
) {
295 emulated_event
.code
= AbsCodeToMtCode(event
.code
);
296 if (emulated_event
.code
>= 0)
297 ProcessMultitouchEvent(emulated_event
);
298 } else if (event
.type
== EV_KEY
) {
299 if (event
.code
== BTN_TOUCH
||
300 (quirk_left_mouse_button_
&& event
.code
== BTN_LEFT
)) {
301 emulated_event
.type
= EV_ABS
;
302 emulated_event
.code
= ABS_MT_TRACKING_ID
;
303 emulated_event
.value
=
304 event
.value
? NextTrackingId() : kTrackingIdForUnusedSlot
;
305 ProcessMultitouchEvent(emulated_event
);
310 void TouchEventConverterEvdev::ProcessKey(const input_event
& input
) {
311 switch (input
.code
) {
316 NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input
.code
;
320 void TouchEventConverterEvdev::ProcessAbs(const input_event
& input
) {
321 switch (input
.code
) {
322 case ABS_MT_TOUCH_MAJOR
:
323 // TODO(spang): If we have all of major, minor, and orientation,
324 // we can scale the ellipse correctly. However on the Pixel we get
325 // neither minor nor orientation, so this is all we can do.
326 events_
[current_slot_
].radius_x
= input
.value
/ 2.0f
;
328 case ABS_MT_TOUCH_MINOR
:
329 events_
[current_slot_
].radius_y
= input
.value
/ 2.0f
;
331 case ABS_MT_POSITION_X
:
332 events_
[current_slot_
].x
= input
.value
;
334 case ABS_MT_POSITION_Y
:
335 events_
[current_slot_
].y
= input
.value
;
337 case ABS_MT_TRACKING_ID
:
338 UpdateTrackingId(current_slot_
, input
.value
);
340 case ABS_MT_PRESSURE
:
341 events_
[current_slot_
].pressure
= ScalePressure(input
.value
);
344 if (input
.value
>= 0 &&
345 static_cast<size_t>(input
.value
) < events_
.size()) {
346 current_slot_
= input
.value
;
348 LOG(ERROR
) << "invalid touch event index: " << input
.value
;
353 DVLOG(5) << "unhandled code for EV_ABS: " << input
.code
;
356 events_
[current_slot_
].altered
= true;
359 void TouchEventConverterEvdev::ProcessSyn(const input_event
& input
) {
360 switch (input
.code
) {
362 ReportEvents(EventConverterEvdev::TimeDeltaFromInputEvent(input
));
365 // Some buffer has overrun. We ignore all events up to and
366 // including the next SYN_REPORT.
367 dropped_events_
= true;
370 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input
.code
;
374 EventType
TouchEventConverterEvdev::GetEventTypeForTouch(
375 const InProgressTouchEvdev
& touch
) {
379 if (touch_noise_finder_
&& touch_noise_finder_
->SlotHasNoise(touch
.slot
)) {
380 if (touch
.touching
&& !touch
.was_touching
)
382 return ET_TOUCH_CANCELLED
;
386 return touch
.was_touching
? ET_TOUCH_MOVED
: ET_TOUCH_PRESSED
;
387 return touch
.was_touching
? ET_TOUCH_RELEASED
: ET_UNKNOWN
;
390 void TouchEventConverterEvdev::ReportEvent(const InProgressTouchEvdev
& event
,
391 EventType event_type
,
392 const base::TimeDelta
& timestamp
) {
393 dispatcher_
->DispatchTouchEvent(TouchEventParams(
394 input_device_
.id
, event
.slot
, event_type
, gfx::PointF(event
.x
, event
.y
),
395 gfx::Vector2dF(event
.radius_x
, event
.radius_y
), event
.pressure
,
399 void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta
) {
400 if (dropped_events_
) {
402 dropped_events_
= false;
405 if (touch_noise_finder_
)
406 touch_noise_finder_
->HandleTouches(events_
, delta
);
408 for (size_t i
= 0; i
< events_
.size(); i
++) {
409 InProgressTouchEvdev
* event
= &events_
[i
];
413 EventType event_type
= GetEventTypeForTouch(*event
);
414 if (event_type
== ET_UNKNOWN
|| event_type
== ET_TOUCH_CANCELLED
)
415 event
->cancelled
= true;
417 if (event_type
!= ET_UNKNOWN
)
418 ReportEvent(*event
, event_type
, delta
);
420 event
->was_touching
= event
->touching
;
421 event
->altered
= false;
425 void TouchEventConverterEvdev::UpdateTrackingId(int slot
, int tracking_id
) {
426 InProgressTouchEvdev
* event
= &events_
[slot
];
428 if (event
->tracking_id
== tracking_id
)
431 event
->tracking_id
= tracking_id
;
432 event
->touching
= (tracking_id
>= 0);
433 event
->altered
= true;
435 if (tracking_id
>= 0)
436 event
->cancelled
= false;
439 void TouchEventConverterEvdev::ReleaseTouches() {
440 for (size_t slot
= 0; slot
< events_
.size(); slot
++)
441 UpdateTrackingId(slot
, kTrackingIdForUnusedSlot
);
443 ReportEvents(EventTimeForNow());
446 float TouchEventConverterEvdev::ScalePressure(int32_t value
) {
447 float pressure
= value
- pressure_min_
;
448 if (pressure_max_
- pressure_min_
)
449 pressure
/= pressure_max_
- pressure_min_
;
453 int TouchEventConverterEvdev::NextTrackingId() {
454 return next_tracking_id_
++ & kMaxTrackingId
;