Home | History | Annotate | Download | only in evdev
      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