Home | History | Annotate | Download | only in input
      1 // Copyright 2013 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 // MSVC++ requires this to be set before any other includes to get M_PI.
      6 #define _USE_MATH_DEFINES
      7 
      8 #include "content/browser/renderer_host/input/web_input_event_util.h"
      9 
     10 #include <cmath>
     11 
     12 #include "base/strings/string_util.h"
     13 #include "content/common/input/web_touch_event_traits.h"
     14 #include "ui/events/event_constants.h"
     15 #include "ui/events/gesture_detection/gesture_event_data.h"
     16 #include "ui/events/gesture_detection/motion_event.h"
     17 
     18 using blink::WebGestureEvent;
     19 using blink::WebInputEvent;
     20 using blink::WebTouchEvent;
     21 using blink::WebTouchPoint;
     22 using ui::MotionEvent;
     23 
     24 namespace {
     25 
     26 const char* GetKeyIdentifier(ui::KeyboardCode key_code) {
     27   switch (key_code) {
     28     case ui::VKEY_MENU:
     29       return "Alt";
     30     case ui::VKEY_CONTROL:
     31       return "Control";
     32     case ui::VKEY_SHIFT:
     33       return "Shift";
     34     case ui::VKEY_CAPITAL:
     35       return "CapsLock";
     36     case ui::VKEY_LWIN:
     37     case ui::VKEY_RWIN:
     38       return "Win";
     39     case ui::VKEY_CLEAR:
     40       return "Clear";
     41     case ui::VKEY_DOWN:
     42       return "Down";
     43     case ui::VKEY_END:
     44       return "End";
     45     case ui::VKEY_RETURN:
     46       return "Enter";
     47     case ui::VKEY_EXECUTE:
     48       return "Execute";
     49     case ui::VKEY_F1:
     50       return "F1";
     51     case ui::VKEY_F2:
     52       return "F2";
     53     case ui::VKEY_F3:
     54       return "F3";
     55     case ui::VKEY_F4:
     56       return "F4";
     57     case ui::VKEY_F5:
     58       return "F5";
     59     case ui::VKEY_F6:
     60       return "F6";
     61     case ui::VKEY_F7:
     62       return "F7";
     63     case ui::VKEY_F8:
     64       return "F8";
     65     case ui::VKEY_F9:
     66       return "F9";
     67     case ui::VKEY_F10:
     68       return "F10";
     69     case ui::VKEY_F11:
     70       return "F11";
     71     case ui::VKEY_F12:
     72       return "F12";
     73     case ui::VKEY_F13:
     74       return "F13";
     75     case ui::VKEY_F14:
     76       return "F14";
     77     case ui::VKEY_F15:
     78       return "F15";
     79     case ui::VKEY_F16:
     80       return "F16";
     81     case ui::VKEY_F17:
     82       return "F17";
     83     case ui::VKEY_F18:
     84       return "F18";
     85     case ui::VKEY_F19:
     86       return "F19";
     87     case ui::VKEY_F20:
     88       return "F20";
     89     case ui::VKEY_F21:
     90       return "F21";
     91     case ui::VKEY_F22:
     92       return "F22";
     93     case ui::VKEY_F23:
     94       return "F23";
     95     case ui::VKEY_F24:
     96       return "F24";
     97     case ui::VKEY_HELP:
     98       return "Help";
     99     case ui::VKEY_HOME:
    100       return "Home";
    101     case ui::VKEY_INSERT:
    102       return "Insert";
    103     case ui::VKEY_LEFT:
    104       return "Left";
    105     case ui::VKEY_NEXT:
    106       return "PageDown";
    107     case ui::VKEY_PRIOR:
    108       return "PageUp";
    109     case ui::VKEY_PAUSE:
    110       return "Pause";
    111     case ui::VKEY_SNAPSHOT:
    112       return "PrintScreen";
    113     case ui::VKEY_RIGHT:
    114       return "Right";
    115     case ui::VKEY_SCROLL:
    116       return "Scroll";
    117     case ui::VKEY_SELECT:
    118       return "Select";
    119     case ui::VKEY_UP:
    120       return "Up";
    121     case ui::VKEY_DELETE:
    122       return "U+007F";  // Standard says that DEL becomes U+007F.
    123     case ui::VKEY_MEDIA_NEXT_TRACK:
    124       return "MediaNextTrack";
    125     case ui::VKEY_MEDIA_PREV_TRACK:
    126       return "MediaPreviousTrack";
    127     case ui::VKEY_MEDIA_STOP:
    128       return "MediaStop";
    129     case ui::VKEY_MEDIA_PLAY_PAUSE:
    130       return "MediaPlayPause";
    131     case ui::VKEY_VOLUME_MUTE:
    132       return "VolumeMute";
    133     case ui::VKEY_VOLUME_DOWN:
    134       return "VolumeDown";
    135     case ui::VKEY_VOLUME_UP:
    136       return "VolumeUp";
    137     default:
    138       return NULL;
    139   };
    140 }
    141 
    142 WebInputEvent::Type ToWebInputEventType(MotionEvent::Action action) {
    143   switch (action) {
    144     case MotionEvent::ACTION_DOWN:
    145       return WebInputEvent::TouchStart;
    146     case MotionEvent::ACTION_MOVE:
    147       return WebInputEvent::TouchMove;
    148     case MotionEvent::ACTION_UP:
    149       return WebInputEvent::TouchEnd;
    150     case MotionEvent::ACTION_CANCEL:
    151       return WebInputEvent::TouchCancel;
    152     case MotionEvent::ACTION_POINTER_DOWN:
    153       return WebInputEvent::TouchStart;
    154     case MotionEvent::ACTION_POINTER_UP:
    155       return WebInputEvent::TouchEnd;
    156   }
    157   NOTREACHED() << "Invalid MotionEvent::Action.";
    158   return WebInputEvent::Undefined;
    159 }
    160 
    161 // Note that |is_action_pointer| is meaningful only in the context of
    162 // |ACTION_POINTER_UP| and |ACTION_POINTER_DOWN|; other actions map directly to
    163 // WebTouchPoint::State.
    164 WebTouchPoint::State ToWebTouchPointState(MotionEvent::Action action,
    165                                           bool is_action_pointer) {
    166   switch (action) {
    167     case MotionEvent::ACTION_DOWN:
    168       return WebTouchPoint::StatePressed;
    169     case MotionEvent::ACTION_MOVE:
    170       return WebTouchPoint::StateMoved;
    171     case MotionEvent::ACTION_UP:
    172       return WebTouchPoint::StateReleased;
    173     case MotionEvent::ACTION_CANCEL:
    174       return WebTouchPoint::StateCancelled;
    175     case MotionEvent::ACTION_POINTER_DOWN:
    176       return is_action_pointer ? WebTouchPoint::StatePressed
    177                                : WebTouchPoint::StateStationary;
    178     case MotionEvent::ACTION_POINTER_UP:
    179       return is_action_pointer ? WebTouchPoint::StateReleased
    180                                : WebTouchPoint::StateStationary;
    181   }
    182   NOTREACHED() << "Invalid MotionEvent::Action.";
    183   return WebTouchPoint::StateUndefined;
    184 }
    185 
    186 WebTouchPoint CreateWebTouchPoint(const MotionEvent& event,
    187                                   size_t pointer_index) {
    188   WebTouchPoint touch;
    189   touch.id = event.GetPointerId(pointer_index);
    190   touch.state = ToWebTouchPointState(
    191       event.GetAction(),
    192       static_cast<int>(pointer_index) == event.GetActionIndex());
    193   touch.position.x = event.GetX(pointer_index);
    194   touch.position.y = event.GetY(pointer_index);
    195   touch.screenPosition.x = event.GetRawX(pointer_index);
    196   touch.screenPosition.y = event.GetRawY(pointer_index);
    197 
    198   // A note on touch ellipse specifications:
    199   //
    200   // Android MotionEvent provides the major and minor axes of the touch ellipse,
    201   // as well as the orientation of the major axis clockwise from vertical, in
    202   // radians. See:
    203   // http://developer.android.com/reference/android/view/MotionEvent.html
    204   //
    205   // The proposed extension to W3C Touch Events specifies the touch ellipse
    206   // using two radii along x- & y-axes and a positive acute rotation angle in
    207   // degrees. See:
    208   // http://dvcs.w3.org/hg/webevents/raw-file/default/touchevents.html
    209 
    210   float major_radius = event.GetTouchMajor(pointer_index) / 2.f;
    211   float minor_radius = event.GetTouchMinor(pointer_index) / 2.f;
    212   float orientation_deg = event.GetOrientation(pointer_index) * 180.f / M_PI;
    213   DCHECK_GE(major_radius, 0) << "Unexpected touch major < 0";
    214   DCHECK_GE(minor_radius, 0) << "Unexpected touch minor < 0";
    215   DCHECK_GE(major_radius, minor_radius) << "Unexpected major/minor touch radii";
    216   DCHECK(-90 <= orientation_deg && orientation_deg <= 90)
    217       << "Unexpected touch orientation angle";
    218   if (orientation_deg >= 0) {
    219     // The case orientation_deg == 0 is handled here on purpose: although the
    220     // 'else' block is equivalent in this case, we want to pass the 0 value
    221     // unchanged (and 0 is the default value for many devices that don't
    222     // report elliptical touches).
    223     touch.radiusX = minor_radius;
    224     touch.radiusY = major_radius;
    225     touch.rotationAngle = orientation_deg;
    226   } else {
    227     touch.radiusX = major_radius;
    228     touch.radiusY = minor_radius;
    229     touch.rotationAngle = orientation_deg + 90;
    230   }
    231 
    232   touch.force = event.GetPressure(pointer_index);
    233 
    234   return touch;
    235 }
    236 
    237 }  // namespace
    238 
    239 namespace content {
    240 
    241 void UpdateWindowsKeyCodeAndKeyIdentifier(blink::WebKeyboardEvent* event,
    242                                           ui::KeyboardCode windows_key_code) {
    243   event->windowsKeyCode = windows_key_code;
    244 
    245   const char* id = GetKeyIdentifier(windows_key_code);
    246   if (id) {
    247     base::strlcpy(event->keyIdentifier, id, sizeof(event->keyIdentifier) - 1);
    248   } else {
    249     base::snprintf(event->keyIdentifier,
    250                    sizeof(event->keyIdentifier),
    251                    "U+%04X",
    252                    base::ToUpperASCII(static_cast<int>(windows_key_code)));
    253   }
    254 }
    255 
    256 blink::WebTouchEvent CreateWebTouchEventFromMotionEvent(
    257     const ui::MotionEvent& event) {
    258   COMPILE_ASSERT(static_cast<int>(MotionEvent::MAX_TOUCH_POINT_COUNT) ==
    259                      static_cast<int>(blink::WebTouchEvent::touchesLengthCap),
    260                  inconsistent_maximum_number_of_active_touch_points);
    261 
    262   blink::WebTouchEvent result;
    263 
    264   WebTouchEventTraits::ResetType(
    265       ToWebInputEventType(event.GetAction()),
    266       (event.GetEventTime() - base::TimeTicks()).InSecondsF(),
    267       &result);
    268 
    269   result.modifiers = EventFlagsToWebEventModifiers(event.GetFlags());
    270   result.touchesLength =
    271       std::min(event.GetPointerCount(),
    272                static_cast<size_t>(WebTouchEvent::touchesLengthCap));
    273   DCHECK_GT(result.touchesLength, 0U);
    274 
    275   for (size_t i = 0; i < result.touchesLength; ++i)
    276     result.touches[i] = CreateWebTouchPoint(event, i);
    277 
    278   return result;
    279 }
    280 
    281 WebGestureEvent CreateWebGestureEventFromGestureEventData(
    282     const ui::GestureEventData& data) {
    283   WebGestureEvent gesture;
    284   gesture.modifiers = EventFlagsToWebEventModifiers(data.flags);
    285   gesture.x = data.x;
    286   gesture.y = data.y;
    287   gesture.globalX = data.raw_x;
    288   gesture.globalY = data.raw_y;
    289   gesture.timeStampSeconds = (data.time - base::TimeTicks()).InSecondsF();
    290   gesture.sourceDevice = blink::WebGestureDeviceTouchscreen;
    291 
    292   switch (data.type()) {
    293     case ui::ET_GESTURE_SHOW_PRESS:
    294       gesture.type = WebInputEvent::GestureShowPress;
    295       gesture.data.showPress.width = data.details.bounding_box_f().width();
    296       gesture.data.showPress.height = data.details.bounding_box_f().height();
    297       break;
    298     case ui::ET_GESTURE_DOUBLE_TAP:
    299       gesture.type = WebInputEvent::GestureDoubleTap;
    300       DCHECK_EQ(1, data.details.tap_count());
    301       gesture.data.tap.tapCount = data.details.tap_count();
    302       gesture.data.tap.width = data.details.bounding_box_f().width();
    303       gesture.data.tap.height = data.details.bounding_box_f().height();
    304       break;
    305     case ui::ET_GESTURE_TAP:
    306       gesture.type = WebInputEvent::GestureTap;
    307       DCHECK_EQ(1, data.details.tap_count());
    308       gesture.data.tap.tapCount = data.details.tap_count();
    309       gesture.data.tap.width = data.details.bounding_box_f().width();
    310       gesture.data.tap.height = data.details.bounding_box_f().height();
    311       break;
    312     case ui::ET_GESTURE_TAP_UNCONFIRMED:
    313       gesture.type = WebInputEvent::GestureTapUnconfirmed;
    314       DCHECK_EQ(1, data.details.tap_count());
    315       gesture.data.tap.tapCount = data.details.tap_count();
    316       gesture.data.tap.width = data.details.bounding_box_f().width();
    317       gesture.data.tap.height = data.details.bounding_box_f().height();
    318       break;
    319     case ui::ET_GESTURE_LONG_PRESS:
    320       gesture.type = WebInputEvent::GestureLongPress;
    321       gesture.data.longPress.width = data.details.bounding_box_f().width();
    322       gesture.data.longPress.height = data.details.bounding_box_f().height();
    323       break;
    324     case ui::ET_GESTURE_LONG_TAP:
    325       gesture.type = WebInputEvent::GestureLongTap;
    326       gesture.data.longPress.width = data.details.bounding_box_f().width();
    327       gesture.data.longPress.height = data.details.bounding_box_f().height();
    328       break;
    329     case ui::ET_GESTURE_SCROLL_BEGIN:
    330       gesture.type = WebInputEvent::GestureScrollBegin;
    331       gesture.data.scrollBegin.deltaXHint = data.details.scroll_x_hint();
    332       gesture.data.scrollBegin.deltaYHint = data.details.scroll_y_hint();
    333       break;
    334     case ui::ET_GESTURE_SCROLL_UPDATE:
    335       gesture.type = WebInputEvent::GestureScrollUpdate;
    336       gesture.data.scrollUpdate.deltaX = data.details.scroll_x();
    337       gesture.data.scrollUpdate.deltaY = data.details.scroll_y();
    338       break;
    339     case ui::ET_GESTURE_SCROLL_END:
    340       gesture.type = WebInputEvent::GestureScrollEnd;
    341       break;
    342     case ui::ET_SCROLL_FLING_START:
    343       gesture.type = WebInputEvent::GestureFlingStart;
    344       gesture.data.flingStart.velocityX = data.details.velocity_x();
    345       gesture.data.flingStart.velocityY = data.details.velocity_y();
    346       break;
    347     case ui::ET_SCROLL_FLING_CANCEL:
    348       gesture.type = WebInputEvent::GestureFlingCancel;
    349       break;
    350     case ui::ET_GESTURE_PINCH_BEGIN:
    351       gesture.type = WebInputEvent::GesturePinchBegin;
    352       break;
    353     case ui::ET_GESTURE_PINCH_UPDATE:
    354       gesture.type = WebInputEvent::GesturePinchUpdate;
    355       gesture.data.pinchUpdate.scale = data.details.scale();
    356       break;
    357     case ui::ET_GESTURE_PINCH_END:
    358       gesture.type = WebInputEvent::GesturePinchEnd;
    359       break;
    360     case ui::ET_GESTURE_TAP_CANCEL:
    361       gesture.type = WebInputEvent::GestureTapCancel;
    362       break;
    363     case ui::ET_GESTURE_TAP_DOWN:
    364       gesture.type = WebInputEvent::GestureTapDown;
    365       gesture.data.tapDown.width = data.details.bounding_box_f().width();
    366       gesture.data.tapDown.height = data.details.bounding_box_f().height();
    367       break;
    368     case ui::ET_GESTURE_BEGIN:
    369     case ui::ET_GESTURE_END:
    370       NOTREACHED() << "ET_GESTURE_BEGIN and ET_GESTURE_END are only produced "
    371                    << "in Aura, and should never end up here.";
    372       break;
    373     default:
    374       NOTREACHED() << "ui::EventType provided wasn't a valid gesture event.";
    375       break;
    376   }
    377 
    378   return gesture;
    379 }
    380 
    381 int EventFlagsToWebEventModifiers(int flags) {
    382   int modifiers = 0;
    383 
    384   if (flags & ui::EF_SHIFT_DOWN)
    385     modifiers |= blink::WebInputEvent::ShiftKey;
    386   if (flags & ui::EF_CONTROL_DOWN)
    387     modifiers |= blink::WebInputEvent::ControlKey;
    388   if (flags & ui::EF_ALT_DOWN)
    389     modifiers |= blink::WebInputEvent::AltKey;
    390   if (flags & ui::EF_COMMAND_DOWN)
    391     modifiers |= blink::WebInputEvent::MetaKey;
    392 
    393   if (flags & ui::EF_LEFT_MOUSE_BUTTON)
    394     modifiers |= blink::WebInputEvent::LeftButtonDown;
    395   if (flags & ui::EF_MIDDLE_MOUSE_BUTTON)
    396     modifiers |= blink::WebInputEvent::MiddleButtonDown;
    397   if (flags & ui::EF_RIGHT_MOUSE_BUTTON)
    398     modifiers |= blink::WebInputEvent::RightButtonDown;
    399   if (flags & ui::EF_CAPS_LOCK_DOWN)
    400     modifiers |= blink::WebInputEvent::CapsLockOn;
    401   if (flags & ui::EF_IS_REPEAT)
    402     modifiers |= blink::WebInputEvent::IsAutoRepeat;
    403   if (flags & ui::EF_NUMPAD_KEY)
    404     modifiers |= blink::WebInputEvent::IsKeyPad;
    405 
    406   return modifiers;
    407 }
    408 
    409 int WebEventModifiersToEventFlags(int modifiers) {
    410   int flags = 0;
    411 
    412   if (modifiers & blink::WebInputEvent::ShiftKey)
    413     flags |= ui::EF_SHIFT_DOWN;
    414   if (modifiers & blink::WebInputEvent::ControlKey)
    415     flags |= ui::EF_CONTROL_DOWN;
    416   if (modifiers & blink::WebInputEvent::AltKey)
    417     flags |= ui::EF_ALT_DOWN;
    418   if (modifiers & blink::WebInputEvent::MetaKey)
    419     flags |= ui::EF_COMMAND_DOWN;
    420 
    421   if (modifiers & blink::WebInputEvent::LeftButtonDown)
    422     flags |= ui::EF_LEFT_MOUSE_BUTTON;
    423   if (modifiers & blink::WebInputEvent::MiddleButtonDown)
    424     flags |= ui::EF_MIDDLE_MOUSE_BUTTON;
    425   if (modifiers & blink::WebInputEvent::RightButtonDown)
    426     flags |= ui::EF_RIGHT_MOUSE_BUTTON;
    427   if (modifiers & blink::WebInputEvent::CapsLockOn)
    428     flags |= ui::EF_CAPS_LOCK_DOWN;
    429   if (modifiers & blink::WebInputEvent::IsAutoRepeat)
    430     flags |= ui::EF_IS_REPEAT;
    431   if (modifiers & blink::WebInputEvent::IsKeyPad)
    432     flags |= ui::EF_NUMPAD_KEY;
    433 
    434   return flags;
    435 }
    436 
    437 }  // namespace content
    438