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 struct TouchCalibration
{
40 void GetTouchCalibration(TouchCalibration
* cal
) {
41 std::vector
<std::string
> parts
;
42 if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
43 switches::kTouchCalibration
),
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.";
57 float TuxelsToPixels(float val
,
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
;
67 float TuxelToPixelSize(float val
, float num_tuxels
, float num_pixels
) {
68 return val
* 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 // TODO(spang): If we have all of major, minor, and orientation,
205 // we can scale the ellipse correctly. However on the Pixel we get
206 // neither minor nor orientation, so this is all we can do.
207 events_
[current_slot_
].radius_x_
=
208 TuxelToPixelSize(input
.value
, x_num_tuxels_
, x_num_pixels_
) / 2.0f
;
210 case ABS_MT_TOUCH_MINOR
:
211 altered_slots_
.set(current_slot_
);
212 events_
[current_slot_
].radius_y_
=
213 TuxelToPixelSize(input
.value
, y_num_tuxels_
, y_num_pixels_
) / 2.0f
;
215 case ABS_MT_POSITION_X
:
216 altered_slots_
.set(current_slot_
);
217 events_
[current_slot_
].x_
= TuxelsToPixels(input
.value
,
223 case ABS_MT_POSITION_Y
:
224 altered_slots_
.set(current_slot_
);
225 events_
[current_slot_
].y_
= TuxelsToPixels(input
.value
,
231 case ABS_MT_TRACKING_ID
:
232 altered_slots_
.set(current_slot_
);
233 if (input
.value
< 0) {
234 events_
[current_slot_
].type_
= ET_TOUCH_RELEASED
;
236 events_
[current_slot_
].finger_
= input
.value
;
237 events_
[current_slot_
].type_
= ET_TOUCH_PRESSED
;
240 case ABS_MT_PRESSURE
:
241 altered_slots_
.set(current_slot_
);
242 events_
[current_slot_
].pressure_
= input
.value
- pressure_min_
;
243 events_
[current_slot_
].pressure_
/= pressure_max_
- pressure_min_
;
246 current_slot_
= input
.value
;
247 altered_slots_
.set(current_slot_
);
250 DVLOG(5) << "unhandled code for EV_ABS: " << input
.code
;
254 void TouchEventConverterEvdev::ProcessSyn(const input_event
& input
) {
255 switch (input
.code
) {
258 // Have to re-initialize.
259 if (Reinitialize()) {
260 syn_dropped_
= false;
261 altered_slots_
.reset();
263 LOG(ERROR
) << "failed to re-initialize device info";
266 ReportEvents(base::TimeDelta::FromMicroseconds(
267 input
.time
.tv_sec
* 1000000 + input
.time
.tv_usec
));
273 // For type A devices, we just get a stream of all current contacts,
274 // in some arbitrary order.
275 events_
[current_slot_
++].type_
= ET_TOUCH_PRESSED
;
279 // Some buffer has overrun. We ignore all events up to and
280 // including the next SYN_REPORT.
284 NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input
.code
;
288 void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta
) {
289 for (int i
= 0; i
< MAX_FINGERS
; i
++) {
290 if (altered_slots_
[i
]) {
291 // TODO(rikroege): Support elliptical finger regions.
292 TouchEvent
evt(events_
[i
].type_
,
293 gfx::PointF(events_
[i
].x_
, events_
[i
].y_
),
295 /* touch_id */ events_
[i
].finger_
,
297 /* radius_x */ events_
[i
].radius_x_
,
298 /* radius_y */ events_
[i
].radius_y_
,
300 events_
[i
].pressure_
);
301 DispatchEventToCallback(&evt
);
303 // Subsequent events for this finger will be touch-move until it
305 events_
[i
].type_
= ET_TOUCH_MOVED
;
308 altered_slots_
.reset();