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