Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / ui / events / ozone / evdev / touch_event_converter_evdev.cc
blobea95b12b94f4e23313d034242dfce54c47496a5a
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_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "ui/events/devices/device_data_manager.h"
27 #include "ui/events/devices/device_util_linux.h"
28 #include "ui/events/event.h"
29 #include "ui/events/event_constants.h"
30 #include "ui/events/event_switches.h"
31 #include "ui/events/event_utils.h"
32 #include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
33 #include "ui/events/ozone/evdev/touch_evdev_types.h"
34 #include "ui/events/ozone/evdev/touch_noise/touch_noise_finder.h"
36 namespace {
38 const int kMaxTrackingId = 0xffff; // TRKID_MAX in kernel.
40 struct TouchCalibration {
41 int bezel_left;
42 int bezel_right;
43 int bezel_top;
44 int bezel_bottom;
47 void GetTouchCalibration(TouchCalibration* cal) {
48 std::vector<std::string> parts;
49 if (Tokenize(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
50 switches::kTouchCalibration),
51 ",", &parts) >= 4) {
52 if (!base::StringToInt(parts[0], &cal->bezel_left))
53 LOG(ERROR) << "Incorrect left border calibration value passed.";
54 if (!base::StringToInt(parts[1], &cal->bezel_right))
55 LOG(ERROR) << "Incorrect right border calibration value passed.";
56 if (!base::StringToInt(parts[2], &cal->bezel_top))
57 LOG(ERROR) << "Incorrect top border calibration value passed.";
58 if (!base::StringToInt(parts[3], &cal->bezel_bottom))
59 LOG(ERROR) << "Incorrect bottom border calibration value passed.";
63 int32_t AbsCodeToMtCode(int32_t code) {
64 switch (code) {
65 case ABS_X:
66 return ABS_MT_POSITION_X;
67 case ABS_Y:
68 return ABS_MT_POSITION_Y;
69 case ABS_PRESSURE:
70 return ABS_MT_PRESSURE;
71 case ABS_DISTANCE:
72 return ABS_MT_DISTANCE;
73 default:
74 return -1;
78 const int kTrackingIdForUnusedSlot = -1;
80 } // namespace
82 namespace ui {
84 TouchEventConverterEvdev::TouchEventConverterEvdev(
85 int fd,
86 base::FilePath path,
87 int id,
88 InputDeviceType type,
89 DeviceEventDispatcherEvdev* dispatcher)
90 : EventConverterEvdev(fd, path, id, type),
91 dispatcher_(dispatcher),
92 syn_dropped_(false),
93 has_mt_(false),
94 touch_points_(0),
95 next_tracking_id_(0),
96 current_slot_(0) {
97 if (base::CommandLine::ForCurrentProcess()->HasSwitch(
98 switches::kExtraTouchNoiseFiltering)) {
99 touch_noise_finder_.reset(new TouchNoiseFinder);
103 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
104 Stop();
105 close(fd_);
108 void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
109 has_mt_ = info.HasMultitouch();
111 if (has_mt_) {
112 pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE);
113 pressure_max_ = info.GetAbsMaximum(ABS_MT_PRESSURE);
114 x_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_X);
115 x_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_X) - x_min_tuxels_ + 1;
116 y_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_Y);
117 y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1;
118 touch_points_ =
119 std::min<int>(info.GetAbsMaximum(ABS_MT_SLOT) + 1, kNumTouchEvdevSlots);
120 current_slot_ = info.GetAbsValue(ABS_MT_SLOT);
121 } else {
122 pressure_min_ = info.GetAbsMinimum(ABS_PRESSURE);
123 pressure_max_ = info.GetAbsMaximum(ABS_PRESSURE);
124 x_min_tuxels_ = info.GetAbsMinimum(ABS_X);
125 x_num_tuxels_ = info.GetAbsMaximum(ABS_X) - x_min_tuxels_ + 1;
126 y_min_tuxels_ = info.GetAbsMinimum(ABS_Y);
127 y_num_tuxels_ = info.GetAbsMaximum(ABS_Y) - y_min_tuxels_ + 1;
128 touch_points_ = 1;
129 current_slot_ = 0;
132 // Apply --touch-calibration.
133 if (type() == INPUT_DEVICE_INTERNAL) {
134 TouchCalibration cal = {};
135 GetTouchCalibration(&cal);
136 x_min_tuxels_ += cal.bezel_left;
137 x_num_tuxels_ -= cal.bezel_left + cal.bezel_right;
138 y_min_tuxels_ += cal.bezel_top;
139 y_num_tuxels_ -= cal.bezel_top + cal.bezel_bottom;
141 VLOG(1) << "applying touch calibration: "
142 << base::StringPrintf("[%d, %d, %d, %d]", cal.bezel_left,
143 cal.bezel_right, cal.bezel_top,
144 cal.bezel_bottom);
147 events_.resize(touch_points_);
149 if (has_mt_) {
150 for (size_t i = 0; i < events_.size(); ++i) {
151 events_[i].x = info.GetAbsMtSlotValueWithDefault(ABS_MT_POSITION_X, i, 0);
152 events_[i].y = info.GetAbsMtSlotValueWithDefault(ABS_MT_POSITION_Y, i, 0);
153 events_[i].tracking_id = info.GetAbsMtSlotValueWithDefault(
154 ABS_MT_TRACKING_ID, i, kTrackingIdForUnusedSlot);
155 events_[i].touching = (events_[i].tracking_id >= 0);
156 events_[i].slot = i;
158 // Dirty the slot so we'll update the consumer at the first opportunity.
159 // We can't dispatch here as this is currently called on the worker pool.
160 // TODO(spang): Move initialization off worker pool.
161 events_[i].altered = true;
163 // Optional bits.
164 events_[i].radius_x =
165 info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MAJOR, i, 0) / 2.0f;
166 events_[i].radius_y =
167 info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MINOR, i, 0) / 2.0f;
168 events_[i].pressure = ScalePressure(
169 info.GetAbsMtSlotValueWithDefault(ABS_MT_PRESSURE, i, 0));
171 } else {
172 // TODO(spang): Add key state to EventDeviceInfo to allow initial contact.
173 events_[0].x = 0;
174 events_[0].y = 0;
175 events_[0].tracking_id = kTrackingIdForUnusedSlot;
176 events_[0].touching = false;
177 events_[0].slot = 0;
178 events_[0].radius_x = 0;
179 events_[0].radius_y = 0;
180 events_[0].pressure = 0;
184 bool TouchEventConverterEvdev::Reinitialize() {
185 EventDeviceInfo info;
186 if (info.Initialize(fd_)) {
187 Initialize(info);
188 return true;
190 return false;
193 bool TouchEventConverterEvdev::HasTouchscreen() const {
194 return true;
197 gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const {
198 return gfx::Size(x_num_tuxels_, y_num_tuxels_);
201 int TouchEventConverterEvdev::GetTouchPoints() const {
202 return touch_points_;
205 void TouchEventConverterEvdev::OnStopped() {
206 ReleaseTouches();
209 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
210 input_event inputs[kNumTouchEvdevSlots * 6 + 1];
211 ssize_t read_size = read(fd, inputs, sizeof(inputs));
212 if (read_size < 0) {
213 if (errno == EINTR || errno == EAGAIN)
214 return;
215 if (errno != ENODEV)
216 PLOG(ERROR) << "error reading device " << path_.value();
217 Stop();
218 return;
221 if (ignore_events_)
222 return;
224 for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
225 if (!has_mt_) {
226 // Emulate the device as an MT device with only 1 slot by inserting extra
227 // MT protocol events in the stream.
228 EmulateMultitouchEvent(inputs[i]);
231 ProcessMultitouchEvent(inputs[i]);
235 void TouchEventConverterEvdev::ProcessMultitouchEvent(
236 const input_event& input) {
237 if (input.type == EV_SYN) {
238 ProcessSyn(input);
239 } else if (syn_dropped_) {
240 // Do nothing. This branch indicates we have lost sync with the driver.
241 } else if (input.type == EV_ABS) {
242 if (events_.size() <= current_slot_) {
243 LOG(ERROR) << "current_slot_ (" << current_slot_
244 << ") >= events_.size() (" << events_.size() << ")";
245 } else {
246 ProcessAbs(input);
248 } else if (input.type == EV_KEY) {
249 ProcessKey(input);
250 } else {
251 NOTIMPLEMENTED() << "invalid type: " << input.type;
255 void TouchEventConverterEvdev::EmulateMultitouchEvent(
256 const input_event& event) {
257 input_event emulated_event = event;
259 if (event.type == EV_ABS) {
260 emulated_event.code = AbsCodeToMtCode(event.code);
261 if (emulated_event.code >= 0)
262 ProcessMultitouchEvent(emulated_event);
263 } else if (event.type == EV_KEY && event.code == BTN_TOUCH) {
264 emulated_event.type = EV_ABS;
265 emulated_event.code = ABS_MT_TRACKING_ID;
266 emulated_event.value =
267 event.value ? NextTrackingId() : kTrackingIdForUnusedSlot;
268 ProcessMultitouchEvent(emulated_event);
272 void TouchEventConverterEvdev::ProcessKey(const input_event& input) {
273 switch (input.code) {
274 case BTN_TOUCH:
275 break;
276 default:
277 NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
281 void TouchEventConverterEvdev::ProcessAbs(const input_event& input) {
282 switch (input.code) {
283 case ABS_MT_TOUCH_MAJOR:
284 // TODO(spang): If we have all of major, minor, and orientation,
285 // we can scale the ellipse correctly. However on the Pixel we get
286 // neither minor nor orientation, so this is all we can do.
287 events_[current_slot_].radius_x = input.value / 2.0f;
288 break;
289 case ABS_MT_TOUCH_MINOR:
290 events_[current_slot_].radius_y = input.value / 2.0f;
291 break;
292 case ABS_MT_POSITION_X:
293 events_[current_slot_].x = input.value;
294 break;
295 case ABS_MT_POSITION_Y:
296 events_[current_slot_].y = input.value;
297 break;
298 case ABS_MT_TRACKING_ID:
299 UpdateTrackingId(current_slot_, input.value);
300 break;
301 case ABS_MT_PRESSURE:
302 events_[current_slot_].pressure = ScalePressure(input.value);
303 break;
304 case ABS_MT_SLOT:
305 if (input.value >= 0 &&
306 static_cast<size_t>(input.value) < events_.size()) {
307 current_slot_ = input.value;
308 } else {
309 LOG(ERROR) << "invalid touch event index: " << input.value;
310 return;
312 break;
313 default:
314 DVLOG(5) << "unhandled code for EV_ABS: " << input.code;
315 return;
317 events_[current_slot_].altered = true;
320 void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
321 switch (input.code) {
322 case SYN_REPORT:
323 if (syn_dropped_) {
324 // Have to re-initialize.
325 if (Reinitialize()) {
326 syn_dropped_ = false;
327 } else {
328 LOG(ERROR) << "failed to re-initialize device info";
330 } else {
331 ReportEvents(EventConverterEvdev::TimeDeltaFromInputEvent(input));
333 break;
334 case SYN_DROPPED:
335 // Some buffer has overrun. We ignore all events up to and
336 // including the next SYN_REPORT.
337 syn_dropped_ = true;
338 break;
339 default:
340 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
344 EventType TouchEventConverterEvdev::GetEventTypeForTouch(
345 const InProgressTouchEvdev& touch) {
346 if (touch.cancelled)
347 return ET_UNKNOWN;
349 if (touch_noise_finder_ && touch_noise_finder_->SlotHasNoise(touch.slot)) {
350 if (touch.touching && !touch.was_touching)
351 return ET_UNKNOWN;
352 return ET_TOUCH_CANCELLED;
355 if (touch.touching)
356 return touch.was_touching ? ET_TOUCH_MOVED : ET_TOUCH_PRESSED;
357 return touch.was_touching ? ET_TOUCH_RELEASED : ET_UNKNOWN;
360 void TouchEventConverterEvdev::ReportEvent(const InProgressTouchEvdev& event,
361 EventType event_type,
362 const base::TimeDelta& timestamp) {
363 dispatcher_->DispatchTouchEvent(TouchEventParams(
364 id_, event.slot, event_type, gfx::PointF(event.x, event.y),
365 gfx::Vector2dF(event.radius_x, event.radius_y), event.pressure,
366 timestamp));
369 void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) {
370 if (touch_noise_finder_)
371 touch_noise_finder_->HandleTouches(events_, delta);
373 for (size_t i = 0; i < events_.size(); i++) {
374 InProgressTouchEvdev* event = &events_[i];
375 if (!event->altered)
376 continue;
378 EventType event_type = GetEventTypeForTouch(*event);
379 if (event_type == ET_UNKNOWN || event_type == ET_TOUCH_CANCELLED)
380 event->cancelled = true;
382 if (event_type != ET_UNKNOWN)
383 ReportEvent(*event, event_type, delta);
385 event->was_touching = event->touching;
386 event->altered = false;
390 void TouchEventConverterEvdev::UpdateTrackingId(int slot, int tracking_id) {
391 InProgressTouchEvdev* event = &events_[slot];
393 if (event->tracking_id == tracking_id)
394 return;
396 event->tracking_id = tracking_id;
397 event->touching = (tracking_id >= 0);
398 event->altered = true;
400 if (tracking_id >= 0)
401 event->cancelled = false;
404 void TouchEventConverterEvdev::ReleaseTouches() {
405 for (size_t slot = 0; slot < events_.size(); slot++)
406 UpdateTrackingId(slot, kTrackingIdForUnusedSlot);
408 ReportEvents(EventTimeForNow());
411 float TouchEventConverterEvdev::ScalePressure(int32_t value) {
412 float pressure = value - pressure_min_;
413 if (pressure_max_ - pressure_min_)
414 pressure /= pressure_max_ - pressure_min_;
415 return pressure;
418 int TouchEventConverterEvdev::NextTrackingId() {
419 return next_tracking_id_++ & kMaxTrackingId;
422 } // namespace ui