Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / ui / events / ozone / evdev / touch_event_converter_evdev.cc
index cb78cbe..883e0ad 100644 (file)
 
 #include "base/bind.h"
 #include "base/callback.h"
+#include "base/command_line.h"
 #include "base/logging.h"
-#include "base/message_loop/message_pump_ozone.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/events/ozone/event_factory_ozone.h"
 #include "ui/gfx/screen.h"
 
@@ -29,25 +35,56 @@ namespace {
 // TODO(rjkroege): Configure this per device.
 const float kFingerWidth = 25.f;
 
+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;
+}
+
 }  // namespace
 
 namespace ui {
 
-TouchEventConverterEvdev::TouchEventConverterEvdev(int fd,
-                                                   base::FilePath path,
-                                                   const EventDeviceInfo& info)
-    : pressure_min_(info.GetAbsMinimum(ABS_MT_PRESSURE)),
-      pressure_max_(info.GetAbsMaximum(ABS_MT_PRESSURE)),
-      x_scale_(1.),
-      y_scale_(1.),
-      x_min_(info.GetAbsMinimum(ABS_MT_POSITION_X)),
-      x_max_(info.GetAbsMaximum(ABS_MT_POSITION_X)),
-      y_min_(info.GetAbsMinimum(ABS_MT_POSITION_Y)),
-      y_max_(info.GetAbsMaximum(ABS_MT_POSITION_Y)),
+TouchEventConverterEvdev::TouchEventConverterEvdev(
+    int fd,
+    base::FilePath path,
+    const EventDeviceInfo& info,
+    const EventDispatchCallback& callback)
+    : EventConverterEvdev(callback),
+      syn_dropped_(false),
+      is_type_a_(false),
       current_slot_(0),
       fd_(fd),
       path_(path) {
-  Init();
+  Init(info);
 }
 
 TouchEventConverterEvdev::~TouchEventConverterEvdev() {
@@ -55,20 +92,51 @@ TouchEventConverterEvdev::~TouchEventConverterEvdev() {
   close(fd_);
 }
 
-void TouchEventConverterEvdev::Init() {
+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();
 
-  x_scale_ = (double)size.width() / (x_max_ - x_min_);
-  y_scale_ = (double)size.height() / (y_max_ - y_min_);
-  VLOG(1) << "touch scaling x_scale=" << x_scale_ << " y_scale=" << y_scale_;
+  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,
+  x_min_pixels_ = x_min_tuxels_,
+  x_num_pixels_ = x_num_tuxels_,
+  y_min_pixels_ = y_min_tuxels_,
+  y_num_pixels_ = 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);
 }
 
 void TouchEventConverterEvdev::Start() {
-  base::MessagePumpOzone::Current()->WatchFileDescriptor(
+  base::MessageLoopForUI::current()->WatchFileDescriptor(
       fd_, true, base::MessagePumpLibevent::WATCH_READ, &controller_, this);
 }
 
@@ -76,6 +144,15 @@ void TouchEventConverterEvdev::Stop() {
   controller_.StopWatchingFileDescriptor();
 }
 
+bool TouchEventConverterEvdev::Reinitialize() {
+  EventDeviceInfo info;
+  if (info.Initialize(fd_)) {
+    Init(info);
+    return true;
+  }
+  return false;
+}
+
 void TouchEventConverterEvdev::OnFileCanWriteWithoutBlocking(int /* fd */) {
   // Read-only file-descriptors.
   NOTREACHED();
@@ -93,9 +170,16 @@ void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
     return;
   }
 
+  ScopedVector<ui::TouchEvent> touch_events;
   for (unsigned i = 0; i < read_size / sizeof(*inputs); i++) {
     const input_event& input = inputs[i];
+    if (syn_dropped_ && input.type != EV_SYN)
+      continue;
     if (input.type == EV_ABS) {
+      if (current_slot_ >= MAX_FINGERS) {
+        PLOG(ERROR) << "too many touch events: " << current_slot_;
+        continue;
+      }
       switch (input.code) {
         case ABS_MT_TOUCH_MAJOR:
           altered_slots_.set(current_slot_);
@@ -104,12 +188,20 @@ void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
         case ABS_X:
         case ABS_MT_POSITION_X:
           altered_slots_.set(current_slot_);
-          events_[current_slot_].x_ = roundf(input.value * x_scale_);
+          events_[current_slot_].x_ = TuxelsToPixels(input.value,
+                                                     x_min_tuxels_,
+                                                     x_num_tuxels_,
+                                                     x_min_pixels_,
+                                                     x_num_pixels_);
           break;
         case ABS_Y:
         case ABS_MT_POSITION_Y:
           altered_slots_.set(current_slot_);
-          events_[current_slot_].y_ = roundf(input.value * y_scale_);
+          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_);
@@ -136,33 +228,51 @@ void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
     } else if (input.type == EV_SYN) {
       switch (input.code) {
         case SYN_REPORT:
-          for (int j = 0; j < MAX_FINGERS; j++) {
-            if (altered_slots_[j]) {
-              // TODO(rjkroege): Support elliptical finger regions.
-              scoped_ptr<TouchEvent> tev(new TouchEvent(
-                  events_[j].type_,
-                  gfx::Point(std::min(x_max_, events_[j].x_),
-                             std::min(y_max_, events_[j].y_)),
-                  /* flags */ 0,
-                  /* touch_id */ j,
-                  base::TimeDelta::FromMicroseconds(
-                      input.time.tv_sec * 1000000 + input.time.tv_usec),
-                  events_[j].pressure_ * kFingerWidth,
-                  events_[j].pressure_ * kFingerWidth,
-                  /* angle */ 0.,
-                  events_[j].pressure_));
-              DispatchEvent(tev.PassAs<ui::Event>());
-
-              // Subsequent events for this finger will be touch-move until it
-              // is released.
-              events_[j].type_ = ET_TOUCH_MOVED;
+          if (syn_dropped_) {
+            // Have to re-initialize.
+            if (Reinitialize()) {
+              syn_dropped_ = false;
+            } else {
+              PLOG(ERROR) << "failed to re-initialize device info";
+            }
+          } else {
+            for (int j = 0; j < MAX_FINGERS; j++) {
+              if (altered_slots_[j]) {
+                // TODO(rjkroege): Support elliptical finger regions.
+                touch_events.push_back(new TouchEvent(
+                    events_[j].type_,
+                    gfx::PointF(events_[j].x_, events_[j].y_),
+                    /* flags */ 0,
+                    /* touch_id */ j,
+                    base::TimeDelta::FromMicroseconds(
+                        input.time.tv_sec * 1000000 + input.time.tv_usec),
+                    events_[j].pressure_ * kFingerWidth,
+                    events_[j].pressure_ * kFingerWidth,
+                    /* angle */ 0.,
+                    events_[j].pressure_));
+
+                // Subsequent events for this finger will be touch-move until it
+                // is released.
+                events_[j].type_ = ET_TOUCH_MOVED;
+              }
             }
           }
           altered_slots_.reset();
+          if (is_type_a_)
+            current_slot_ = 0;
           break;
         case SYN_MT_REPORT:
-        case SYN_CONFIG:
+          // For type A devices, we just get a stream of all current contacts,
+          // in some arbitrary order.
+          current_slot_++;
+          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;
           break;
       }
@@ -177,6 +287,10 @@ void TouchEventConverterEvdev::OnFileCanReadWithoutBlocking(int fd) {
       NOTIMPLEMENTED() << "invalid type: " << input.type;
     }
   }
+  for (ScopedVector<ui::TouchEvent>::iterator iter = touch_events.begin();
+       iter != touch_events.end(); ++iter) {
+    DispatchEventToCallback(*iter);
+  }
 }
 
 }  // namespace ui