Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(169)

Unified Diff: ui/events/ozone/evdev/touch_event_converter_evdev.cc

Issue 1287103004: Sync ui/events to chromium @ https://codereview.chromium.org/1210203002 (Closed) Base URL: https://github.com/domokit/mojo.git@master
Patch Set: rebased Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: ui/events/ozone/evdev/touch_event_converter_evdev.cc
diff --git a/ui/events/ozone/evdev/touch_event_converter_evdev.cc b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
new file mode 100644
index 0000000000000000000000000000000000000000..0c9be25f2b702d21ecd225220ddd0f57c844b56e
--- /dev/null
+++ b/ui/events/ozone/evdev/touch_event_converter_evdev.cc
@@ -0,0 +1,442 @@
+// 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 "base/trace_event/trace_event.h"
+#include "ui/events/devices/device_data_manager.h"
+#include "ui/events/devices/device_util_linux.h"
+#include "ui/events/event.h"
+#include "ui/events/event_constants.h"
+#include "ui/events/event_switches.h"
+#include "ui/events/event_utils.h"
+#include "ui/events/ozone/evdev/device_event_dispatcher_evdev.h"
+#include "ui/events/ozone/evdev/touch_evdev_types.h"
+#include "ui/events/ozone/evdev/touch_noise/touch_noise_finder.h"
+
+namespace {
+
+const int kMaxTrackingId = 0xffff; // TRKID_MAX in kernel.
+
+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(base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
+ switches::kTouchCalibration),
+ ",", &parts) >= 4) {
+ if (!base::StringToInt(parts[0], &cal->bezel_left))
+ LOG(ERROR) << "Incorrect left border calibration value passed.";
+ if (!base::StringToInt(parts[1], &cal->bezel_right))
+ LOG(ERROR) << "Incorrect right border calibration value passed.";
+ if (!base::StringToInt(parts[2], &cal->bezel_top))
+ LOG(ERROR) << "Incorrect top border calibration value passed.";
+ if (!base::StringToInt(parts[3], &cal->bezel_bottom))
+ LOG(ERROR) << "Incorrect bottom border calibration value passed.";
+ }
+}
+
+int32_t AbsCodeToMtCode(int32_t code) {
+ switch (code) {
+ case ABS_X:
+ return ABS_MT_POSITION_X;
+ case ABS_Y:
+ return ABS_MT_POSITION_Y;
+ case ABS_PRESSURE:
+ return ABS_MT_PRESSURE;
+ case ABS_DISTANCE:
+ return ABS_MT_DISTANCE;
+ default:
+ return -1;
+ }
+}
+
+const int kTrackingIdForUnusedSlot = -1;
+
+} // namespace
+
+namespace ui {
+
+TouchEventConverterEvdev::TouchEventConverterEvdev(
+ int fd,
+ base::FilePath path,
+ int id,
+ InputDeviceType type,
+ const EventDeviceInfo& devinfo,
+ DeviceEventDispatcherEvdev* dispatcher)
+ : EventConverterEvdev(fd,
+ path,
+ id,
+ type,
+ devinfo.name(),
+ devinfo.vendor_id(),
+ devinfo.product_id()),
+ dispatcher_(dispatcher) {
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kExtraTouchNoiseFiltering)) {
+ touch_noise_finder_.reset(new TouchNoiseFinder);
+ }
+}
+
+TouchEventConverterEvdev::~TouchEventConverterEvdev() {
+}
+
+void TouchEventConverterEvdev::Initialize(const EventDeviceInfo& info) {
+ has_mt_ = info.HasMultitouch();
+
+ if (has_mt_) {
+ 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;
+ touch_points_ =
+ std::min<int>(info.GetAbsMaximum(ABS_MT_SLOT) + 1, kNumTouchEvdevSlots);
+ current_slot_ = info.GetAbsValue(ABS_MT_SLOT);
+ } else {
+ pressure_min_ = info.GetAbsMinimum(ABS_PRESSURE);
+ pressure_max_ = info.GetAbsMaximum(ABS_PRESSURE);
+ x_min_tuxels_ = info.GetAbsMinimum(ABS_X);
+ x_num_tuxels_ = info.GetAbsMaximum(ABS_X) - x_min_tuxels_ + 1;
+ y_min_tuxels_ = info.GetAbsMinimum(ABS_Y);
+ y_num_tuxels_ = info.GetAbsMaximum(ABS_Y) - y_min_tuxels_ + 1;
+ touch_points_ = 1;
+ current_slot_ = 0;
+ }
+
+ quirk_left_mouse_button_ =
+ !has_mt_ && !info.HasKeyEvent(BTN_TOUCH) && info.HasKeyEvent(BTN_LEFT);
+
+ // Apply --touch-calibration.
+ if (type() == INPUT_DEVICE_INTERNAL) {
+ 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);
+ }
+
+ events_.resize(touch_points_);
+
+ if (has_mt_) {
+ for (size_t i = 0; i < events_.size(); ++i) {
+ events_[i].x = info.GetAbsMtSlotValueWithDefault(ABS_MT_POSITION_X, i, 0);
+ events_[i].y = info.GetAbsMtSlotValueWithDefault(ABS_MT_POSITION_Y, i, 0);
+ events_[i].tracking_id = info.GetAbsMtSlotValueWithDefault(
+ ABS_MT_TRACKING_ID, i, kTrackingIdForUnusedSlot);
+ events_[i].touching = (events_[i].tracking_id >= 0);
+ events_[i].slot = i;
+
+ // Dirty the slot so we'll update the consumer at the first opportunity.
+ // We can't dispatch here as this is currently called on the worker pool.
+ // TODO(spang): Move initialization off worker pool.
+ events_[i].altered = true;
+
+ // Optional bits.
+ events_[i].radius_x =
+ info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MAJOR, i, 0) / 2.0f;
+ events_[i].radius_y =
+ info.GetAbsMtSlotValueWithDefault(ABS_MT_TOUCH_MINOR, i, 0) / 2.0f;
+ events_[i].pressure = ScalePressure(
+ info.GetAbsMtSlotValueWithDefault(ABS_MT_PRESSURE, i, 0));
+ }
+ } else {
+ // TODO(spang): Add key state to EventDeviceInfo to allow initial contact.
+ // (and make sure to take into account quirk_left_mouse_button_)
+ events_[0].x = 0;
+ events_[0].y = 0;
+ events_[0].tracking_id = kTrackingIdForUnusedSlot;
+ events_[0].touching = false;
+ events_[0].slot = 0;
+ events_[0].radius_x = 0;
+ events_[0].radius_y = 0;
+ events_[0].pressure = 0;
+ }
+}
+
+void TouchEventConverterEvdev::Reinitialize() {
+ EventDeviceInfo info;
+ if (!info.Initialize(fd_)) {
+ LOG(ERROR) << "Failed to synchronize state for touch device: "
+ << path_.value();
+ Stop();
+ return;
+ }
+
+ Initialize(info);
+}
+
+bool TouchEventConverterEvdev::HasTouchscreen() const {
+ return true;
+}
+
+gfx::Size TouchEventConverterEvdev::GetTouchscreenSize() const {
+ return gfx::Size(x_num_tuxels_, y_num_tuxels_);
+}
+
+int TouchEventConverterEvdev::GetTouchPoints() const {
+ return touch_points_;
+}
+
+void TouchEventConverterEvdev::OnEnabled() {
+ ReportEvents(EventTimeForNow());
+}
+
+void TouchEventConverterEvdev::OnDisabled() {
+ ReleaseTouches();
+}
+
+void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
+ TRACE_EVENT1("evdev",
+ "TouchEventConverterEvdev::OnFileCanReadWithoutBlocking", "fd",
+ fd);
+
+ input_event inputs[kNumTouchEvdevSlots * 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;
+ }
+
+ if (!enabled_) {
+ dropped_events_ = true;
+ return;
+ }
+
+ for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
+ if (!has_mt_) {
+ // Emulate the device as an MT device with only 1 slot by inserting extra
+ // MT protocol events in the stream.
+ EmulateMultitouchEvent(inputs[i]);
+ }
+
+ ProcessMultitouchEvent(inputs[i]);
+ }
+}
+
+void TouchEventConverterEvdev::ProcessMultitouchEvent(
+ const input_event& input) {
+ if (input.type == EV_SYN) {
+ ProcessSyn(input);
+ } else if (dropped_events_) {
+ // Do nothing. This branch indicates we have lost sync with the driver.
+ } else if (input.type == EV_ABS) {
+ if (events_.size() <= current_slot_) {
+ LOG(ERROR) << "current_slot_ (" << current_slot_
+ << ") >= events_.size() (" << events_.size() << ")";
+ } else {
+ ProcessAbs(input);
+ }
+ } else if (input.type == EV_KEY) {
+ ProcessKey(input);
+ } else if (input.type == EV_MSC) {
+ // Ignored.
+ } else {
+ NOTIMPLEMENTED() << "invalid type: " << input.type;
+ }
+}
+
+void TouchEventConverterEvdev::EmulateMultitouchEvent(
+ const input_event& event) {
+ input_event emulated_event = event;
+
+ if (event.type == EV_ABS) {
+ emulated_event.code = AbsCodeToMtCode(event.code);
+ if (emulated_event.code >= 0)
+ ProcessMultitouchEvent(emulated_event);
+ } else if (event.type == EV_KEY) {
+ if (event.code == BTN_TOUCH ||
+ (quirk_left_mouse_button_ && event.code == BTN_LEFT)) {
+ emulated_event.type = EV_ABS;
+ emulated_event.code = ABS_MT_TRACKING_ID;
+ emulated_event.value =
+ event.value ? NextTrackingId() : kTrackingIdForUnusedSlot;
+ ProcessMultitouchEvent(emulated_event);
+ }
+ }
+}
+
+void TouchEventConverterEvdev::ProcessKey(const input_event& input) {
+ switch (input.code) {
+ case BTN_TOUCH:
+ case BTN_LEFT:
+ break;
+ default:
+ NOTIMPLEMENTED() << "invalid code for EV_KEY: " << input.code;
+ }
+}
+
+void TouchEventConverterEvdev::ProcessAbs(const input_event& input) {
+ switch (input.code) {
+ case ABS_MT_TOUCH_MAJOR:
+ // 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 = input.value / 2.0f;
+ break;
+ case ABS_MT_TOUCH_MINOR:
+ events_[current_slot_].radius_y = input.value / 2.0f;
+ break;
+ case ABS_MT_POSITION_X:
+ events_[current_slot_].x = input.value;
+ break;
+ case ABS_MT_POSITION_Y:
+ events_[current_slot_].y = input.value;
+ break;
+ case ABS_MT_TRACKING_ID:
+ UpdateTrackingId(current_slot_, input.value);
+ break;
+ case ABS_MT_PRESSURE:
+ events_[current_slot_].pressure = ScalePressure(input.value);
+ break;
+ case ABS_MT_SLOT:
+ if (input.value >= 0 &&
+ static_cast<size_t>(input.value) < events_.size()) {
+ current_slot_ = input.value;
+ } else {
+ LOG(ERROR) << "invalid touch event index: " << input.value;
+ return;
+ }
+ break;
+ default:
+ DVLOG(5) << "unhandled code for EV_ABS: " << input.code;
+ return;
+ }
+ events_[current_slot_].altered = true;
+}
+
+void TouchEventConverterEvdev::ProcessSyn(const input_event& input) {
+ switch (input.code) {
+ case SYN_REPORT:
+ ReportEvents(EventConverterEvdev::TimeDeltaFromInputEvent(input));
+ break;
+ case SYN_DROPPED:
+ // Some buffer has overrun. We ignore all events up to and
+ // including the next SYN_REPORT.
+ dropped_events_ = true;
+ break;
+ default:
+ NOTIMPLEMENTED() << "invalid code for EV_SYN: " << input.code;
+ }
+}
+
+EventType TouchEventConverterEvdev::GetEventTypeForTouch(
+ const InProgressTouchEvdev& touch) {
+ if (touch.cancelled)
+ return ET_UNKNOWN;
+
+ if (touch_noise_finder_ && touch_noise_finder_->SlotHasNoise(touch.slot)) {
+ if (touch.touching && !touch.was_touching)
+ return ET_UNKNOWN;
+ return ET_TOUCH_CANCELLED;
+ }
+
+ if (touch.touching)
+ return touch.was_touching ? ET_TOUCH_MOVED : ET_TOUCH_PRESSED;
+ return touch.was_touching ? ET_TOUCH_RELEASED : ET_UNKNOWN;
+}
+
+void TouchEventConverterEvdev::ReportEvent(const InProgressTouchEvdev& event,
+ EventType event_type,
+ const base::TimeDelta& timestamp) {
+ dispatcher_->DispatchTouchEvent(TouchEventParams(
+ input_device_.id, event.slot, event_type, gfx::PointF(event.x, event.y),
+ gfx::Vector2dF(event.radius_x, event.radius_y), event.pressure,
+ timestamp));
+}
+
+void TouchEventConverterEvdev::ReportEvents(base::TimeDelta delta) {
+ if (dropped_events_) {
+ Reinitialize();
+ dropped_events_ = false;
+ }
+
+ if (touch_noise_finder_)
+ touch_noise_finder_->HandleTouches(events_, delta);
+
+ for (size_t i = 0; i < events_.size(); i++) {
+ InProgressTouchEvdev* event = &events_[i];
+ if (!event->altered)
+ continue;
+
+ EventType event_type = GetEventTypeForTouch(*event);
+ if (event_type == ET_UNKNOWN || event_type == ET_TOUCH_CANCELLED)
+ event->cancelled = true;
+
+ if (event_type != ET_UNKNOWN)
+ ReportEvent(*event, event_type, delta);
+
+ event->was_touching = event->touching;
+ event->altered = false;
+ }
+}
+
+void TouchEventConverterEvdev::UpdateTrackingId(int slot, int tracking_id) {
+ InProgressTouchEvdev* event = &events_[slot];
+
+ if (event->tracking_id == tracking_id)
+ return;
+
+ event->tracking_id = tracking_id;
+ event->touching = (tracking_id >= 0);
+ event->altered = true;
+
+ if (tracking_id >= 0)
+ event->cancelled = false;
+}
+
+void TouchEventConverterEvdev::ReleaseTouches() {
+ for (size_t slot = 0; slot < events_.size(); slot++)
+ UpdateTrackingId(slot, kTrackingIdForUnusedSlot);
+
+ ReportEvents(EventTimeForNow());
+}
+
+float TouchEventConverterEvdev::ScalePressure(int32_t value) {
+ float pressure = value - pressure_min_;
+ if (pressure_max_ - pressure_min_)
+ pressure /= pressure_max_ - pressure_min_;
+ return pressure;
+}
+
+int TouchEventConverterEvdev::NextTrackingId() {
+ return next_tracking_id_++ & kMaxTrackingId;
+}
+
+} // namespace ui
« no previous file with comments | « ui/events/ozone/evdev/touch_event_converter_evdev.h ('k') | ui/events/ozone/evdev/touch_event_converter_evdev_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698