|  | // Copyright 2014 The Chromium Authors. All rights reserved. | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "ui/events/ozone/evdev/touch_event_converter_evdev.h" | 
|  |  | 
|  | #include <errno.h> | 
|  | #include <fcntl.h> | 
|  | #include <linux/input.h> | 
|  | #include <poll.h> | 
|  | #include <stdio.h> | 
|  | #include <unistd.h> | 
|  |  | 
|  | #include <cmath> | 
|  | #include <limits> | 
|  |  | 
|  | #include "base/bind.h" | 
|  | #include "base/callback.h" | 
|  | #include "base/command_line.h" | 
|  | #include "base/logging.h" | 
|  | #include "base/memory/scoped_vector.h" | 
|  | #include "base/message_loop/message_loop.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/strings/string_util.h" | 
|  | #include "base/strings/stringprintf.h" | 
|  | #include "ui/events/event.h" | 
|  | #include "ui/events/event_constants.h" | 
|  | #include "ui/events/event_switches.h" | 
|  | #include "ui/gfx/screen.h" | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | struct TouchCalibration { | 
|  | int bezel_left; | 
|  | int bezel_right; | 
|  | int bezel_top; | 
|  | int bezel_bottom; | 
|  | }; | 
|  |  | 
|  | void GetTouchCalibration(TouchCalibration* cal) { | 
|  | std::vector<std::string> parts; | 
|  | if (Tokenize(CommandLine::ForCurrentProcess()->GetSwitchValueASCII( | 
|  | switches::kTouchCalibration), | 
|  | ",", | 
|  | &parts) >= 4) { | 
|  | if (!base::StringToInt(parts[0], &cal->bezel_left)) | 
|  | DLOG(ERROR) << "Incorrect left border calibration value passed."; | 
|  | if (!base::StringToInt(parts[1], &cal->bezel_right)) | 
|  | DLOG(ERROR) << "Incorrect right border calibration value passed."; | 
|  | if (!base::StringToInt(parts[2], &cal->bezel_top)) | 
|  | DLOG(ERROR) << "Incorrect top border calibration value passed."; | 
|  | if (!base::StringToInt(parts[3], &cal->bezel_bottom)) | 
|  | DLOG(ERROR) << "Incorrect bottom border calibration value passed."; | 
|  | } | 
|  | } | 
|  |  | 
|  | float TuxelsToPixels(float val, | 
|  | float min_tuxels, | 
|  | float num_tuxels, | 
|  | float min_pixels, | 
|  | float num_pixels) { | 
|  | // Map [min_tuxels, min_tuxels + num_tuxels) to | 
|  | //     [min_pixels, min_pixels + num_pixels). | 
|  | return min_pixels + (val - min_tuxels) * num_pixels / num_tuxels; | 
|  | } | 
|  |  | 
|  | float TuxelToPixelSize(float val, float num_tuxels, float num_pixels) { | 
|  | return val * num_pixels / num_tuxels; | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | namespace ui { | 
|  |  | 
|  | TouchEventConverterEvdev::TouchEventConverterEvdev( | 
|  | int fd, | 
|  | base::FilePath path, | 
|  | int id, | 
|  | const EventDeviceInfo& info, | 
|  | const EventDispatchCallback& callback) | 
|  | : EventConverterEvdev(fd, path, id), | 
|  | callback_(callback), | 
|  | syn_dropped_(false), | 
|  | is_type_a_(false), | 
|  | current_slot_(0) { | 
|  | Init(info); | 
|  | } | 
|  |  | 
|  | TouchEventConverterEvdev::~TouchEventConverterEvdev() { | 
|  | Stop(); | 
|  | close(fd_); | 
|  | } | 
|  |  | 
|  | void TouchEventConverterEvdev::Init(const EventDeviceInfo& info) { | 
|  | gfx::Screen* screen = gfx::Screen::GetScreenByType(gfx::SCREEN_TYPE_NATIVE); | 
|  | if (!screen) | 
|  | return;  // No scaling. | 
|  | gfx::Display display = screen->GetPrimaryDisplay(); | 
|  | gfx::Size size = display.GetSizeInPixel(); | 
|  |  | 
|  | pressure_min_ = info.GetAbsMinimum(ABS_MT_PRESSURE); | 
|  | pressure_max_ = info.GetAbsMaximum(ABS_MT_PRESSURE); | 
|  | x_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_X); | 
|  | x_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_X) - x_min_tuxels_ + 1; | 
|  | y_min_tuxels_ = info.GetAbsMinimum(ABS_MT_POSITION_Y); | 
|  | y_num_tuxels_ = info.GetAbsMaximum(ABS_MT_POSITION_Y) - y_min_tuxels_ + 1; | 
|  | native_size_ = gfx::Size(x_num_tuxels_, y_num_tuxels_); | 
|  |  | 
|  | // Map coordinates onto screen. | 
|  | x_min_pixels_ = 0; | 
|  | y_min_pixels_ = 0; | 
|  | x_num_pixels_ = size.width(); | 
|  | y_num_pixels_ = size.height(); | 
|  |  | 
|  | VLOG(1) << "mapping touch coordinates to screen coordinates: " | 
|  | << base::StringPrintf("%dx%d", size.width(), size.height()); | 
|  |  | 
|  | // Apply --touch-calibration. | 
|  | TouchCalibration cal = {}; | 
|  | GetTouchCalibration(&cal); | 
|  | x_min_tuxels_ += cal.bezel_left; | 
|  | x_num_tuxels_ -= cal.bezel_left + cal.bezel_right; | 
|  | y_min_tuxels_ += cal.bezel_top; | 
|  | y_num_tuxels_ -= cal.bezel_top + cal.bezel_bottom; | 
|  |  | 
|  | VLOG(1) << "applying touch calibration: " | 
|  | << base::StringPrintf("[%d, %d, %d, %d]", | 
|  | cal.bezel_left, | 
|  | cal.bezel_right, | 
|  | cal.bezel_top, | 
|  | cal.bezel_bottom); | 
|  | } | 
|  |  | 
|  | bool TouchEventConverterEvdev::Reinitialize() { | 
|  | EventDeviceInfo info; | 
|  | if (info.Initialize(fd_)) { | 
|  | Init(info); | 
|  | return true; | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool TouchEventConverterEvdev::HasTouchscreen() const { | 
|  | return true; | 
|  | } | 
|  |  | 
|  | gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const { | 
|  | return native_size_; | 
|  | } | 
|  |  | 
|  | void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) { | 
|  | input_event inputs[MAX_FINGERS * 6 + 1]; | 
|  | ssize_t read_size = read(fd, inputs, sizeof(inputs)); | 
|  | if (read_size < 0) { | 
|  | if (errno == EINTR || errno == EAGAIN) | 
|  | return; | 
|  | if (errno != ENODEV) | 
|  | PLOG(ERROR) << "error reading device " << path_.value(); | 
|  | Stop(); | 
|  | return; | 
|  | } | 
|  |  | 
|  | for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) { | 
|  | ProcessInputEvent(inputs[i]); | 
|  | } | 
|  | } | 
|  |  | 
|  | void TouchEventConverterEvdev::ProcessInputEvent(const input_event& input) { | 
|  | if (input.type == EV_SYN) { | 
|  | ProcessSyn(input); | 
|  | } else if(syn_dropped_) { | 
|  | // Do nothing. This branch indicates we have lost sync with the driver. | 
|  | } else if (input.type == EV_ABS) { | 
|  | if (current_slot_ >= MAX_FINGERS) { | 
|  | LOG(ERROR) << "too many touch events: " << current_slot_; | 
|  | return; | 
|  | } | 
|  | ProcessAbs(input); | 
|  | } else if (input.type == EV_KEY) { | 
|  | switch (input.code) { | 
|  | case BTN_TOUCH: | 
|  | break; | 
|  | default: | 
|  | NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code; | 
|  | } | 
|  | } else { | 
|  | NOTIMPLEMENTED() << "invalid type: " << input.type; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TouchEventConverterEvdev::ProcessAbs(const input_event& input) { | 
|  | switch (input.code) { | 
|  | case ABS_MT_TOUCH_MAJOR: | 
|  | altered_slots_.set(current_slot_); | 
|  | // TODO(spang): If we have all of major, minor, and orientation, | 
|  | // we can scale the ellipse correctly. However on the Pixel we get | 
|  | // neither minor nor orientation, so this is all we can do. | 
|  | events_[current_slot_].radius_x_ = | 
|  | TuxelToPixelSize(input.value, x_num_tuxels_, x_num_pixels_) / 2.0f; | 
|  | break; | 
|  | case ABS_MT_TOUCH_MINOR: | 
|  | altered_slots_.set(current_slot_); | 
|  | events_[current_slot_].radius_y_ = | 
|  | TuxelToPixelSize(input.value, y_num_tuxels_, y_num_pixels_) / 2.0f; | 
|  | break; | 
|  | case ABS_MT_POSITION_X: | 
|  | altered_slots_.set(current_slot_); | 
|  | events_[current_slot_].x_ = TuxelsToPixels(input.value, | 
|  | x_min_tuxels_, | 
|  | x_num_tuxels_, | 
|  | x_min_pixels_, | 
|  | x_num_pixels_); | 
|  | break; | 
|  | case ABS_MT_POSITION_Y: | 
|  | altered_slots_.set(current_slot_); | 
|  | events_[current_slot_].y_ = TuxelsToPixels(input.value, | 
|  | y_min_tuxels_, | 
|  | y_num_tuxels_, | 
|  | y_min_pixels_, | 
|  | y_num_pixels_); | 
|  | break; | 
|  | case ABS_MT_TRACKING_ID: | 
|  | altered_slots_.set(current_slot_); | 
|  | if (input.value < 0) { | 
|  | events_[current_slot_].type_ = ET_TOUCH_RELEASED; | 
|  | } else { | 
|  | events_[current_slot_].finger_ = input.value; | 
|  | events_[current_slot_].type_ = ET_TOUCH_PRESSED; | 
|  | } | 
|  | break; | 
|  | case ABS_MT_PRESSURE: | 
|  | altered_slots_.set(current_slot_); | 
|  | events_[current_slot_].pressure_ = input.value - pressure_min_; | 
|  | events_[current_slot_].pressure_ /= pressure_max_ - pressure_min_; | 
|  | break; | 
|  | case ABS_MT_SLOT: | 
|  | current_slot_ = input.value; | 
|  | altered_slots_.set(current_slot_); | 
|  | break; | 
|  | default: | 
|  | DVLOG(5) << "unhandled code for EV_ABS: " << input.code; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TouchEventConverterEvdev::ProcessSyn(const input_event& input) { | 
|  | switch (input.code) { | 
|  | case SYN_REPORT: | 
|  | if (syn_dropped_) { | 
|  | // Have to re-initialize. | 
|  | if (Reinitialize()) { | 
|  | syn_dropped_ = false; | 
|  | altered_slots_.reset(); | 
|  | } else { | 
|  | LOG(ERROR) << "failed to re-initialize device info"; | 
|  | } | 
|  | } else { | 
|  | ReportEvents(base::TimeDelta::FromMicroseconds( | 
|  | input.time.tv_sec * 1000000 + input.time.tv_usec)); | 
|  | } | 
|  | if (is_type_a_) | 
|  | current_slot_ = 0; | 
|  | break; | 
|  | case SYN_MT_REPORT: | 
|  | // For type A devices, we just get a stream of all current contacts, | 
|  | // in some arbitrary order. | 
|  | events_[current_slot_++].type_ = ET_TOUCH_PRESSED; | 
|  | is_type_a_ = true; | 
|  | break; | 
|  | case SYN_DROPPED: | 
|  | // Some buffer has overrun. We ignore all events up to and | 
|  | // including the next SYN_REPORT. | 
|  | syn_dropped_ = true; | 
|  | break; | 
|  | default: | 
|  | NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code; | 
|  | } | 
|  | } | 
|  |  | 
|  | void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) { | 
|  | for (int i = 0; i < MAX_FINGERS; i++) { | 
|  | if (altered_slots_[i]) { | 
|  | // TODO(rikroege): Support elliptical finger regions. | 
|  | callback_.Run(make_scoped_ptr( | 
|  | new TouchEvent(events_[i].type_, | 
|  | gfx::PointF(events_[i].x_, events_[i].y_), | 
|  | /* flags */ 0, | 
|  | /* touch_id */ i, | 
|  | delta, | 
|  | /* radius_x */ events_[i].radius_x_, | 
|  | /* radius_y */ events_[i].radius_y_, | 
|  | /* angle */ 0., | 
|  | events_[i].pressure_))); | 
|  |  | 
|  | // Subsequent events for this finger will be touch-move until it | 
|  | // is released. | 
|  | events_[i].type_ = ET_TOUCH_MOVED; | 
|  | } | 
|  | } | 
|  | altered_slots_.reset(); | 
|  | } | 
|  |  | 
|  | }  // namespace ui |