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/event.h"
27 #include "ui/events/event_constants.h"
28 #include "ui/events/event_switches.h"
29 #include "ui/gfx/screen.h"
33 // Number is determined empirically.
34 // TODO(rjkroege): Configure this per device.
35 const float kFingerWidth = 25.f;
37 struct TouchCalibration {
44 void GetTouchCalibration(TouchCalibration* cal) {
45 std::vector<std::string> parts;
46 if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
47 switches::kTouchCalibration),
50 if (!base::StringToInt(parts[0], &cal->bezel_left))
51 DLOG(ERROR) << "Incorrect left border calibration value passed.";
52 if (!base::StringToInt(parts[1], &cal->bezel_right))
53 DLOG(ERROR) << "Incorrect right border calibration value passed.";
54 if (!base::StringToInt(parts[2], &cal->bezel_top))
55 DLOG(ERROR) << "Incorrect top border calibration value passed.";
56 if (!base::StringToInt(parts[3], &cal->bezel_bottom))
57 DLOG(ERROR) << "Incorrect bottom border calibration value passed.";
61 float TuxelsToPixels(float val,
66 // Map [min_tuxels, min_tuxels + num_tuxels) to
67 // [min_pixels, min_pixels + num_pixels).
68 return min_pixels + (val - min_tuxels) * num_pixels / num_tuxels;
75 TouchEventConverterEvdev::TouchEventConverterEvdev(
78 const EventDeviceInfo& info,
79 const EventDispatchCallback& callback)
80 : EventConverterEvdev(callback),
89 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
94 void TouchEventConverterEvdev::Init(const EventDeviceInfo& info) {
95 gfx::Screen *screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE);
97 return; // No scaling.
98 gfx::Display display = screen->GetPrimaryDisplay();
99 gfx::Size size = display.GetSizeInPixel();
101 pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE),
102 pressure_max_ = info.GetAbsMaximum(ABS_MT_PRESSURE),
103 x_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_X),
104 x_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_X) - x_min_tuxels_ + 1,
105 y_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_Y),
106 y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1,
107 x_min_pixels_ = x_min_tuxels_,
108 x_num_pixels_ = x_num_tuxels_,
109 y_min_pixels_ = y_min_tuxels_,
110 y_num_pixels_ = y_num_tuxels_,
112 // Map coordinates onto screen.
115 x_num_pixels_ = size.width();
116 y_num_pixels_ = size.height();
118 VLOG(1) << "mapping touch coordinates to screen coordinates: "
119 << base::StringPrintf("%dx%d", size.width(), size.height());
121 // Apply --touch-calibration.
122 TouchCalibration cal = {};
123 GetTouchCalibration(&cal);
124 x_min_tuxels_ += cal.bezel_left;
125 x_num_tuxels_ -= cal.bezel_left + cal.bezel_right;
126 y_min_tuxels_ += cal.bezel_top;
127 y_num_tuxels_ -= cal.bezel_top + cal.bezel_bottom;
129 VLOG(1) << "applying touch calibration: "
130 << base::StringPrintf("[%d, %d, %d, %d]",
137 void TouchEventConverterEvdev::Start() {
138 base::MessageLoopForUI::current()->WatchFileDescriptor(
139 fd_, true, base::MessagePumpLibevent::WATCH_READ, &controller_, this);
142 void TouchEventConverterEvdev::Stop() {
143 controller_.StopWatchingFileDescriptor();
146 bool TouchEventConverterEvdev::Reinitialize() {
147 EventDeviceInfo info;
148 if (info.Initialize(fd_)) {
155 void TouchEventConverterEvdev::OnFileCanWriteWithoutBlocking(int /* fd */) {
156 // Read-only file-descriptors.
160 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
161 input_event inputs[MAX_FINGERS * 6 + 1];
162 ssize_t read_size = read(fd, inputs, sizeof(inputs));
164 if (errno == EINTR || errno == EAGAIN)
167 PLOG(ERROR) << "error reading device " << path_.value();
172 for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
173 ProcessInputEvent(inputs[i]);
177 void TouchEventConverterEvdev::ProcessInputEvent(const input_event& input) {
178 if (input.type == EV_SYN) {
180 } else if(syn_dropped_) {
181 // Do nothing. This branch indicates we have lost sync with the driver.
182 } else if (input.type == EV_ABS) {
183 if (current_slot_ >= MAX_FINGERS) {
184 LOG(ERROR) << "too many touch events: " << current_slot_;
188 } else if (input.type == EV_KEY) {
189 switch (input.code) {
193 NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
196 NOTIMPLEMENTED() << "invalid type: " << input.type;
200 void TouchEventConverterEvdev::ProcessAbs(const input_event& input) {
201 switch (input.code) {
202 case ABS_MT_TOUCH_MAJOR:
203 altered_slots_.set(current_slot_);
204 events_[current_slot_].major_ = input.value;
207 case ABS_MT_POSITION_X:
208 altered_slots_.set(current_slot_);
209 events_[current_slot_].x_ = TuxelsToPixels(input.value,
216 case ABS_MT_POSITION_Y:
217 altered_slots_.set(current_slot_);
218 events_[current_slot_].y_ = TuxelsToPixels(input.value,
224 case ABS_MT_TRACKING_ID:
225 altered_slots_.set(current_slot_);
226 if (input.value < 0) {
227 events_[current_slot_].type_ = ET_TOUCH_RELEASED;
229 events_[current_slot_].finger_ = input.value;
230 events_[current_slot_].type_ = ET_TOUCH_PRESSED;
233 case ABS_MT_PRESSURE:
235 altered_slots_.set(current_slot_);
236 events_[current_slot_].pressure_ = input.value - pressure_min_;
237 events_[current_slot_].pressure_ /= pressure_max_ - pressure_min_;
240 current_slot_ = input.value;
241 altered_slots_.set(current_slot_);
244 NOTIMPLEMENTED() << "invalid code for EV_ABS: " << input.code;
248 void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
249 switch (input.code) {
252 // Have to re-initialize.
253 if (Reinitialize()) {
254 syn_dropped_ = false;
255 altered_slots_.reset();
257 LOG(ERROR) << "failed to re-initialize device info";
260 ReportEvents(base::TimeDelta::FromMicroseconds(
261 input.time.tv_sec * 1000000 + input.time.tv_usec));
267 // For type A devices, we just get a stream of all current contacts,
268 // in some arbitrary order.
269 events_[current_slot_++].type_ = ET_TOUCH_PRESSED;
273 // Some buffer has overrun. We ignore all events up to and
274 // including the next SYN_REPORT.
278 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
282 void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) {
283 for (int i = 0; i < MAX_FINGERS; i++) {
284 if (altered_slots_[i]) {
285 // TODO(rikroege): Support elliptical finger regions.
288 gfx::PointF(events_[i].x_, events_[i].y_),
292 events_[i].pressure_ * kFingerWidth,
293 events_[i].pressure_ * kFingerWidth,
295 events_[i].pressure_);
296 DispatchEventToCallback(&evt);
298 // Subsequent events for this finger will be touch-move until it
300 events_[i].type_ = ET_TOUCH_MOVED;
303 altered_slots_.reset();