touchui: Create touch-events.

Create and process touch-events. The touch-events are created from
pointer-events. The way to decide whether a 'pointer event' originated from a
touch-device is not yet well-defined. So for now, use the --touch-devices
command line parameter to specify which pointer devices should be treated as
touch-device. For example, you can run:

  ./out/Debug/chrome --touch-devices=7,8

to treat the events coming in from devices with id 7 and 8 as touch events.
(these are the slave ids you get from 'xinput list')

BUG=None
TEST=None

Review URL: http://codereview.chromium.org/4738001

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@67133 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/renderer_host/render_widget_host.cc b/chrome/browser/renderer_host/render_widget_host.cc
index cd1a472..7f84227 100644
--- a/chrome/browser/renderer_host/render_widget_host.cc
+++ b/chrome/browser/renderer_host/render_widget_host.cc
@@ -628,6 +628,13 @@
   // only handled by RenderView.
 }
 
+#if defined(TOUCH_UI)
+void RenderWidgetHost::ForwardTouchEvent(
+    const WebKit::WebTouchEvent& touch_event) {
+  ForwardInputEvent(touch_event, sizeof(WebKit::WebTouchEvent), false);
+}
+#endif
+
 void RenderWidgetHost::RendererExited() {
   // Clearing this flag causes us to re-create the renderer when recovering
   // from a crashed renderer.
diff --git a/chrome/browser/renderer_host/render_widget_host.h b/chrome/browser/renderer_host/render_widget_host.h
index 8514ce0..9f2225b6 100644
--- a/chrome/browser/renderer_host/render_widget_host.h
+++ b/chrome/browser/renderer_host/render_widget_host.h
@@ -279,6 +279,10 @@
                                   const std::string& value);
   virtual void ForwardEditCommandsForNextKeyEvent(
       const EditCommands& edit_commands);
+#if defined(TOUCH_UI)
+  virtual void ForwardTouchEvent(const WebKit::WebTouchEvent& touch_event);
+#endif
+
 
   // Update the text direction of the focused input element and notify it to a
   // renderer process.
diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.cc b/chrome/browser/renderer_host/render_widget_host_view_views.cc
index 591e3985..a9bc958 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_views.cc
+++ b/chrome/browser/renderer_host/render_widget_host_view_views.cc
@@ -33,6 +33,7 @@
 
 using WebKit::WebInputEventFactory;
 using WebKit::WebMouseWheelEvent;
+using WebKit::WebTouchEvent;
 
 namespace {
 
@@ -51,6 +52,48 @@
   return modifiers;
 }
 
+WebKit::WebTouchPoint::State TouchPointStateFromEvent(
+    const views::TouchEvent* event) {
+  switch (event->GetType()) {
+    case views::Event::ET_TOUCH_PRESSED:
+      return WebKit::WebTouchPoint::StatePressed;
+    case views::Event::ET_TOUCH_RELEASED:
+      return WebKit::WebTouchPoint::StateReleased;
+    case views::Event::ET_TOUCH_MOVED:
+      return WebKit::WebTouchPoint::StateMoved;
+    case views::Event::ET_TOUCH_CANCELLED:
+      return WebKit::WebTouchPoint::StateCancelled;
+    default:
+      return WebKit::WebTouchPoint::StateUndefined;
+  }
+}
+
+WebKit::WebInputEvent::Type TouchEventTypeFromEvent(
+    const views::TouchEvent* event) {
+  switch (event->GetType()) {
+    case views::Event::ET_TOUCH_PRESSED:
+      return WebKit::WebInputEvent::TouchStart;
+    case views::Event::ET_TOUCH_RELEASED:
+      return WebKit::WebInputEvent::TouchEnd;
+    case views::Event::ET_TOUCH_MOVED:
+      return WebKit::WebInputEvent::TouchMove;
+    case views::Event::ET_TOUCH_CANCELLED:
+      return WebKit::WebInputEvent::TouchCancel;
+    default:
+      return WebKit::WebInputEvent::Undefined;
+  }
+}
+
+void UpdateTouchPointPosition(const views::TouchEvent* event,
+                              const gfx::Point& origin,
+                              WebKit::WebTouchPoint* tpoint) {
+  tpoint->position.x = event->x();
+  tpoint->position.y = event->y();
+
+  tpoint->screenPosition.x = tpoint->position.x + origin.x();
+  tpoint->screenPosition.y = tpoint->position.y + origin.y();
+}
+
 void InitializeWebMouseEventFromViewsEvent(const views::LocatedEvent& e,
                                            const gfx::Point& origin,
                                            WebKit::WebMouseEvent* wmevent) {
@@ -78,7 +121,9 @@
       is_loading_(false),
       native_cursor_(NULL),
       is_showing_context_menu_(false),
-      visually_deemphasized_(false) {
+      visually_deemphasized_(false),
+      touch_event_()
+    {
   SetFocusable(true);
   host_->set_view(this);
 }
@@ -581,6 +626,75 @@
   host_->ForwardKeyboardEvent(event);
 }
 
+bool RenderWidgetHostViewViews::OnTouchEvent(const views::TouchEvent& e) {
+  // Update the list of touch points first.
+  WebKit::WebTouchPoint* point = NULL;
+
+  switch (e.GetType()) {
+    case views::Event::ET_TOUCH_PRESSED:
+      // Add a new touch point.
+      if (touch_event_.touchPointsLength <
+          WebTouchEvent::touchPointsLengthCap) {
+        point = &touch_event_.touchPoints[touch_event_.touchPointsLength++];
+        point->id = e.identity();
+      }
+      break;
+    case views::Event::ET_TOUCH_RELEASED:
+    case views::Event::ET_TOUCH_CANCELLED:
+    case views::Event::ET_TOUCH_MOVED: {
+      // The touch point should have been added to the event from an earlier
+      // _PRESSED event. So find that.
+      // At the moment, only a maximum of 4 touch-points are allowed. So a
+      // simple loop should be sufficient.
+      for (int i = 0; i < WebTouchEvent::touchPointsLengthCap; ++i) {
+        point = touch_event_.touchPoints + i;
+        if (point->id == e.identity()) {
+          break;
+        }
+        point = NULL;
+      }
+      DCHECK(point != NULL) << "Touchpoint not found for event " << e.GetType();
+      break;
+    }
+    default:
+      DLOG(WARNING) << "Unknown touch event " << e.GetType();
+      break;
+  }
+
+  if (!point)
+    return false;
+
+  // Update the location and state of the point.
+  UpdateTouchPointPosition(&e, GetPosition(), point);
+  point->state = TouchPointStateFromEvent(&e);
+
+  // Mark the rest of the points as stationary.
+  for (int i = 0; i < touch_event_.touchPointsLength; ++i) {
+    WebKit::WebTouchPoint* iter = touch_event_.touchPoints + i;
+    if (iter != point) {
+      iter->state = WebKit::WebTouchPoint::StateStationary;
+    }
+  }
+
+  // Update the type of the touch event.
+  touch_event_.type = TouchEventTypeFromEvent(&e);
+
+  // The event and all the touches have been updated. Dispatch.
+  host_->ForwardTouchEvent(touch_event_);
+
+  // If the touch was released, then remove it from the list of touch points.
+  if (e.GetType() == views::Event::ET_TOUCH_RELEASED) {
+    --touch_event_.touchPointsLength;
+    for (int i = point - touch_event_.touchPoints;
+         i < touch_event_.touchPointsLength;
+         ++i) {
+      touch_event_.touchPoints[i] = touch_event_.touchPoints[i + 1];
+    }
+  }
+
+  return true;
+}
+
 // static
 RenderWidgetHostView*
     RenderWidgetHostView::GetRenderWidgetHostViewFromNativeView(
diff --git a/chrome/browser/renderer_host/render_widget_host_view_views.h b/chrome/browser/renderer_host/render_widget_host_view_views.h
index 88a4746d..0689fbe 100644
--- a/chrome/browser/renderer_host/render_widget_host_view_views.h
+++ b/chrome/browser/renderer_host/render_widget_host_view_views.h
@@ -14,6 +14,7 @@
 #include "base/time.h"
 #include "chrome/browser/renderer_host/render_widget_host_view.h"
 #include "gfx/native_widget_types.h"
+#include "third_party/WebKit/WebKit/chromium/public/WebInputEvent.h"
 #include "views/controls/native/native_view_host.h"
 #include "views/event.h"
 #include "views/view.h"
@@ -22,10 +23,6 @@
 class RenderWidgetHost;
 struct NativeWebKeyboardEvent;
 
-namespace WebKit {
-class WebMouseEvent;
-}
-
 // -----------------------------------------------------------------------------
 // See comments in render_widget_host_view.h about this class and its members.
 // -----------------------------------------------------------------------------
@@ -85,7 +82,7 @@
   gfx::NativeCursor GetCursorForPoint(views::Event::EventType type,
                                       const gfx::Point& point);
 
-  // Views mouse events
+  // Views mouse events, overridden from views::View.
   virtual bool OnMousePressed(const views::MouseEvent& event);
   virtual bool OnMouseDragged(const views::MouseEvent& event);
   virtual void OnMouseReleased(const views::MouseEvent& event, bool canceled);
@@ -94,7 +91,7 @@
   virtual void OnMouseExited(const views::MouseEvent& event);
   virtual bool OnMouseWheel(const views::MouseWheelEvent& e);
 
-  // Views keyboard events
+  // Views keyboard events, overridden from views::View.
   virtual bool OnKeyPressed(const views::KeyEvent &e);
   virtual bool OnKeyReleased(const views::KeyEvent &e);
 
@@ -104,6 +101,9 @@
   // Forwards a keyboard event to renderer.
   void ForwardKeyboardEvent(const NativeWebKeyboardEvent& event);
 
+  // Views touch events, overridden from views::View.
+  virtual bool OnTouchEvent(const views::TouchEvent& e);
+
  private:
   friend class RenderWidgetHostViewViewsWidget;
 
@@ -162,6 +162,11 @@
   // The size that we want the renderer to be.
   gfx::Size requested_size_;
 
+  // The touch-event. Its touch-points are updated as necessary. A new
+  // touch-point is added from an ET_TOUCH_PRESSED event, and a touch-point is
+  // removed from the list on an ET_TOUCH_RELEASED event.
+  WebKit::WebTouchEvent touch_event_;
+
   DISALLOW_COPY_AND_ASSIGN(RenderWidgetHostViewViews);
 };
 
diff --git a/chrome/browser/ui/browser_init.cc b/chrome/browser/ui/browser_init.cc
index 9d1e89c..ac4932da 100644
--- a/chrome/browser/ui/browser_init.cc
+++ b/chrome/browser/ui/browser_init.cc
@@ -15,6 +15,7 @@
 #include "base/path_service.h"
 #include "base/scoped_ptr.h"
 #include "base/string_number_conversions.h"
+#include "base/string_split.h"
 #include "base/thread_restrictions.h"
 #include "base/utf_string_conversions.h"
 #include "chrome/browser/automation/automation_provider.h"
@@ -96,6 +97,10 @@
 #include "chrome/browser/dom_ui/mediaplayer_ui.h"
 #endif
 
+#if defined(HAVE_XINPUT2)
+#include "views/focus/accelerator_handler.h"
+#endif
+
 namespace {
 
 class SetAsDefaultBrowserTask : public Task {
@@ -1017,6 +1022,30 @@
   }
 #endif
 
+#if defined(HAVE_XINPUT2)
+  // Get a list of pointer-devices that should be treated as touch-devices.
+  // TODO(sad): Instead of/in addition to getting the list from the
+  // command-line, query X for a list of touch devices.
+  std::string touch_devices =
+    command_line.GetSwitchValueASCII(switches::kTouchDevices);
+
+  if (!touch_devices.empty()) {
+    std::vector<std::string> devs;
+    std::vector<unsigned int> device_ids;
+    unsigned int devid;
+    base::SplitString(touch_devices, ',', &devs);
+    for (std::vector<std::string>::iterator iter = devs.begin();
+        iter != devs.end(); ++iter) {
+      if (base::StringToInt(*iter, reinterpret_cast<int*>(&devid))) {
+        device_ids.push_back(devid);
+      } else {
+        DLOG(WARNING) << "Invalid touch-device id: " << *iter;
+      }
+    }
+    views::SetTouchDeviceList(device_ids);
+  }
+#endif
+
   // If we don't want to launch a new browser window or tab (in the case
   // of an automation request), we are done here.
   if (!silent_launch) {
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 62185c6..41123a0 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -3472,6 +3472,7 @@
             '../build/linux/system.gyp:gtk',
             '../build/linux/system.gyp:gtkprint',
             '../build/linux/system.gyp:nss',
+            '../build/linux/system.gyp:x11',
             '../third_party/undoview/undoview.gyp:undoview',
           ],
           'link_settings': {
@@ -3545,6 +3546,7 @@
             '../build/linux/system.gyp:gtk',
             '../build/linux/system.gyp:gtkprint',
             '../build/linux/system.gyp:nss',
+            '../build/linux/system.gyp:x11',
           ],
           'sources': [
             'browser/file_path_watcher_stub.cc',
diff --git a/chrome/common/chrome_switches.cc b/chrome/common/chrome_switches.cc
index 34dbc285..8e99e8e 100644
--- a/chrome/common/chrome_switches.cc
+++ b/chrome/common/chrome_switches.cc
@@ -1313,6 +1313,11 @@
 const char kWebSocketLiveExperimentHost[]   = "websocket-live-experiment-host";
 #endif
 
+#if defined(HAVE_XINPUT2)
+const char kTouchDevices[]                  = "touch-devices";
+#endif
+
+
 // USE_SECCOMP_SANDBOX controls whether the seccomp sandbox is opt-in or -out.
 // TODO(evan): unify all of these once we turn the seccomp sandbox always
 // on.  Also remove the #include of command_line.h above.
diff --git a/chrome/common/chrome_switches.h b/chrome/common/chrome_switches.h
index 422cb40..1154171 100644
--- a/chrome/common/chrome_switches.h
+++ b/chrome/common/chrome_switches.h
@@ -390,6 +390,10 @@
 extern const char kRendererCheckFalseTest[];
 #endif
 
+#if defined(HAVE_XINPUT2)
+extern const char kTouchDevices[];
+#endif
+
 #if defined(USE_SECCOMP_SANDBOX)
 extern const char kDisableSeccompSandbox[];
 #else