base: Change DCHECK_IS_ON to a macro DCHECK_IS_ON().
[chromium-blink-merge.git] / ui / events / ozone / evdev / touch_event_converter_evdev.cc
blobc30bba367942224f948ede01e0b67bf3b2c9bdc3
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"
32 namespace {
34 struct TouchCalibration {
35 int bezel_left;
36 int bezel_right;
37 int bezel_top;
38 int bezel_bottom;
41 void GetTouchCalibration(TouchCalibration* cal) {
42 std::vector<std::string> parts;
43 if (Tokenize(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
44 switches::kTouchCalibration),
45 ",", &parts) >= 4) {
46 if (!base::StringToInt(parts[0], &cal->bezel_left))
47 DLOG(ERROR) << "Incorrect left border calibration value passed.";
48 if (!base::StringToInt(parts[1], &cal->bezel_right))
49 DLOG(ERROR) << "Incorrect right border calibration value passed.";
50 if (!base::StringToInt(parts[2], &cal->bezel_top))
51 DLOG(ERROR) << "Incorrect top border calibration value passed.";
52 if (!base::StringToInt(parts[3], &cal->bezel_bottom))
53 DLOG(ERROR) << "Incorrect bottom border calibration value passed.";
57 } // namespace
59 namespace ui {
61 TouchEventConverterEvdev::InProgressEvents::InProgressEvents()
62 : x_(0),
63 y_(0),
64 id_(-1),
65 finger_(-1),
66 type_(ET_UNKNOWN),
67 radius_x_(0),
68 radius_y_(0),
69 pressure_(0) {
72 TouchEventConverterEvdev::TouchEventConverterEvdev(
73 int fd,
74 base::FilePath path,
75 int id,
76 InputDeviceType type,
77 const EventDispatchCallback& callback)
78 : EventConverterEvdev(fd, path, id, type),
79 callback_(callback),
80 syn_dropped_(false),
81 is_type_a_(false),
82 current_slot_(0) {
85 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
86 Stop();
87 close(fd_);
90 void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
91 pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE);
92 pressure_max_ = info.GetAbsMaximum(ABS_MT_PRESSURE);
93 x_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_X);
94 x_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_X) - x_min_tuxels_ + 1;
95 y_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_Y);
96 y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1;
98 // Apply --touch-calibration.
99 if (type() == INPUT_DEVICE_INTERNAL) {
100 TouchCalibration cal = {};
101 GetTouchCalibration(&cal);
102 x_min_tuxels_ += cal.bezel_left;
103 x_num_tuxels_ -= cal.bezel_left + cal.bezel_right;
104 y_min_tuxels_ += cal.bezel_top;
105 y_num_tuxels_ -= cal.bezel_top + cal.bezel_bottom;
107 VLOG(1) << "applying touch calibration: "
108 << base::StringPrintf("[%d, %d, %d, %d]", cal.bezel_left,
109 cal.bezel_right, cal.bezel_top,
110 cal.bezel_bottom);
113 native_size_ = gfx::Size(x_num_tuxels_, y_num_tuxels_);
115 for (int i = 0;
116 i < std::min<int>(info.GetAbsMaximum(ABS_MT_SLOT) + 1, MAX_FINGERS);
117 ++i) {
118 events_[i].finger_ = info.GetSlotValue(ABS_MT_TRACKING_ID, i);
119 events_[i].type_ =
120 events_[i].finger_ < 0 ? ET_TOUCH_RELEASED : ET_TOUCH_PRESSED;
121 events_[i].x_ = info.GetSlotValue(ABS_MT_POSITION_X, i);
122 events_[i].y_ = info.GetSlotValue(ABS_MT_POSITION_Y, i);
123 events_[i].radius_x_ = info.GetSlotValue(ABS_MT_TOUCH_MAJOR, i);
124 events_[i].radius_y_ = info.GetSlotValue(ABS_MT_TOUCH_MINOR, i);
125 events_[i].pressure_ = info.GetSlotValue(ABS_MT_PRESSURE, i);
129 bool TouchEventConverterEvdev::Reinitialize() {
130 EventDeviceInfo info;
131 if (info.Initialize(fd_)) {
132 Initialize(info);
133 return true;
135 return false;
138 bool TouchEventConverterEvdev::HasTouchscreen() const {
139 return true;
142 gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const {
143 return native_size_;
146 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
147 input_event inputs[MAX_FINGERS * 6 + 1];
148 ssize_t read_size = read(fd, inputs, sizeof(inputs));
149 if (read_size < 0) {
150 if (errno == EINTR || errno == EAGAIN)
151 return;
152 if (errno != ENODEV)
153 PLOG(ERROR) << "error reading device " << path_.value();
154 Stop();
155 return;
158 for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
159 ProcessInputEvent(inputs[i]);
163 void TouchEventConverterEvdev::ProcessInputEvent(const input_event& input) {
164 if (input.type == EV_SYN) {
165 ProcessSyn(input);
166 } else if(syn_dropped_) {
167 // Do nothing. This branch indicates we have lost sync with the driver.
168 } else if (input.type == EV_ABS) {
169 if (current_slot_ >= MAX_FINGERS) {
170 LOG(ERROR) << "too many touch events: " << current_slot_;
171 return;
173 ProcessAbs(input);
174 } else if (input.type == EV_KEY) {
175 switch (input.code) {
176 case BTN_TOUCH:
177 break;
178 default:
179 NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
181 } else {
182 NOTIMPLEMENTED() << "invalid type: " << input.type;
186 void TouchEventConverterEvdev::ProcessAbs(const input_event& input) {
187 switch (input.code) {
188 case ABS_MT_TOUCH_MAJOR:
189 altered_slots_.set(current_slot_);
190 // TODO(spang): If we have all of major, minor, and orientation,
191 // we can scale the ellipse correctly. However on the Pixel we get
192 // neither minor nor orientation, so this is all we can do.
193 events_[current_slot_].radius_x_ = input.value / 2.0f;
194 break;
195 case ABS_MT_TOUCH_MINOR:
196 altered_slots_.set(current_slot_);
197 events_[current_slot_].radius_y_ = input.value / 2.0f;
198 break;
199 case ABS_MT_POSITION_X:
200 altered_slots_.set(current_slot_);
201 events_[current_slot_].x_ = input.value;
202 break;
203 case ABS_MT_POSITION_Y:
204 altered_slots_.set(current_slot_);
205 events_[current_slot_].y_ = input.value;
206 break;
207 case ABS_MT_TRACKING_ID:
208 altered_slots_.set(current_slot_);
209 if (input.value < 0) {
210 events_[current_slot_].type_ = ET_TOUCH_RELEASED;
211 } else {
212 events_[current_slot_].finger_ = input.value;
213 events_[current_slot_].type_ = ET_TOUCH_PRESSED;
215 break;
216 case ABS_MT_PRESSURE:
217 altered_slots_.set(current_slot_);
218 events_[current_slot_].pressure_ = input.value - pressure_min_;
219 events_[current_slot_].pressure_ /= pressure_max_ - pressure_min_;
220 break;
221 case ABS_MT_SLOT:
222 current_slot_ = input.value;
223 altered_slots_.set(current_slot_);
224 break;
225 default:
226 DVLOG(5) << "unhandled code for EV_ABS: " << input.code;
230 void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
231 switch (input.code) {
232 case SYN_REPORT:
233 if (syn_dropped_) {
234 // Have to re-initialize.
235 if (Reinitialize()) {
236 syn_dropped_ = false;
237 altered_slots_.reset();
238 } else {
239 LOG(ERROR) << "failed to re-initialize device info";
241 } else {
242 ReportEvents(base::TimeDelta::FromMicroseconds(
243 input.time.tv_sec * 1000000 + input.time.tv_usec));
245 if (is_type_a_)
246 current_slot_ = 0;
247 break;
248 case SYN_MT_REPORT:
249 // For type A devices, we just get a stream of all current contacts,
250 // in some arbitrary order.
251 events_[current_slot_++].type_ = ET_TOUCH_PRESSED;
252 is_type_a_ = true;
253 break;
254 case SYN_DROPPED:
255 // Some buffer has overrun. We ignore all events up to and
256 // including the next SYN_REPORT.
257 syn_dropped_ = true;
258 break;
259 default:
260 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
264 void TouchEventConverterEvdev::ReportEvent(int touch_id,
265 const InProgressEvents& event, const base::TimeDelta& delta) {
266 float x = event.x_;
267 float y = event.y_;
269 double radius_x = event.radius_x_;
270 double radius_y = event.radius_y_;
272 // Transform the event according (this is used to align touches
273 // to the image based on display mode).
274 DeviceDataManager::GetInstance()->ApplyTouchTransformer(
275 id_, &x, &y);
276 DeviceDataManager::GetInstance()->ApplyTouchRadiusScale(
277 id_, &radius_x);
278 DeviceDataManager::GetInstance()->ApplyTouchRadiusScale(
279 id_, &radius_y);
281 gfx::PointF location(x, y);
283 scoped_ptr<TouchEvent> touch_event(
284 new TouchEvent(event.type_, location,
285 /* flags */ 0,
286 /* touch_id */ touch_id,
287 delta,
288 /* radius_x */ radius_x,
289 /* radius_y */ radius_y,
290 /* angle */ 0.,
291 event.pressure_));
292 touch_event->set_source_device_id(id_);
293 callback_.Run(touch_event.Pass());
296 void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) {
297 for (int i = 0; i < MAX_FINGERS; i++) {
298 if (altered_slots_[i]) {
299 ReportEvent(i, events_[i], delta);
301 // Subsequent events for this finger will be touch-move until it
302 // is released.
303 events_[i].type_ = ET_TOUCH_MOVED;
306 altered_slots_.reset();
309 } // namespace ui