Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / ui / events / ozone / evdev / touch_event_converter_evdev.cc
blob47af1602d3beb3fa83f54c204944fc0c8bd79a3e
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"
7 #include <errno.h>
8 #include <fcntl.h>
9 #include <linux/input.h>
10 #include <poll.h>
11 #include <stdio.h>
12 #include <unistd.h>
14 #include <cmath>
15 #include <limits>
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"
40 namespace {
42 const int kMaxTrackingId = 0xffff; // TRKID_MAX in kernel.
44 struct TouchCalibration {
45 int bezel_left;
46 int bezel_right;
47 int bezel_top;
48 int bezel_bottom;
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) {
69 switch (code) {
70 case ABS_X:
71 return ABS_MT_POSITION_X;
72 case ABS_Y:
73 return ABS_MT_POSITION_Y;
74 case ABS_PRESSURE:
75 return ABS_MT_PRESSURE;
76 case ABS_DISTANCE:
77 return ABS_MT_DISTANCE;
78 default:
79 return -1;
83 const int kTrackingIdForUnusedSlot = -1;
85 } // namespace
87 namespace ui {
89 TouchEventConverterEvdev::TouchEventConverterEvdev(
90 int fd,
91 base::FilePath path,
92 int id,
93 const EventDeviceInfo& devinfo,
94 DeviceEventDispatcherEvdev* dispatcher)
95 : EventConverterEvdev(fd,
96 path,
97 id,
98 devinfo.device_type(),
99 devinfo.name(),
100 devinfo.vendor_id(),
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();
116 if (has_mt_) {
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;
123 touch_points_ =
124 std::min<int>(info.GetAbsMaximum(ABS_MT_SLOT) + 1, kNumTouchEvdevSlots);
125 current_slot_ = info.GetAbsValue(ABS_MT_SLOT);
126 } else {
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;
133 touch_points_ = 1;
134 current_slot_ = 0;
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,
152 cal.bezel_bottom);
155 events_.resize(touch_points_);
157 if (has_mt_) {
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);
164 events_[i].slot = i;
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;
171 // Optional bits.
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));
179 } else {
180 // TODO(spang): Add key state to EventDeviceInfo to allow initial contact.
181 // (and make sure to take into account quirk_left_mouse_button_)
182 events_[0].x = 0;
183 events_[0].y = 0;
184 events_[0].tracking_id = kTrackingIdForUnusedSlot;
185 events_[0].touching = false;
186 events_[0].slot = 0;
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: "
197 << path_.value();
198 Stop();
199 return;
202 Initialize(info);
205 bool TouchEventConverterEvdev::HasTouchscreen() const {
206 return true;
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() {
222 ReleaseTouches();
225 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
226 TRACE_EVENT1("evdev",
227 "TouchEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
228 fd);
230 input_event inputs[kNumTouchEvdevSlots * 6 + 1];
231 ssize_t read_size = read(fd, inputs, sizeof(inputs));
232 if (read_size < 0) {
233 if (errno == EINTR || errno == EAGAIN)
234 return;
235 if (errno != ENODEV)
236 PLOG(ERROR) << "error reading device " << path_.value();
237 Stop();
238 return;
241 if (!enabled_) {
242 dropped_events_ = true;
243 return;
246 for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
247 if (!has_mt_) {
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) {
271 ProcessSyn(input);
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() << ")";
278 } else {
279 ProcessAbs(input);
281 } else if (input.type == EV_KEY) {
282 ProcessKey(input);
283 } else if (input.type == EV_MSC) {
284 // Ignored.
285 } else {
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) {
312 case BTN_TOUCH:
313 case BTN_LEFT:
314 break;
315 default:
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;
327 break;
328 case ABS_MT_TOUCH_MINOR:
329 events_[current_slot_].radius_y = input.value / 2.0f;
330 break;
331 case ABS_MT_POSITION_X:
332 events_[current_slot_].x = input.value;
333 break;
334 case ABS_MT_POSITION_Y:
335 events_[current_slot_].y = input.value;
336 break;
337 case ABS_MT_TRACKING_ID:
338 UpdateTrackingId(current_slot_, input.value);
339 break;
340 case ABS_MT_PRESSURE:
341 events_[current_slot_].pressure = ScalePressure(input.value);
342 break;
343 case ABS_MT_SLOT:
344 if (input.value >= 0 &&
345 static_cast<size_t>(input.value) < events_.size()) {
346 current_slot_ = input.value;
347 } else {
348 LOG(ERROR) << "invalid touch event index: " << input.value;
349 return;
351 break;
352 default:
353 DVLOG(5) << "unhandled code for EV_ABS: " << input.code;
354 return;
356 events_[current_slot_].altered = true;
359 void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
360 switch (input.code) {
361 case SYN_REPORT:
362 ReportEvents(EventConverterEvdev::TimeDeltaFromInputEvent(input));
363 break;
364 case SYN_DROPPED:
365 // Some buffer has overrun. We ignore all events up to and
366 // including the next SYN_REPORT.
367 dropped_events_ = true;
368 break;
369 default:
370 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
374 EventType TouchEventConverterEvdev::GetEventTypeForTouch(
375 const InProgressTouchEvdev& touch) {
376 if (touch.cancelled)
377 return ET_UNKNOWN;
379 if (touch_noise_finder_ && touch_noise_finder_->SlotHasNoise(touch.slot)) {
380 if (touch.touching && !touch.was_touching)
381 return ET_UNKNOWN;
382 return ET_TOUCH_CANCELLED;
385 if (touch.touching)
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,
396 timestamp));
399 void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) {
400 if (dropped_events_) {
401 Reinitialize();
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];
410 if (!event->altered)
411 continue;
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)
429 return;
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_;
450 return pressure;
453 int TouchEventConverterEvdev::NextTrackingId() {
454 return next_tracking_id_++ & kMaxTrackingId;
457 } // namespace ui