Upstream version 9.38.198.0
[platform/framework/web/crosswalk.git] / src / ui / events / ozone / evdev / touch_event_converter_evdev.cc
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
31 namespace {
32
33 // Number is determined empirically.
34 // TODO(rjkroege): Configure this per device.
35 const float kFingerWidth = 25.f;
36
37 struct TouchCalibration {
38   int bezel_left;
39   int bezel_right;
40   int bezel_top;
41   int bezel_bottom;
42 };
43
44 void GetTouchCalibration(TouchCalibration* cal) {
45   std::vector<std::string> parts;
46   if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
47                    switches::kTouchCalibration),
48                ",",
49                &parts) >= 4) {
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.";
58   }
59 }
60
61 float TuxelsToPixels(float val,
62                      float min_tuxels,
63                      float num_tuxels,
64                      float min_pixels,
65                      float num_pixels) {
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;
69 }
70
71 }  // namespace
72
73 namespace ui {
74
75 TouchEventConverterEvdev::TouchEventConverterEvdev(
76     int fd,
77     base::FilePath path,
78     const EventDeviceInfo& info,
79     const EventDispatchCallback& callback)
80     : EventConverterEvdev(callback),
81       syn_dropped_(false),
82       is_type_a_(false),
83       current_slot_(0),
84       fd_(fd),
85       path_(path) {
86   Init(info);
87 }
88
89 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
90   Stop();
91   close(fd_);
92 }
93
94 void TouchEventConverterEvdev::Init(const EventDeviceInfo& info) {
95   gfx::Screen *screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE);
96   if (!screen)
97     return;  // No scaling.
98   gfx::Display display = screen->GetPrimaryDisplay();
99   gfx::Size size = display.GetSizeInPixel();
100
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_,
111
112   // Map coordinates onto screen.
113   x_min_pixels_ = 0;
114   y_min_pixels_ = 0;
115   x_num_pixels_ = size.width();
116   y_num_pixels_ = size.height();
117
118   VLOG(1) << "mapping touch coordinates to screen coordinates: "
119           << base::StringPrintf("%dx%d", size.width(), size.height());
120
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;
128
129   VLOG(1) << "applying touch calibration: "
130           << base::StringPrintf("[%d, %d, %d, %d]",
131                                 cal.bezel_left,
132                                 cal.bezel_right,
133                                 cal.bezel_top,
134                                 cal.bezel_bottom);
135 }
136
137 void TouchEventConverterEvdev::Start() {
138   base::MessageLoopForUI::current()->WatchFileDescriptor(
139       fd_, true, base::MessagePumpLibevent::WATCH_READ, &controller_, this);
140 }
141
142 void TouchEventConverterEvdev::Stop() {
143   controller_.StopWatchingFileDescriptor();
144 }
145
146 bool TouchEventConverterEvdev::Reinitialize() {
147   EventDeviceInfo info;
148   if (info.Initialize(fd_)) {
149     Init(info);
150     return true;
151   }
152   return false;
153 }
154
155 void TouchEventConverterEvdev::OnFileCanWriteWithoutBlocking(int /* fd */) {
156   // Read-only file-descriptors.
157   NOTREACHED();
158 }
159
160 void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
161   input_event inputs[MAX_FINGERS * 6 + 1];
162   ssize_t read_size = read(fd, inputs, sizeof(inputs));
163   if (read_size < 0) {
164     if (errno == EINTR || errno == EAGAIN)
165       return;
166     if (errno != ENODEV)
167       PLOG(ERROR) << "error reading device " << path_.value();
168     Stop();
169     return;
170   }
171
172   for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
173     ProcessInputEvent(inputs[i]);
174   }
175 }
176
177 void TouchEventConverterEvdev::ProcessInputEvent(const input_event& input) {
178   if (input.type == EV_SYN) {
179     ProcessSyn(input);
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_;
185       return;
186     }
187     ProcessAbs(input);
188   } else if (input.type == EV_KEY) {
189     switch (input.code) {
190       case BTN_TOUCH:
191         break;
192       default:
193         NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
194     }
195   } else {
196     NOTIMPLEMENTED() << "invalid type: " << input.type;
197   }
198 }
199
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;
205       break;
206     case ABS_X:
207     case ABS_MT_POSITION_X:
208       altered_slots_.set(current_slot_);
209       events_[current_slot_].x_ = TuxelsToPixels(input.value,
210                                                  x_min_tuxels_,
211                                                  x_num_tuxels_,
212                                                  x_min_pixels_,
213                                                  x_num_pixels_);
214       break;
215     case ABS_Y:
216     case ABS_MT_POSITION_Y:
217       altered_slots_.set(current_slot_);
218       events_[current_slot_].y_ = TuxelsToPixels(input.value,
219                                                  y_min_tuxels_,
220                                                  y_num_tuxels_,
221                                                  y_min_pixels_,
222                                                  y_num_pixels_);
223       break;
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;
228       } else {
229         events_[current_slot_].finger_ = input.value;
230         events_[current_slot_].type_ = ET_TOUCH_PRESSED;
231       }
232       break;
233     case ABS_MT_PRESSURE:
234     case ABS_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_;
238       break;
239     case ABS_MT_SLOT:
240       current_slot_ = input.value;
241       altered_slots_.set(current_slot_);
242       break;
243     default:
244       NOTIMPLEMENTED() << "invalid code for EV_ABS: " << input.code;
245   }
246 }
247
248 void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
249   switch (input.code) {
250     case SYN_REPORT:
251       if (syn_dropped_) {
252         // Have to re-initialize.
253         if (Reinitialize()) {
254           syn_dropped_ = false;
255           altered_slots_.reset();
256         } else {
257           LOG(ERROR) << "failed to re-initialize device info";
258         }
259       } else {
260         ReportEvents(base::TimeDelta::FromMicroseconds(
261             input.time.tv_sec * 1000000 + input.time.tv_usec));
262       }
263       if (is_type_a_)
264         current_slot_ = 0;
265       break;
266     case SYN_MT_REPORT:
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;
270       is_type_a_ = true;
271       break;
272     case SYN_DROPPED:
273       // Some buffer has overrun. We ignore all events up to and
274       // including the next SYN_REPORT.
275       syn_dropped_ = true;
276       break;
277     default:
278       NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
279   }
280 }
281
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.
286       TouchEvent evt(
287           events_[i].type_,
288           gfx::PointF(events_[i].x_, events_[i].y_),
289           /* flags */ 0,
290           /* touch_id */ i,
291           delta,
292           events_[i].pressure_ * kFingerWidth,
293           events_[i].pressure_ * kFingerWidth,
294           /* angle */ 0.,
295           events_[i].pressure_);
296       DispatchEventToCallback(&evt);
297
298       // Subsequent events for this finger will be touch-move until it
299       // is released.
300       events_[i].type_ = ET_TOUCH_MOVED;
301     }
302   }
303   altered_slots_.reset();
304 }
305
306 }  // namespace ui