1 // Copyright 2020 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 #include "ui/ozone/platform/wayland/host/wayland_event_source.h"
6 
7 #include <algorithm>
8 #include <memory>
9 
10 #include "base/bind.h"
11 #include "base/check.h"
12 #include "base/optional.h"
13 #include "base/time/time.h"
14 #include "ui/events/base_event_utils.h"
15 #include "ui/events/event.h"
16 #include "ui/events/keycodes/dom/dom_code.h"
17 #include "ui/events/keycodes/dom/dom_key.h"
18 #include "ui/events/keycodes/keyboard_code_conversion.h"
19 #include "ui/events/keycodes/keyboard_codes.h"
20 #include "ui/events/pointer_details.h"
21 #include "ui/events/types/event_type.h"
22 #include "ui/gfx/geometry/point_f.h"
23 #include "ui/gfx/geometry/vector2d_f.h"
24 #include "ui/ozone/platform/wayland/host/wayland_connection.h"
25 #include "ui/ozone/platform/wayland/host/wayland_event_watcher.h"
26 #include "ui/ozone/platform/wayland/host/wayland_keyboard.h"
27 #include "ui/ozone/platform/wayland/host/wayland_window.h"
28 #include "ui/ozone/platform/wayland/host/wayland_window_manager.h"
29 
30 namespace ui {
31 
32 namespace {
33 
HasAnyPointerButtonFlag(int flags)34 bool HasAnyPointerButtonFlag(int flags) {
35   return (flags & (EF_LEFT_MOUSE_BUTTON | EF_MIDDLE_MOUSE_BUTTON |
36                    EF_RIGHT_MOUSE_BUTTON | EF_BACK_MOUSE_BUTTON |
37                    EF_FORWARD_MOUSE_BUTTON)) != 0;
38 }
39 
40 // Number of fingers for scroll gestures.
41 constexpr int kGestureScrollFingerCount = 2;
42 
43 // Maximum size of the stored recent pointer frame information.
44 constexpr int kRecentPointerFrameMaxSize = 20;
45 
46 }  // namespace
47 
48 struct WaylandEventSource::TouchPoint {
49   TouchPoint(gfx::PointF location, WaylandWindow* current_window);
50   ~TouchPoint() = default;
51 
52   WaylandWindow* const window;
53   gfx::PointF last_known_location;
54 };
55 
TouchPoint(gfx::PointF location,WaylandWindow * current_window)56 WaylandEventSource::TouchPoint::TouchPoint(gfx::PointF location,
57                                            WaylandWindow* current_window)
58     : window(current_window), last_known_location(location) {
59   DCHECK(window);
60 }
61 
62 // WaylandEventSource implementation
63 
WaylandEventSource(wl_display * display,WaylandWindowManager * window_manager)64 WaylandEventSource::WaylandEventSource(wl_display* display,
65                                        WaylandWindowManager* window_manager)
66     : window_manager_(window_manager),
67       event_watcher_(std::make_unique<WaylandEventWatcher>(display)) {
68   DCHECK(window_manager_);
69 
70   // Observes remove changes to know when touch points can be removed.
71   window_manager_->AddObserver(this);
72 }
73 
74 WaylandEventSource::~WaylandEventSource() = default;
75 
SetShutdownCb(base::OnceCallback<void ()> shutdown_cb)76 void WaylandEventSource::SetShutdownCb(base::OnceCallback<void()> shutdown_cb) {
77   event_watcher_->SetShutdownCb(std::move(shutdown_cb));
78 }
79 
StartProcessingEvents()80 bool WaylandEventSource::StartProcessingEvents() {
81   return event_watcher_->StartProcessingEvents();
82 }
83 
StopProcessingEvents()84 bool WaylandEventSource::StopProcessingEvents() {
85   return event_watcher_->StopProcessingEvents();
86 }
87 
OnKeyboardCreated(WaylandKeyboard * keyboard)88 void WaylandEventSource::OnKeyboardCreated(WaylandKeyboard* keyboard) {
89   DCHECK(keyboard);
90   keyboard_ = keyboard;
91 }
92 
OnKeyboardDestroyed(WaylandKeyboard * keyboard)93 void WaylandEventSource::OnKeyboardDestroyed(WaylandKeyboard* keyboard) {
94   DCHECK_EQ(keyboard_, keyboard);
95   keyboard_modifiers_ = 0;
96   keyboard_ = nullptr;
97 }
98 
OnKeyboardFocusChanged(WaylandWindow * window,bool focused)99 void WaylandEventSource::OnKeyboardFocusChanged(WaylandWindow* window,
100                                                 bool focused) {
101   DCHECK(window);
102   HandleKeyboardFocusChange(window, focused);
103 }
104 
OnKeyboardModifiersChanged(int modifiers)105 void WaylandEventSource::OnKeyboardModifiersChanged(int modifiers) {
106   keyboard_modifiers_ = modifiers;
107 }
108 
OnKeyboardKeyEvent(EventType type,DomCode dom_code,bool repeat,base::TimeTicks timestamp)109 uint32_t WaylandEventSource::OnKeyboardKeyEvent(EventType type,
110                                                 DomCode dom_code,
111                                                 bool repeat,
112                                                 base::TimeTicks timestamp) {
113   DCHECK(type == ET_KEY_PRESSED || type == ET_KEY_RELEASED);
114   if (!keyboard_)
115     return POST_DISPATCH_NONE;
116 
117   DomKey dom_key;
118   KeyboardCode key_code;
119   if (!keyboard_->Decode(dom_code, keyboard_modifiers_, &dom_key, &key_code)) {
120     LOG(ERROR) << "Failed to decode key event.";
121     return POST_DISPATCH_NONE;
122   }
123 
124   if (!repeat) {
125     int flag = ModifierDomKeyToEventFlag(dom_key);
126     UpdateKeyboardModifiers(flag, type == ET_KEY_PRESSED);
127   }
128 
129   KeyEvent event(type, key_code, dom_code, keyboard_modifiers_, dom_key,
130                  timestamp);
131   event.set_source_device_id(keyboard_->device_id());
132   return DispatchEvent(&event);
133 }
134 
OnPointerCreated(WaylandPointer * pointer)135 void WaylandEventSource::OnPointerCreated(WaylandPointer* pointer) {
136   DCHECK(pointer);
137   pointer_ = pointer;
138 }
139 
OnPointerDestroyed(WaylandPointer * pointer)140 void WaylandEventSource::OnPointerDestroyed(WaylandPointer* pointer) {
141   DCHECK_EQ(pointer_, pointer);
142 
143   // Clear focused window, if any.
144   HandlePointerFocusChange(nullptr);
145 
146   ResetPointerFlags();
147   pointer_ = nullptr;
148 }
149 
OnPointerFocusChanged(WaylandWindow * window,const gfx::PointF & location)150 void WaylandEventSource::OnPointerFocusChanged(WaylandWindow* window,
151                                                const gfx::PointF& location) {
152   if (!pointer_)
153     return;
154 
155   // Save new pointer location.
156   pointer_location_ = location;
157 
158   bool focused = !!window;
159   if (focused)
160     HandlePointerFocusChange(window);
161 
162   EventType type = focused ? ET_MOUSE_ENTERED : ET_MOUSE_EXITED;
163   MouseEvent event(type, location, location, EventTimeForNow(), pointer_flags_,
164                    0);
165   DispatchEvent(&event);
166 
167   if (!focused)
168     HandlePointerFocusChange(nullptr);
169 }
170 
OnPointerButtonEvent(EventType type,int changed_button,WaylandWindow * window)171 void WaylandEventSource::OnPointerButtonEvent(EventType type,
172                                               int changed_button,
173                                               WaylandWindow* window) {
174   DCHECK(type == ET_MOUSE_PRESSED || type == ET_MOUSE_RELEASED);
175   DCHECK(HasAnyPointerButtonFlag(changed_button));
176 
177   if (!pointer_)
178     return;
179 
180   auto* prev_focused_window = window_with_pointer_focus_;
181   if (window)
182     HandlePointerFocusChange(window);
183 
184   pointer_flags_ = type == ET_MOUSE_PRESSED
185                        ? (pointer_flags_ | changed_button)
186                        : (pointer_flags_ & ~changed_button);
187   last_pointer_button_pressed_ = changed_button;
188   // MouseEvent's flags should contain the button that was released too.
189   int flags = pointer_flags_ | keyboard_modifiers_ | changed_button;
190   MouseEvent event(type, pointer_location_, pointer_location_,
191                    EventTimeForNow(), flags, changed_button);
192   DispatchEvent(&event);
193 
194   if (window)
195     HandlePointerFocusChange(prev_focused_window);
196 }
197 
OnPointerMotionEvent(const gfx::PointF & location)198 void WaylandEventSource::OnPointerMotionEvent(const gfx::PointF& location) {
199   pointer_location_ = location;
200   int flags = pointer_flags_ | keyboard_modifiers_;
201   MouseEvent event(ET_MOUSE_MOVED, pointer_location_, pointer_location_,
202                    EventTimeForNow(), flags, 0);
203   DispatchEvent(&event);
204 }
205 
OnPointerAxisEvent(const gfx::Vector2d & offset)206 void WaylandEventSource::OnPointerAxisEvent(const gfx::Vector2d& offset) {
207   int flags = pointer_flags_ | keyboard_modifiers_;
208   MouseWheelEvent event(offset, pointer_location_, pointer_location_,
209                         EventTimeForNow(), flags, 0);
210   DispatchEvent(&event);
211   current_pointer_frame_.dx += offset.x();
212   current_pointer_frame_.dy += offset.y();
213 }
214 
OnPointerFrameEvent()215 void WaylandEventSource::OnPointerFrameEvent() {
216   base::TimeTicks now = EventTimeForNow();
217   current_pointer_frame_.dt = now - last_pointer_frame_time_;
218   last_pointer_frame_time_ = now;
219 
220   // Dispatch Fling event if pointer.axis_stop is notified and the recent
221   // pointer.axis events meets the criteria to start fling scroll.
222   if (current_pointer_frame_.dx == 0 && current_pointer_frame_.dy == 0 &&
223       current_pointer_frame_.is_axis_stop) {
224     gfx::Vector2dF initial_velocity = ComputeFlingVelocity();
225     float vx = initial_velocity.x();
226     float vy = initial_velocity.y();
227     ScrollEvent event(
228         vx == 0 && vy == 0 ? ET_SCROLL_FLING_CANCEL : ET_SCROLL_FLING_START,
229         pointer_location_, pointer_location_, now,
230         pointer_flags_ | keyboard_modifiers_, vx, vy, vx, vy,
231         kGestureScrollFingerCount);
232     DispatchEvent(&event);
233     recent_pointer_frames_.clear();
234   } else {
235     if (recent_pointer_frames_.size() + 1 > kRecentPointerFrameMaxSize)
236       recent_pointer_frames_.pop_back();
237     recent_pointer_frames_.push_front(current_pointer_frame_);
238   }
239   // Reset |current_pointer_frame_|.
240   current_pointer_frame_.dx = 0;
241   current_pointer_frame_.dy = 0;
242   current_pointer_frame_.is_axis_stop = false;
243 }
244 
OnPointerAxisSourceEvent(uint32_t axis_source)245 void WaylandEventSource::OnPointerAxisSourceEvent(uint32_t axis_source) {
246   current_pointer_frame_.axis_source = axis_source;
247 }
248 
OnPointerAxisStopEvent(uint32_t axis)249 void WaylandEventSource::OnPointerAxisStopEvent(uint32_t axis) {
250   if (axis == WL_POINTER_AXIS_VERTICAL_SCROLL) {
251     current_pointer_frame_.dy = 0;
252   } else if (axis == WL_POINTER_AXIS_HORIZONTAL_SCROLL) {
253     current_pointer_frame_.dx = 0;
254   }
255   current_pointer_frame_.is_axis_stop = true;
256 }
257 
OnTouchCreated(WaylandTouch * touch)258 void WaylandEventSource::OnTouchCreated(WaylandTouch* touch) {
259   DCHECK(touch);
260   touch_ = touch;
261 }
262 
OnTouchDestroyed(WaylandTouch * touch)263 void WaylandEventSource::OnTouchDestroyed(WaylandTouch* touch) {
264   DCHECK_EQ(touch_, touch);
265   touch_points_.clear();
266   touch_ = nullptr;
267 }
268 
OnTouchPressEvent(WaylandWindow * window,const gfx::PointF & location,base::TimeTicks timestamp,PointerId id)269 void WaylandEventSource::OnTouchPressEvent(WaylandWindow* window,
270                                            const gfx::PointF& location,
271                                            base::TimeTicks timestamp,
272                                            PointerId id) {
273   DCHECK(window);
274   HandleTouchFocusChange(window, true);
275 
276   // Make sure this touch point wasn't present before.
277   auto success = touch_points_.try_emplace(
278       id, std::make_unique<TouchPoint>(location, window));
279   if (!success.second) {
280     LOG(WARNING) << "Touch down fired with wrong id";
281     return;
282   }
283 
284   PointerDetails details(EventPointerType::kTouch, id);
285   TouchEvent event(ET_TOUCH_PRESSED, location, location, timestamp, details);
286   DispatchEvent(&event);
287 }
288 
OnTouchReleaseEvent(base::TimeTicks timestamp,PointerId id)289 void WaylandEventSource::OnTouchReleaseEvent(base::TimeTicks timestamp,
290                                              PointerId id) {
291   // Make sure this touch point was present before.
292   const auto it = touch_points_.find(id);
293   if (it == touch_points_.end()) {
294     LOG(WARNING) << "Touch up fired with no matching touch down";
295     return;
296   }
297 
298   TouchPoint* touch_point = it->second.get();
299   gfx::PointF location = touch_point->last_known_location;
300   PointerDetails details(EventPointerType::kTouch, id);
301 
302   TouchEvent event(ET_TOUCH_RELEASED, location, location, timestamp, details);
303   DispatchEvent(&event);
304 
305   HandleTouchFocusChange(touch_point->window, false, id);
306   touch_points_.erase(it);
307 }
308 
OnTouchMotionEvent(const gfx::PointF & location,base::TimeTicks timestamp,PointerId id)309 void WaylandEventSource::OnTouchMotionEvent(const gfx::PointF& location,
310                                             base::TimeTicks timestamp,
311                                             PointerId id) {
312   const auto it = touch_points_.find(id);
313   // Make sure this touch point was present before.
314   if (it == touch_points_.end()) {
315     LOG(WARNING) << "Touch event fired with wrong id";
316     return;
317   }
318   it->second->last_known_location = location;
319   PointerDetails details(EventPointerType::kTouch, id);
320   TouchEvent event(ET_TOUCH_MOVED, location, location, timestamp, details);
321   DispatchEvent(&event);
322 }
323 
OnTouchCancelEvent()324 void WaylandEventSource::OnTouchCancelEvent() {
325   gfx::PointF location;
326   base::TimeTicks timestamp = base::TimeTicks::Now();
327   for (auto& touch_point : touch_points_) {
328     PointerId id = touch_point.first;
329     TouchEvent event(ET_TOUCH_CANCELLED, location, location, timestamp,
330                      PointerDetails(EventPointerType::kTouch, id));
331     DispatchEvent(&event);
332     HandleTouchFocusChange(touch_point.second->window, false);
333   }
334   touch_points_.clear();
335 }
336 
IsPointerButtonPressed(EventFlags button) const337 bool WaylandEventSource::IsPointerButtonPressed(EventFlags button) const {
338   DCHECK(HasAnyPointerButtonFlag(button));
339   return pointer_flags_ & button;
340 }
341 
ResetPointerFlags()342 void WaylandEventSource::ResetPointerFlags() {
343   pointer_flags_ = 0;
344 }
345 
OnDispatcherListChanged()346 void WaylandEventSource::OnDispatcherListChanged() {
347   StartProcessingEvents();
348 }
349 
OnWindowRemoved(WaylandWindow * window)350 void WaylandEventSource::OnWindowRemoved(WaylandWindow* window) {
351   // Clear pointer-related data.
352   if (window == window_with_pointer_focus_)
353     window_with_pointer_focus_ = nullptr;
354 
355   // Clear touch-related data.
356   base::EraseIf(touch_points_, [window](const auto& point) {
357     return point.second->window == window;
358   });
359 }
360 
361 // Currently EF_MOD3_DOWN means that the CapsLock key is currently down, and
362 // EF_CAPS_LOCK_ON means the caps lock state is enabled (and the key may or
363 // may not be down, but usually isn't). There does need to be two different
364 // flags, since the physical CapsLock key is subject to remapping, but the
365 // caps lock state (which can be triggered in a variety of ways) is not.
366 //
367 // TODO(crbug.com/1076661): This is likely caused by some CrOS-specific code.
368 // Get rid of this function once it is properly guarded under OS_CHROMEOS.
UpdateKeyboardModifiers(int modifier,bool down)369 void WaylandEventSource::UpdateKeyboardModifiers(int modifier, bool down) {
370   if (modifier == EF_NONE)
371     return;
372 
373   if (modifier == EF_CAPS_LOCK_ON) {
374     modifier = (modifier & ~EF_CAPS_LOCK_ON) | EF_MOD3_DOWN;
375   }
376   keyboard_modifiers_ = down ? (keyboard_modifiers_ | modifier)
377                              : (keyboard_modifiers_ & ~modifier);
378 }
379 
HandleKeyboardFocusChange(WaylandWindow * window,bool focused)380 void WaylandEventSource::HandleKeyboardFocusChange(WaylandWindow* window,
381                                                    bool focused) {
382   DCHECK(window);
383   window->set_keyboard_focus(focused);
384 }
385 
HandlePointerFocusChange(WaylandWindow * window)386 void WaylandEventSource::HandlePointerFocusChange(WaylandWindow* window) {
387   // Focused window might have been destroyed at this point (eg: context menus),
388   // in this case, |window| is null.
389   if (window_with_pointer_focus_)
390     window_with_pointer_focus_->SetPointerFocus(false);
391 
392   window_with_pointer_focus_ = window;
393 
394   if (window_with_pointer_focus_)
395     window_with_pointer_focus_->SetPointerFocus(true);
396 }
397 
HandleTouchFocusChange(WaylandWindow * window,bool focused,base::Optional<PointerId> id)398 void WaylandEventSource::HandleTouchFocusChange(WaylandWindow* window,
399                                                 bool focused,
400                                                 base::Optional<PointerId> id) {
401   DCHECK(window);
402   bool actual_focus = id ? !ShouldUnsetTouchFocus(window, id.value()) : focused;
403   window->set_touch_focus(actual_focus);
404 }
405 
406 // Focus must not be unset if there is another touch point within |window|.
ShouldUnsetTouchFocus(WaylandWindow * win,PointerId id)407 bool WaylandEventSource::ShouldUnsetTouchFocus(WaylandWindow* win,
408                                                PointerId id) {
409   auto result = std::find_if(
410       touch_points_.begin(), touch_points_.end(),
411       [win, id](auto& p) { return p.second->window == win && p.first != id; });
412   return result == touch_points_.end();
413 }
414 
ComputeFlingVelocity()415 gfx::Vector2dF WaylandEventSource::ComputeFlingVelocity() {
416   // Return average velocity in the last 200ms.
417   // TODO(fukino): Make the formula similar to libgestures's
418   // RegressScrollVelocity(). crbug.com/1129263.
419   base::TimeDelta dt;
420   float dx = 0.0f;
421   float dy = 0.0f;
422   for (auto& frame : recent_pointer_frames_) {
423     if (frame.axis_source != WL_POINTER_AXIS_SOURCE_FINGER)
424       break;
425     if (frame.dx == 0 && frame.dy == 0)
426       break;
427     if (dt + frame.dt > base::TimeDelta::FromMilliseconds(200))
428       break;
429 
430     dx += frame.dx;
431     dy += frame.dy;
432     dt += frame.dt;
433   }
434   float dt_inv = 1.0f / dt.InSecondsF();
435   return dt.is_zero() ? gfx::Vector2dF()
436                       : gfx::Vector2dF(dx * dt_inv, dy * dt_inv);
437 }
438 
439 }  // namespace ui
440