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_util.h"
25 #include "base/strings/stringprintf.h"
26 #include "ui/events/devices/device_util_linux.h"
27 #include "ui/events/event.h"
28 #include "ui/events/event_constants.h"
29 #include "ui/events/event_switches.h"
30 #include "ui/gfx/screen.h"
34 struct TouchCalibration {
41 void GetTouchCalibration(TouchCalibration* cal) {
42 std::vector<std::string> parts;
43 if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
44 switches::kTouchCalibration),
47 if (!base::StringToInt(parts[0], &cal->bezel_left))
48 DLOG(ERROR) << "Incorrect left border calibration value passed.";
49 if (!base::StringToInt(parts[1], &cal->bezel_right))
50 DLOG(ERROR) << "Incorrect right border calibration value passed.";
51 if (!base::StringToInt(parts[2], &cal->bezel_top))
52 DLOG(ERROR) << "Incorrect top border calibration value passed.";
53 if (!base::StringToInt(parts[3], &cal->bezel_bottom))
54 DLOG(ERROR) << "Incorrect bottom border calibration value passed.";
58 float TuxelsToPixels(float val,
63 // Map [min_tuxels, min_tuxels + num_tuxels) to
64 // [min_pixels, min_pixels + num_pixels).
65 return min_pixels + (val - min_tuxels) * num_pixels / num_tuxels;
68 float TuxelToPixelSize(float val, float num_tuxels, float num_pixels) {
69 return val * num_pixels / num_tuxels;
76 TouchEventConverterEvdev::InProgressEvents::InProgressEvents()
87 TouchEventConverterEvdev::TouchEventConverterEvdev(
91 const EventDeviceInfo& info,
92 const EventDispatchCallback& callback)
93 : EventConverterEvdev(fd, path, id),
98 is_internal_(IsTouchscreenInternal(path)) {
102 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
107 void TouchEventConverterEvdev::Init(const EventDeviceInfo& info) {
108 gfx::Screen* screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE);
110 return; // No scaling.
111 gfx::Display display = screen->GetPrimaryDisplay();
112 gfx::Size size = display.GetSizeInPixel();
114 pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE);
115 pressure_max_ = info.GetAbsMaximum(ABS_MT_PRESSURE);
116 x_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_X);
117 x_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_X) - x_min_tuxels_ + 1;
118 y_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_Y);
119 y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1;
120 native_size_ = gfx::Size(x_num_tuxels_, y_num_tuxels_);
122 // Map coordinates onto screen.
125 x_num_pixels_ = size.width();
126 y_num_pixels_ = size.height();
128 VLOG(1) << "mapping touch coordinates to screen coordinates: "
129 << base::StringPrintf("%dx%d", size.width(), size.height());
131 // Apply --touch-calibration.
132 TouchCalibration cal = {};
133 GetTouchCalibration(&cal);
134 x_min_tuxels_ += cal.bezel_left;
135 x_num_tuxels_ -= cal.bezel_left + cal.bezel_right;
136 y_min_tuxels_ += cal.bezel_top;
137 y_num_tuxels_ -= cal.bezel_top + cal.bezel_bottom;
139 VLOG(1) << "applying touch calibration: "
140 << base::StringPrintf("[%d, %d, %d, %d]",
147 i < std::min<int>(info.GetAbsMaximum(ABS_MT_SLOT) + 1, MAX_FINGERS);
149 events_[i].finger_ = info.GetSlotValue(ABS_MT_TRACKING_ID, i);
151 events_[i].finger_ < 0 ? ET_TOUCH_RELEASED : ET_TOUCH_PRESSED;
152 events_[i].x_ = info.GetSlotValue(ABS_MT_POSITION_X, i);
153 events_[i].y_ = info.GetSlotValue(ABS_MT_POSITION_Y, i);
154 events_[i].radius_x_ = info.GetSlotValue(ABS_MT_TOUCH_MAJOR, i);
155 events_[i].radius_y_ = info.GetSlotValue(ABS_MT_TOUCH_MINOR, i);
156 events_[i].pressure_ = info.GetSlotValue(ABS_MT_PRESSURE, i);
160 bool TouchEventConverterEvdev::Reinitialize() {
161 EventDeviceInfo info;
162 if (info.Initialize(fd_)) {
169 bool TouchEventConverterEvdev::HasTouchscreen() const {
173 gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const {
177 bool TouchEventConverterEvdev::IsInternal() const {
181 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
182 input_event inputs[MAX_FINGERS * 6 + 1];
183 ssize_t read_size = read(fd, inputs, sizeof(inputs));
185 if (errno == EINTR || errno == EAGAIN)
188 PLOG(ERROR) << "error reading device " << path_.value();
193 for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
194 ProcessInputEvent(inputs[i]);
198 void TouchEventConverterEvdev::ProcessInputEvent(const input_event& input) {
199 if (input.type == EV_SYN) {
201 } else if(syn_dropped_) {
202 // Do nothing. This branch indicates we have lost sync with the driver.
203 } else if (input.type == EV_ABS) {
204 if (current_slot_ >= MAX_FINGERS) {
205 LOG(ERROR) << "too many touch events: " << current_slot_;
209 } else if (input.type == EV_KEY) {
210 switch (input.code) {
214 NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
217 NOTIMPLEMENTED() << "invalid type: " << input.type;
221 void TouchEventConverterEvdev::ProcessAbs(const input_event& input) {
222 switch (input.code) {
223 case ABS_MT_TOUCH_MAJOR:
224 altered_slots_.set(current_slot_);
225 // TODO(spang): If we have all of major, minor, and orientation,
226 // we can scale the ellipse correctly. However on the Pixel we get
227 // neither minor nor orientation, so this is all we can do.
228 events_[current_slot_].radius_x_ =
229 TuxelToPixelSize(input.value, x_num_tuxels_, x_num_pixels_) / 2.0f;
231 case ABS_MT_TOUCH_MINOR:
232 altered_slots_.set(current_slot_);
233 events_[current_slot_].radius_y_ =
234 TuxelToPixelSize(input.value, y_num_tuxels_, y_num_pixels_) / 2.0f;
236 case ABS_MT_POSITION_X:
237 altered_slots_.set(current_slot_);
238 events_[current_slot_].x_ = TuxelsToPixels(input.value,
244 case ABS_MT_POSITION_Y:
245 altered_slots_.set(current_slot_);
246 events_[current_slot_].y_ = TuxelsToPixels(input.value,
252 case ABS_MT_TRACKING_ID:
253 altered_slots_.set(current_slot_);
254 if (input.value < 0) {
255 events_[current_slot_].type_ = ET_TOUCH_RELEASED;
257 events_[current_slot_].finger_ = input.value;
258 events_[current_slot_].type_ = ET_TOUCH_PRESSED;
261 case ABS_MT_PRESSURE:
262 altered_slots_.set(current_slot_);
263 events_[current_slot_].pressure_ = input.value - pressure_min_;
264 events_[current_slot_].pressure_ /= pressure_max_ - pressure_min_;
267 current_slot_ = input.value;
268 altered_slots_.set(current_slot_);
271 DVLOG(5) << "unhandled code for EV_ABS: " << input.code;
275 void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
276 switch (input.code) {
279 // Have to re-initialize.
280 if (Reinitialize()) {
281 syn_dropped_ = false;
282 altered_slots_.reset();
284 LOG(ERROR) << "failed to re-initialize device info";
287 ReportEvents(base::TimeDelta::FromMicroseconds(
288 input.time.tv_sec * 1000000 + input.time.tv_usec));
294 // For type A devices, we just get a stream of all current contacts,
295 // in some arbitrary order.
296 events_[current_slot_++].type_ = ET_TOUCH_PRESSED;
300 // Some buffer has overrun. We ignore all events up to and
301 // including the next SYN_REPORT.
305 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
309 void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) {
310 for (int i = 0; i < MAX_FINGERS; i++) {
311 if (altered_slots_[i]) {
312 // TODO(rikroege): Support elliptical finger regions.
313 scoped_ptr<TouchEvent> event(
314 new TouchEvent(events_[i].type_,
315 gfx::PointF(events_[i].x_, events_[i].y_),
319 /* radius_x */ events_[i].radius_x_,
320 /* radius_y */ events_[i].radius_y_,
322 events_[i].pressure_));
323 event->set_source_device_id(id_);
324 callback_.Run(event.Pass());
326 // Subsequent events for this finger will be touch-move until it
328 events_[i].type_ = ET_TOUCH_MOVED;
331 altered_slots_.reset();