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 297