• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2017 The Chromium Embedded Framework Authors.
2 // Portions copyright (c) 2012 The Chromium Authors. All rights reserved.
3 // Use of this source code is governed by a BSD-style license that can be
4 // found in the LICENSE file.
5 
6 #include "libcef/browser/osr/motion_event_osr.h"
7 
8 #include <algorithm>
9 
10 #include "ui/events/base_event_utils.h"
11 #include "ui/events/gesture_detection/gesture_configuration.h"
12 
13 namespace {
14 
CefPointerTypeToMotionEventToolType(cef_pointer_type_t pointer_type)15 ui::MotionEvent::ToolType CefPointerTypeToMotionEventToolType(
16     cef_pointer_type_t pointer_type) {
17   switch (pointer_type) {
18     case CEF_POINTER_TYPE_TOUCH:
19       return ui::MotionEvent::ToolType::FINGER;
20     case CEF_POINTER_TYPE_MOUSE:
21       return ui::MotionEvent::ToolType::MOUSE;
22     case CEF_POINTER_TYPE_PEN:
23       return ui::MotionEvent::ToolType::STYLUS;
24     case CEF_POINTER_TYPE_ERASER:
25       return ui::MotionEvent::ToolType::ERASER;
26     case CEF_POINTER_TYPE_UNKNOWN:
27       return ui::MotionEvent::ToolType::UNKNOWN;
28   }
29   NOTREACHED();
30   return ui::MotionEvent::ToolType::UNKNOWN;
31 }
32 
33 }  // namespace
34 
CefMotionEventOSR()35 CefMotionEventOSR::CefMotionEventOSR() {
36   std::fill(id_map_, id_map_ + blink::WebTouchEvent::kTouchesLengthCap, -1);
37 }
38 
~CefMotionEventOSR()39 CefMotionEventOSR::~CefMotionEventOSR() {}
40 
GetSourceDeviceId(size_t pointer_index) const41 int CefMotionEventOSR::GetSourceDeviceId(size_t pointer_index) const {
42   if (IsValidIndex(pointer_index))
43     return pointer(pointer_index).source_device_id;
44   else
45     return -1;
46 }
47 
48 // Returns true if the touch was valid.
OnTouch(const CefTouchEvent & touch)49 bool CefMotionEventOSR::OnTouch(const CefTouchEvent& touch) {
50   int id = LookupId(touch.id);
51   bool pointer_id_is_active = id != -1;
52 
53   if (touch.type == CEF_TET_PRESSED && pointer_id_is_active) {
54     // Ignore pressed events for already active touches.
55     return false;
56   } else if (touch.type != CEF_TET_PRESSED && !pointer_id_is_active) {
57     // When a window begins capturing touch events, we could have an active
58     // touch stream transfered to us, resulting in touch move or touch up
59     // events without associated touch down events. Ignore them.
60     return false;
61   }
62 
63   switch (touch.type) {
64     case CEF_TET_PRESSED:
65       id = AddId(touch.id);
66       if (id == -1)
67         return false;
68       if (!AddTouch(touch, id))
69         return false;
70       break;
71 
72     case CEF_TET_MOVED: {
73       // Discard if touch is stationary.
74       int index = FindPointerIndexOfId(id);
75       if (IsValidIndex(index) &&
76           (touch.x == GetX(index) && touch.y == GetY(index))) {
77         return false;
78       }
79     }
80       [[fallthrough]];
81     // No break.
82     case CEF_TET_RELEASED:
83     case CEF_TET_CANCELLED:
84       // Removing these touch points needs to be postponed until after the
85       // MotionEvent has been dispatched. This cleanup occurs in
86       // CleanupRemovedTouchPoints.
87       UpdateTouch(touch, id);
88       break;
89   }
90 
91   UpdateCachedAction(touch, id);
92   set_unique_event_id(ui::GetNextTouchEventId());
93   set_flags(touch.modifiers);
94   set_event_time(base::TimeTicks::Now());
95   return true;
96 }
97 
98 // We can't cleanup removed touch points immediately upon receipt of a
99 // TouchCancel or TouchRelease, as the MotionEvent needs to be able to report
100 // information about those touch events. Once the MotionEvent has been
101 // processed, we call CleanupRemovedTouchPoints to do the required
102 // book-keeping.
CleanupRemovedTouchPoints(const CefTouchEvent & event)103 void CefMotionEventOSR::CleanupRemovedTouchPoints(const CefTouchEvent& event) {
104   if (event.type != CEF_TET_RELEASED && event.type != CEF_TET_CANCELLED) {
105     return;
106   }
107 
108   DCHECK(GetPointerCount());
109 
110   int id = LookupId(event.id);
111   int index_to_delete = FindPointerIndexOfId(id);
112   set_action_index(-1);
113   set_action(ui::MotionEvent::Action::NONE);
114   if (IsValidIndex(index_to_delete)) {
115     pointer(index_to_delete) = pointer(GetPointerCount() - 1);
116     PopPointer();
117     RemoveId(event.id);
118   }
119 }
120 
121 // Reset unchanged touch point to StateStationary for touchmove and touchcancel.
MarkUnchangedTouchPointsAsStationary(blink::WebTouchEvent * event,const CefTouchEvent & cef_event)122 void CefMotionEventOSR::MarkUnchangedTouchPointsAsStationary(
123     blink::WebTouchEvent* event,
124     const CefTouchEvent& cef_event) {
125   int id = LookupId(cef_event.id);
126   if (event->GetType() == blink::WebInputEvent::Type::kTouchMove ||
127       event->GetType() == blink::WebInputEvent::Type::kTouchCancel) {
128     for (size_t i = 0; i < event->touches_length; ++i) {
129       if (event->touches[i].id != id)
130         event->touches[i].state = blink::WebTouchPoint::State::kStateStationary;
131     }
132   }
133 }
134 
LookupId(int id)135 int CefMotionEventOSR::LookupId(int id) {
136   if (id == -1)
137     return -1;
138 
139   for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
140     if (id_map_[i] == id) {
141       return i;
142     }
143   }
144   return -1;
145 }
146 
AddId(int id)147 int CefMotionEventOSR::AddId(int id) {
148   if (id == -1 || LookupId(id) >= 0)
149     return -1;
150 
151   for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
152     if (id_map_[i] == -1) {
153       id_map_[i] = id;
154       return i;
155     }
156   }
157 
158   return -1;
159 }
160 
RemoveId(int id)161 void CefMotionEventOSR::RemoveId(int id) {
162   for (int i = 0; i < blink::WebTouchEvent::kTouchesLengthCap; i++) {
163     if (id_map_[i] == id) {
164       id_map_[i] = -1;
165     }
166   }
167 }
168 
AddTouch(const CefTouchEvent & touch,int id)169 bool CefMotionEventOSR::AddTouch(const CefTouchEvent& touch, int id) {
170   if (GetPointerCount() == MotionEvent::MAX_TOUCH_POINT_COUNT)
171     return false;
172 
173   PushPointer(GetPointerPropertiesFromTouchEvent(touch, id));
174   return true;
175 }
176 
UpdateTouch(const CefTouchEvent & touch,int id)177 void CefMotionEventOSR::UpdateTouch(const CefTouchEvent& touch, int id) {
178   int index_to_update = FindPointerIndexOfId(id);
179   if (IsValidIndex(index_to_update))
180     pointer(index_to_update) = GetPointerPropertiesFromTouchEvent(touch, id);
181 }
182 
UpdateCachedAction(const CefTouchEvent & touch,int id)183 void CefMotionEventOSR::UpdateCachedAction(const CefTouchEvent& touch, int id) {
184   DCHECK(GetPointerCount());
185   switch (touch.type) {
186     case CEF_TET_PRESSED:
187       if (GetPointerCount() == 1) {
188         set_action(ui::MotionEvent::Action::DOWN);
189       } else {
190         set_action(ui::MotionEvent::Action::POINTER_DOWN);
191         set_action_index(FindPointerIndexOfId(id));
192       }
193       break;
194     case CEF_TET_RELEASED:
195       if (GetPointerCount() == 1) {
196         set_action(ui::MotionEvent::Action::UP);
197       } else {
198         set_action(ui::MotionEvent::Action::POINTER_UP);
199         set_action_index(FindPointerIndexOfId(id));
200       }
201       break;
202     case CEF_TET_CANCELLED:
203       set_action(ui::MotionEvent::Action::CANCEL);
204       break;
205     case CEF_TET_MOVED:
206       set_action(ui::MotionEvent::Action::MOVE);
207       break;
208   }
209 }
210 
IsValidIndex(int index) const211 bool CefMotionEventOSR::IsValidIndex(int index) const {
212   return (index >= 0) && (index < static_cast<int>(GetPointerCount()));
213 }
214 
GetPointerPropertiesFromTouchEvent(const CefTouchEvent & touch,int id)215 ui::PointerProperties CefMotionEventOSR::GetPointerPropertiesFromTouchEvent(
216     const CefTouchEvent& touch,
217     int id) {
218   ui::PointerProperties pointer_properties;
219   pointer_properties.x = touch.x;
220   pointer_properties.y = touch.y;
221   pointer_properties.raw_x = touch.x;
222   pointer_properties.raw_y = touch.y;
223   pointer_properties.id = id;
224   pointer_properties.pressure = touch.pressure;
225   pointer_properties.source_device_id = 0;
226 
227   pointer_properties.SetAxesAndOrientation(touch.radius_x, touch.radius_y,
228                                            touch.rotation_angle);
229   if (!pointer_properties.touch_major) {
230     float default_size;
231     switch (touch.pointer_type) {
232       case CEF_POINTER_TYPE_PEN:
233       case CEF_POINTER_TYPE_ERASER:
234         // Default size for stylus events is 1x1.
235         default_size = 1;
236         break;
237       default:
238         default_size =
239             2.f * ui::GestureConfiguration::GetInstance()->default_radius();
240         break;
241     }
242     pointer_properties.touch_major = pointer_properties.touch_minor =
243         default_size;
244     pointer_properties.orientation = 0;
245   }
246 
247   pointer_properties.tool_type =
248       CefPointerTypeToMotionEventToolType(touch.pointer_type);
249 
250   return pointer_properties;
251 }
252