• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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