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