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.
4
5 #include "ui/events/ozone/evdev/touch_event_converter_evdev.h"
6
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>
13
14 #include <cmath>
15 #include <limits>
16
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"
30 #include "ui/ozone/public/event_factory_ozone.h"
31
32 namespace {
33
34 // Number is determined empirically.
35 // TODO(rjkroege): Configure this per device.
36 const float kFingerWidth = 25.f;
37
38 struct TouchCalibration {
39 int bezel_left;
40 int bezel_right;
41 int bezel_top;
42 int bezel_bottom;
43 };
44
GetTouchCalibration(TouchCalibration * cal)45 void GetTouchCalibration(TouchCalibration* cal) {
46 std::vector<std::string> parts;
47 if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
48 switches::kTouchCalibration),
49 ",",
50 &parts) >= 4) {
51 if (!base::StringToInt(parts[0], &cal->bezel_left))
52 DLOG(ERROR) << "Incorrect left border calibration value passed.";
53 if (!base::StringToInt(parts[1], &cal->bezel_right))
54 DLOG(ERROR) << "Incorrect right border calibration value passed.";
55 if (!base::StringToInt(parts[2], &cal->bezel_top))
56 DLOG(ERROR) << "Incorrect top border calibration value passed.";
57 if (!base::StringToInt(parts[3], &cal->bezel_bottom))
58 DLOG(ERROR) << "Incorrect bottom border calibration value passed.";
59 }
60 }
61
TuxelsToPixels(float val,float min_tuxels,float num_tuxels,float min_pixels,float num_pixels)62 float TuxelsToPixels(float val,
63 float min_tuxels,
64 float num_tuxels,
65 float min_pixels,
66 float num_pixels) {
67 // Map [min_tuxels, min_tuxels + num_tuxels) to
68 // [min_pixels, min_pixels + num_pixels).
69 return min_pixels + (val - min_tuxels) * num_pixels / num_tuxels;
70 }
71
72 } // namespace
73
74 namespace ui {
75
TouchEventConverterEvdev(int fd,base::FilePath path,const EventDeviceInfo & info,const EventDispatchCallback & callback)76 TouchEventConverterEvdev::TouchEventConverterEvdev(
77 int fd,
78 base::FilePath path,
79 const EventDeviceInfo& info,
80 const EventDispatchCallback& callback)
81 : EventConverterEvdev(callback),
82 syn_dropped_(false),
83 is_type_a_(false),
84 current_slot_(0),
85 fd_(fd),
86 path_(path) {
87 Init(info);
88 }
89
~TouchEventConverterEvdev()90 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
91 Stop();
92 close(fd_);
93 }
94
Init(const EventDeviceInfo & info)95 void TouchEventConverterEvdev::Init(const EventDeviceInfo& info) {
96 gfx::Screen *screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE);
97 if (!screen)
98 return; // No scaling.
99 gfx::Display display = screen->GetPrimaryDisplay();
100 gfx::Size size = display.GetSizeInPixel();
101
102 pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE),
103 pressure_max_ = info.GetAbsMaximum(ABS_MT_PRESSURE),
104 x_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_X),
105 x_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_X) - x_min_tuxels_ + 1,
106 y_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_Y),
107 y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1,
108 x_min_pixels_ = x_min_tuxels_,
109 x_num_pixels_ = x_num_tuxels_,
110 y_min_pixels_ = y_min_tuxels_,
111 y_num_pixels_ = y_num_tuxels_,
112
113 // Map coordinates onto screen.
114 x_min_pixels_ = 0;
115 y_min_pixels_ = 0;
116 x_num_pixels_ = size.width();
117 y_num_pixels_ = size.height();
118
119 VLOG(1) << "mapping touch coordinates to screen coordinates: "
120 << base::StringPrintf("%dx%d", size.width(), size.height());
121
122 // Apply --touch-calibration.
123 TouchCalibration cal = {};
124 GetTouchCalibration(&cal);
125 x_min_tuxels_ += cal.bezel_left;
126 x_num_tuxels_ -= cal.bezel_left + cal.bezel_right;
127 y_min_tuxels_ += cal.bezel_top;
128 y_num_tuxels_ -= cal.bezel_top + cal.bezel_bottom;
129
130 VLOG(1) << "applying touch calibration: "
131 << base::StringPrintf("[%d, %d, %d, %d]",
132 cal.bezel_left,
133 cal.bezel_right,
134 cal.bezel_top,
135 cal.bezel_bottom);
136 }
137
Start()138 void TouchEventConverterEvdev::Start() {
139 base::MessageLoopForUI::current()->WatchFileDescriptor(
140 fd_, true, base::MessagePumpLibevent::WATCH_READ, &controller_, this);
141 }
142
Stop()143 void TouchEventConverterEvdev::Stop() {
144 controller_.StopWatchingFileDescriptor();
145 }
146
Reinitialize()147 bool TouchEventConverterEvdev::Reinitialize() {
148 EventDeviceInfo info;
149 if (info.Initialize(fd_)) {
150 Init(info);
151 return true;
152 }
153 return false;
154 }
155
OnFileCanWriteWithoutBlocking(int)156 void TouchEventConverterEvdev::OnFileCanWriteWithoutBlocking(int /* fd */) {
157 // Read-only file-descriptors.
158 NOTREACHED();
159 }
160
OnFileCanReadWithoutBlocking(int fd)161 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
162 input_event inputs[MAX_FINGERS * 6 + 1];
163 ssize_t read_size = read(fd, inputs, sizeof(inputs));
164 if (read_size < 0) {
165 if (errno == EINTR || errno == EAGAIN)
166 return;
167 if (errno != ENODEV)
168 PLOG(ERROR) << "error reading device " << path_.value();
169 Stop();
170 return;
171 }
172
173 for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
174 ProcessInputEvent(inputs[i]);
175 }
176 }
177
ProcessInputEvent(const input_event & input)178 void TouchEventConverterEvdev::ProcessInputEvent(const input_event& input) {
179 if (input.type == EV_SYN) {
180 ProcessSyn(input);
181 } else if(syn_dropped_) {
182 // Do nothing. This branch indicates we have lost sync with the driver.
183 } else if (input.type == EV_ABS) {
184 if (current_slot_ >= MAX_FINGERS) {
185 LOG(ERROR) << "too many touch events: " << current_slot_;
186 return;
187 }
188 ProcessAbs(input);
189 } else if (input.type == EV_KEY) {
190 switch (input.code) {
191 case BTN_TOUCH:
192 break;
193 default:
194 NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
195 }
196 } else {
197 NOTIMPLEMENTED() << "invalid type: " << input.type;
198 }
199 }
200
ProcessAbs(const input_event & input)201 void TouchEventConverterEvdev::ProcessAbs(const input_event& input) {
202 switch (input.code) {
203 case ABS_MT_TOUCH_MAJOR:
204 altered_slots_.set(current_slot_);
205 events_[current_slot_].major_ = input.value;
206 break;
207 case ABS_X:
208 case ABS_MT_POSITION_X:
209 altered_slots_.set(current_slot_);
210 events_[current_slot_].x_ = TuxelsToPixels(input.value,
211 x_min_tuxels_,
212 x_num_tuxels_,
213 x_min_pixels_,
214 x_num_pixels_);
215 break;
216 case ABS_Y:
217 case ABS_MT_POSITION_Y:
218 altered_slots_.set(current_slot_);
219 events_[current_slot_].y_ = TuxelsToPixels(input.value,
220 y_min_tuxels_,
221 y_num_tuxels_,
222 y_min_pixels_,
223 y_num_pixels_);
224 break;
225 case ABS_MT_TRACKING_ID:
226 altered_slots_.set(current_slot_);
227 if (input.value < 0) {
228 events_[current_slot_].type_ = ET_TOUCH_RELEASED;
229 } else {
230 events_[current_slot_].finger_ = input.value;
231 events_[current_slot_].type_ = ET_TOUCH_PRESSED;
232 }
233 break;
234 case ABS_MT_PRESSURE:
235 case ABS_PRESSURE:
236 altered_slots_.set(current_slot_);
237 events_[current_slot_].pressure_ = input.value - pressure_min_;
238 events_[current_slot_].pressure_ /= pressure_max_ - pressure_min_;
239 break;
240 case ABS_MT_SLOT:
241 current_slot_ = input.value;
242 altered_slots_.set(current_slot_);
243 break;
244 default:
245 NOTIMPLEMENTED() << "invalid code for EV_ABS: " << input.code;
246 }
247 }
248
ProcessSyn(const input_event & input)249 void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
250 switch (input.code) {
251 case SYN_REPORT:
252 if (syn_dropped_) {
253 // Have to re-initialize.
254 if (Reinitialize()) {
255 syn_dropped_ = false;
256 altered_slots_.reset();
257 } else {
258 LOG(ERROR) << "failed to re-initialize device info";
259 }
260 } else {
261 ReportEvents(base::TimeDelta::FromMicroseconds(
262 input.time.tv_sec * 1000000 + input.time.tv_usec));
263 }
264 if (is_type_a_)
265 current_slot_ = 0;
266 break;
267 case SYN_MT_REPORT:
268 // For type A devices, we just get a stream of all current contacts,
269 // in some arbitrary order.
270 events_[current_slot_++].type_ = ET_TOUCH_PRESSED;
271 is_type_a_ = true;
272 break;
273 case SYN_DROPPED:
274 // Some buffer has overrun. We ignore all events up to and
275 // including the next SYN_REPORT.
276 syn_dropped_ = true;
277 break;
278 default:
279 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
280 }
281 }
282
ReportEvents(base::TimeDelta delta)283 void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) {
284 for (int i = 0; i < MAX_FINGERS; i++) {
285 if (altered_slots_[i]) {
286 // TODO(rikroege): Support elliptical finger regions.
287 TouchEvent evt(
288 events_[i].type_,
289 gfx::PointF(events_[i].x_, events_[i].y_),
290 /* flags */ 0,
291 /* touch_id */ i,
292 delta,
293 events_[i].pressure_ * kFingerWidth,
294 events_[i].pressure_ * kFingerWidth,
295 /* angle */ 0.,
296 events_[i].pressure_);
297 DispatchEventToCallback(&evt);
298
299 // Subsequent events for this finger will be touch-move until it
300 // is released.
301 events_[i].type_ = ET_TOUCH_MOVED;
302 }
303 }
304 altered_slots_.reset();
305 }
306
307 } // namespace ui
308