| // Copyright (c) 2017 The Chromium Embedded Framework Authors. |
| // Portions copyright (c) 2012 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 "libcef/browser/osr/motion_event_osr.h" |
| |
| #include <algorithm> |
| |
| #include "ui/events/base_event_utils.h" |
| #include "ui/events/gesture_detection/gesture_configuration.h" |
| |
| namespace { |
| |
| ui::MotionEvent::ToolType CefPointerTypeToMotionEventToolType( |
| cef_pointer_type_t pointer_type) { |
| switch (pointer_type) { |
| case CEF_POINTER_TYPE_TOUCH: |
| return ui::MotionEvent::ToolType::FINGER; |
| case CEF_POINTER_TYPE_MOUSE: |
| return ui::MotionEvent::ToolType::MOUSE; |
| case CEF_POINTER_TYPE_PEN: |
| return ui::MotionEvent::ToolType::STYLUS; |
| case CEF_POINTER_TYPE_ERASER: |
| return ui::MotionEvent::ToolType::ERASER; |
| case CEF_POINTER_TYPE_UNKNOWN: |
| return ui::MotionEvent::ToolType::UNKNOWN; |
| } |
| NOTREACHED(); |
| return ui::MotionEvent::ToolType::UNKNOWN; |
| } |
| |
| } // namespace |
| |
| CefMotionEventOSR::CefMotionEventOSR() { |
| std::fill(id_map_, id_map_ + blink::WebTouchEvent::kTouchesLengthCap, -1); |
| } |
| |
| CefMotionEventOSR::~CefMotionEventOSR() {} |
| |
| int CefMotionEventOSR::GetSourceDeviceId(size_t pointer_index) const { |
| if (IsValidIndex(pointer_index)) |
| return pointer(pointer_index).source_device_id; |
| else |
| return -1; |
| } |
| |
| // Returns true if the touch was valid. |
| bool CefMotionEventOSR::OnTouch(const CefTouchEvent& touch) { |
| int id = LookupId(touch.id); |
| bool pointer_id_is_active = id != -1; |
| |
| if (touch.type == CEF_TET_PRESSED && pointer_id_is_active) { |
| // Ignore pressed events for already active touches. |
| return false; |
| } else if (touch.type != CEF_TET_PRESSED && !pointer_id_is_active) { |
| // When a window begins capturing touch events, we could have an active |
| // touch stream transfered to us, resulting in touch move or touch up |
| // events without associated touch down events. Ignore them. |
| return false; |
| } |
| |
| switch (touch.type) { |
| case CEF_TET_PRESSED: |
| id = AddId(touch.id); |
| if (id == -1) |
| return false; |
| if (!AddTouch(touch, id)) |
| return false; |
| break; |
| |
| case CEF_TET_MOVED: { |
| // Discard if touch is stationary. |
| int index = FindPointerIndexOfId(id); |
| if (IsValidIndex(index) && |
| (touch.x == GetX(index) && touch.y == GetY(index))) { |
| return false; |
| } |
| } |
| [[fallthrough]]; |
| // No break. |
| case CEF_TET_RELEASED: |
| case CEF_TET_CANCELLED: |
| // Removing these touch points needs to be postponed until after the |
| // MotionEvent has been dispatched. This cleanup occurs in |
| // CleanupRemovedTouchPoints. |
| UpdateTouch(touch, id); |
| break; |
| } |
| |
| UpdateCachedAction(touch, id); |
| set_unique_event_id(ui::GetNextTouchEventId()); |
| set_flags(touch.modifiers); |
| set_event_time(base::TimeTicks::Now()); |
| return true; |
| } |
| |
| // We can't cleanup removed touch points immediately upon receipt of a |
| // TouchCancel or TouchRelease, as the MotionEvent needs to be able to report |
| // information about those touch events. Once the MotionEvent has been |
| // processed, we call CleanupRemovedTouchPoints to do the required |
| // book-keeping. |
| void CefMotionEventOSR::CleanupRemovedTouchPoints(const CefTouchEvent& event) { |
| if (event.type != CEF_TET_RELEASED && event.type != CEF_TET_CANCELLED) { |
| return; |
| } |
| |
| DCHECK(GetPointerCount()); |
| |
| int id = LookupId(event.id); |
| int index_to_delete = FindPointerIndexOfId(id); |
| set_action_index(-1); |
| set_action(ui::MotionEvent::Action::NONE); |
| if (IsValidIndex(index_to_delete)) { |
| pointer(index_to_delete) = pointer(GetPointerCount() - 1); |
| PopPointer(); |
| RemoveId(event.id); |
| } |
| } |
| |
| // Reset unchanged touch point to StateStationary for touchmove and touchcancel. |
| void CefMotionEventOSR::MarkUnchangedTouchPointsAsStationary( |
| blink::WebTouchEvent* event, |
| const CefTouchEvent& cef_event) { |
| int id = LookupId(cef_event.id); |
| if (event->GetType() == blink::WebInputEvent::kTouchMove || |
| event->GetType() == blink::WebInputEvent::kTouchCancel) { |
| for (size_t i = 0; i < event->touches_length; ++i) { |
| if (event->touches[i].id != id) |
| event->touches[i].state = blink::WebTouchPoint::kStateStationary; |
| } |
| } |
| } |
| |
| int CefMotionEventOSR::LookupId(int id) { |
| if (id == -1) |
| return -1; |
| |
| for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) { |
| if (id_map_[i] == id) { |
| return i; |
| } |
| } |
| return -1; |
| } |
| |
| int CefMotionEventOSR::AddId(int id) { |
| if (id == -1 || LookupId(id) >= 0) |
| return -1; |
| |
| for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) { |
| if (id_map_[i] == -1) { |
| id_map_[i] = id; |
| return i; |
| } |
| } |
| |
| return -1; |
| } |
| |
| void CefMotionEventOSR::RemoveId(int id) { |
| for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) { |
| if (id_map_[i] == id) { |
| id_map_[i] = -1; |
| } |
| } |
| } |
| |
| bool CefMotionEventOSR::AddTouch(const CefTouchEvent& touch, int id) { |
| if (GetPointerCount() == MotionEvent::MAX_TOUCH_POINT_COUNT) |
| return false; |
| |
| PushPointer(GetPointerPropertiesFromTouchEvent(touch, id)); |
| return true; |
| } |
| |
| void CefMotionEventOSR::UpdateTouch(const CefTouchEvent& touch, int id) { |
| int index_to_update = FindPointerIndexOfId(id); |
| if (IsValidIndex(index_to_update)) |
| pointer(index_to_update) = GetPointerPropertiesFromTouchEvent(touch, id); |
| } |
| |
| void CefMotionEventOSR::UpdateCachedAction(const CefTouchEvent& touch, int id) { |
| DCHECK(GetPointerCount()); |
| switch (touch.type) { |
| case CEF_TET_PRESSED: |
| if (GetPointerCount() == 1) { |
| set_action(ui::MotionEvent::Action::DOWN); |
| } else { |
| set_action(ui::MotionEvent::Action::POINTER_DOWN); |
| set_action_index(FindPointerIndexOfId(id)); |
| } |
| break; |
| case CEF_TET_RELEASED: |
| if (GetPointerCount() == 1) { |
| set_action(ui::MotionEvent::Action::UP); |
| } else { |
| set_action(ui::MotionEvent::Action::POINTER_UP); |
| set_action_index(FindPointerIndexOfId(id)); |
| } |
| break; |
| case CEF_TET_CANCELLED: |
| set_action(ui::MotionEvent::Action::CANCEL); |
| break; |
| case CEF_TET_MOVED: |
| set_action(ui::MotionEvent::Action::MOVE); |
| break; |
| } |
| } |
| |
| bool CefMotionEventOSR::IsValidIndex(int index) const { |
| return (index >= 0) && (index < static_cast<int>(GetPointerCount())); |
| } |
| |
| ui::PointerProperties CefMotionEventOSR::GetPointerPropertiesFromTouchEvent( |
| const CefTouchEvent& touch, |
| int id) { |
| ui::PointerProperties pointer_properties; |
| pointer_properties.x = touch.x; |
| pointer_properties.y = touch.y; |
| pointer_properties.raw_x = touch.x; |
| pointer_properties.raw_y = touch.y; |
| pointer_properties.id = id; |
| pointer_properties.pressure = touch.pressure; |
| pointer_properties.source_device_id = 0; |
| |
| pointer_properties.SetAxesAndOrientation(touch.radius_x, touch.radius_y, |
| touch.rotation_angle); |
| if (!pointer_properties.touch_major) { |
| float default_size; |
| switch (touch.pointer_type) { |
| case CEF_POINTER_TYPE_PEN: |
| case CEF_POINTER_TYPE_ERASER: |
| // Default size for stylus events is 1x1. |
| default_size = 1; |
| break; |
| default: |
| default_size = |
| 2.f * ui::GestureConfiguration::GetInstance()->default_radius(); |
| break; |
| } |
| pointer_properties.touch_major = pointer_properties.touch_minor = |
| default_size; |
| pointer_properties.orientation = 0; |
| } |
| |
| pointer_properties.tool_type = |
| CefPointerTypeToMotionEventToolType(touch.pointer_type); |
| |
| return pointer_properties; |
| } |