Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(94)

Side by Side Diff: components/mus/ws/event_dispatcher.cc

Issue 2119963002: Move mus to //services/ui (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 4 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « components/mus/ws/event_dispatcher.h ('k') | components/mus/ws/event_dispatcher_delegate.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2015 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 "components/mus/ws/event_dispatcher.h"
6
7 #include <algorithm>
8
9 #include "base/time/time.h"
10 #include "components/mus/ws/accelerator.h"
11 #include "components/mus/ws/display.h"
12 #include "components/mus/ws/event_dispatcher_delegate.h"
13 #include "components/mus/ws/server_window.h"
14 #include "components/mus/ws/server_window_delegate.h"
15 #include "components/mus/ws/window_coordinate_conversions.h"
16 #include "components/mus/ws/window_finder.h"
17 #include "ui/events/event_utils.h"
18 #include "ui/gfx/geometry/point.h"
19 #include "ui/gfx/geometry/point_conversions.h"
20
21 namespace mus {
22 namespace ws {
23
24 using Entry = std::pair<uint32_t, std::unique_ptr<Accelerator>>;
25
26 namespace {
27
28 bool IsOnlyOneMouseButtonDown(int flags) {
29 const uint32_t button_only_flags =
30 flags & (ui::EF_LEFT_MOUSE_BUTTON | ui::EF_MIDDLE_MOUSE_BUTTON |
31 ui::EF_RIGHT_MOUSE_BUTTON);
32 return button_only_flags == ui::EF_LEFT_MOUSE_BUTTON ||
33 button_only_flags == ui::EF_MIDDLE_MOUSE_BUTTON ||
34 button_only_flags == ui::EF_RIGHT_MOUSE_BUTTON;
35 }
36
37 bool IsLocationInNonclientArea(const ServerWindow* target,
38 const gfx::Point& location) {
39 if (!target->parent())
40 return false;
41
42 gfx::Rect client_area(target->bounds().size());
43 client_area.Inset(target->client_area());
44 if (client_area.Contains(location))
45 return false;
46
47 for (const auto& rect : target->additional_client_areas()) {
48 if (rect.Contains(location))
49 return false;
50 }
51
52 return true;
53 }
54
55 uint32_t PointerId(const ui::LocatedEvent& event) {
56 if (event.IsPointerEvent())
57 return event.AsPointerEvent()->pointer_id();
58 if (event.IsMouseWheelEvent())
59 return ui::PointerEvent::kMousePointerId;
60
61 NOTREACHED();
62 return 0;
63 }
64
65 } // namespace
66
67 ////////////////////////////////////////////////////////////////////////////////
68
69 EventDispatcher::EventDispatcher(EventDispatcherDelegate* delegate)
70 : delegate_(delegate),
71 capture_window_(nullptr),
72 capture_window_client_id_(kInvalidClientId),
73 modal_window_controller_(this),
74 mouse_button_down_(false),
75 mouse_cursor_source_window_(nullptr),
76 mouse_cursor_in_non_client_area_(false) {}
77
78 EventDispatcher::~EventDispatcher() {
79 if (capture_window_) {
80 UnobserveWindow(capture_window_);
81 capture_window_ = nullptr;
82 capture_window_client_id_ = kInvalidClientId;
83 }
84 for (const auto& pair : pointer_targets_) {
85 if (pair.second.window)
86 UnobserveWindow(pair.second.window);
87 }
88 pointer_targets_.clear();
89 }
90
91 void EventDispatcher::Reset() {
92 if (capture_window_) {
93 CancelPointerEventsToTarget(capture_window_);
94 DCHECK(capture_window_ == nullptr);
95 }
96
97 while (!pointer_targets_.empty())
98 StopTrackingPointer(pointer_targets_.begin()->first);
99
100 mouse_button_down_ = false;
101 }
102
103 void EventDispatcher::SetMousePointerScreenLocation(
104 const gfx::Point& screen_location) {
105 DCHECK(pointer_targets_.empty());
106 mouse_pointer_last_location_ = screen_location;
107 UpdateCursorProviderByLastKnownLocation();
108 // Write our initial location back to our shared screen coordinate. This
109 // shouldn't cause problems because we already read the cursor before we
110 // process any events in views during window construction.
111 delegate_->OnMouseCursorLocationChanged(screen_location);
112 }
113
114 bool EventDispatcher::GetCurrentMouseCursor(int32_t* cursor_out) {
115 if (!mouse_cursor_source_window_)
116 return false;
117
118 *cursor_out = mouse_cursor_in_non_client_area_
119 ? mouse_cursor_source_window_->non_client_cursor()
120 : mouse_cursor_source_window_->cursor();
121 return true;
122 }
123
124 bool EventDispatcher::SetCaptureWindow(ServerWindow* window,
125 ClientSpecificId client_id) {
126 if (!window)
127 client_id = kInvalidClientId;
128
129 if (window == capture_window_ && client_id == capture_window_client_id_)
130 return true;
131
132 // A window that is blocked by a modal window cannot gain capture.
133 if (window && modal_window_controller_.IsWindowBlocked(window))
134 return false;
135
136 if (capture_window_) {
137 // Stop observing old capture window. |pointer_targets_| are cleared on
138 // initial setting of a capture window.
139 delegate_->OnServerWindowCaptureLost(capture_window_);
140 UnobserveWindow(capture_window_);
141 } else {
142 // Cancel implicit capture to all other windows.
143 for (const auto& pair : pointer_targets_) {
144 ServerWindow* target = pair.second.window;
145 if (!target)
146 continue;
147 UnobserveWindow(target);
148 if (target == window)
149 continue;
150
151 ui::EventType event_type = pair.second.is_mouse_event
152 ? ui::ET_POINTER_EXITED
153 : ui::ET_POINTER_CANCELLED;
154 ui::EventPointerType pointer_type =
155 pair.second.is_mouse_event ? ui::EventPointerType::POINTER_TYPE_MOUSE
156 : ui::EventPointerType::POINTER_TYPE_TOUCH;
157 // TODO(jonross): Track previous location in PointerTarget for sending
158 // cancels.
159 ui::PointerEvent event(
160 event_type, gfx::Point(), gfx::Point(), ui::EF_NONE, pair.first,
161 ui::PointerDetails(pointer_type), ui::EventTimeForNow());
162 DispatchToPointerTarget(pair.second, event);
163 }
164 pointer_targets_.clear();
165 }
166
167 // Set the capture before changing native capture; otherwise, the callback
168 // from native platform might try to set the capture again.
169 bool had_capture_window = capture_window_ != nullptr;
170 capture_window_ = window;
171 capture_window_client_id_ = client_id;
172
173 // Begin tracking the capture window if it is not yet being observed.
174 if (window) {
175 ObserveWindow(window);
176 // TODO(sky): this conditional is problematic for the case of capture moving
177 // to a different display.
178 if (!had_capture_window)
179 delegate_->SetNativeCapture(window);
180 } else {
181 delegate_->ReleaseNativeCapture();
182 if (!mouse_button_down_)
183 UpdateCursorProviderByLastKnownLocation();
184 }
185
186 return true;
187 }
188
189 void EventDispatcher::AddSystemModalWindow(ServerWindow* window) {
190 modal_window_controller_.AddSystemModalWindow(window);
191 }
192
193 void EventDispatcher::ReleaseCaptureBlockedByModalWindow(
194 const ServerWindow* modal_window) {
195 if (!capture_window_)
196 return;
197
198 if (modal_window_controller_.IsWindowBlockedBy(capture_window_,
199 modal_window)) {
200 SetCaptureWindow(nullptr, kInvalidClientId);
201 }
202 }
203
204 void EventDispatcher::ReleaseCaptureBlockedByAnyModalWindow() {
205 if (!capture_window_)
206 return;
207
208 if (modal_window_controller_.IsWindowBlocked(capture_window_))
209 SetCaptureWindow(nullptr, kInvalidClientId);
210 }
211
212 void EventDispatcher::UpdateNonClientAreaForCurrentWindow() {
213 if (mouse_cursor_source_window_) {
214 gfx::Point location = mouse_pointer_last_location_;
215 ServerWindow* target = FindDeepestVisibleWindowForEvents(&location);
216 if (target == mouse_cursor_source_window_) {
217 mouse_cursor_in_non_client_area_ =
218 mouse_cursor_source_window_
219 ? IsLocationInNonclientArea(mouse_cursor_source_window_, location)
220 : false;
221 }
222 }
223 }
224
225 void EventDispatcher::UpdateCursorProviderByLastKnownLocation() {
226 if (!mouse_button_down_) {
227 gfx::Point location = mouse_pointer_last_location_;
228 mouse_cursor_source_window_ = FindDeepestVisibleWindowForEvents(&location);
229
230 mouse_cursor_in_non_client_area_ =
231 mouse_cursor_source_window_
232 ? IsLocationInNonclientArea(mouse_cursor_source_window_, location)
233 : false;
234 }
235 }
236
237 bool EventDispatcher::AddAccelerator(uint32_t id,
238 mojom::EventMatcherPtr event_matcher) {
239 std::unique_ptr<Accelerator> accelerator(new Accelerator(id, *event_matcher));
240 // If an accelerator with the same id or matcher already exists, then abort.
241 for (const auto& pair : accelerators_) {
242 if (pair.first == id || accelerator->EqualEventMatcher(pair.second.get()))
243 return false;
244 }
245 accelerators_.insert(Entry(id, std::move(accelerator)));
246 return true;
247 }
248
249 void EventDispatcher::RemoveAccelerator(uint32_t id) {
250 auto it = accelerators_.find(id);
251 // Clients may pass bogus ids.
252 if (it != accelerators_.end())
253 accelerators_.erase(it);
254 }
255
256 void EventDispatcher::ProcessEvent(const ui::Event& event) {
257 if (event.IsKeyEvent()) {
258 const ui::KeyEvent* key_event = event.AsKeyEvent();
259 if (event.type() == ui::ET_KEY_PRESSED && !key_event->is_char()) {
260 Accelerator* pre_target =
261 FindAccelerator(*key_event, ui::mojom::AcceleratorPhase::PRE_TARGET);
262 if (pre_target) {
263 delegate_->OnAccelerator(pre_target->id(), event);
264 return;
265 }
266 }
267 ProcessKeyEvent(*key_event);
268 return;
269 }
270
271 if (event.IsPointerEvent() || event.IsMouseWheelEvent()) {
272 ProcessLocatedEvent(*event.AsLocatedEvent());
273 return;
274 }
275
276 NOTREACHED();
277 }
278
279 void EventDispatcher::ProcessKeyEvent(const ui::KeyEvent& event) {
280 Accelerator* post_target =
281 FindAccelerator(event, ui::mojom::AcceleratorPhase::POST_TARGET);
282 ServerWindow* focused_window =
283 delegate_->GetFocusedWindowForEventDispatcher();
284 if (focused_window) {
285 // Assume key events are for the client area.
286 const bool in_nonclient_area = false;
287 const ClientSpecificId client_id =
288 delegate_->GetEventTargetClientId(focused_window, in_nonclient_area);
289 delegate_->DispatchInputEventToWindow(focused_window, client_id, event,
290 post_target);
291 return;
292 }
293 delegate_->OnEventTargetNotFound(event);
294 if (post_target)
295 delegate_->OnAccelerator(post_target->id(), event);
296 }
297
298 void EventDispatcher::ProcessLocatedEvent(const ui::LocatedEvent& event) {
299 DCHECK(event.IsPointerEvent() || event.IsMouseWheelEvent());
300 const bool is_mouse_event =
301 event.IsMousePointerEvent() || event.IsMouseWheelEvent();
302
303 if (is_mouse_event) {
304 mouse_pointer_last_location_ = event.location();
305 delegate_->OnMouseCursorLocationChanged(event.root_location());
306 }
307
308 // Release capture on pointer up. For mouse we only release if there are
309 // no buttons down.
310 const bool is_pointer_going_up =
311 (event.type() == ui::ET_POINTER_UP ||
312 event.type() == ui::ET_POINTER_CANCELLED) &&
313 (!is_mouse_event || IsOnlyOneMouseButtonDown(event.flags()));
314
315 // Update mouse down state upon events which change it.
316 if (is_mouse_event) {
317 if (event.type() == ui::ET_POINTER_DOWN)
318 mouse_button_down_ = true;
319 else if (is_pointer_going_up)
320 mouse_button_down_ = false;
321 }
322
323 if (capture_window_) {
324 mouse_cursor_source_window_ = capture_window_;
325 DispatchToClient(capture_window_, capture_window_client_id_, event);
326 return;
327 }
328
329 const int32_t pointer_id = PointerId(event);
330 if (!IsTrackingPointer(pointer_id) ||
331 !pointer_targets_[pointer_id].is_pointer_down) {
332 const bool any_pointers_down = AreAnyPointersDown();
333 UpdateTargetForPointer(pointer_id, event);
334 if (is_mouse_event)
335 mouse_cursor_source_window_ = pointer_targets_[pointer_id].window;
336
337 PointerTarget& pointer_target = pointer_targets_[pointer_id];
338 if (pointer_target.is_pointer_down) {
339 if (is_mouse_event)
340 mouse_cursor_source_window_ = pointer_target.window;
341 if (!any_pointers_down) {
342 delegate_->SetFocusedWindowFromEventDispatcher(pointer_target.window);
343 delegate_->SetNativeCapture(pointer_target.window);
344 }
345 }
346 }
347
348 // When we release the mouse button, we want the cursor to be sourced from
349 // the window under the mouse pointer, even though we're sending the button
350 // up event to the window that had implicit capture. We have to set this
351 // before we perform dispatch because the Delegate is going to read this
352 // information from us.
353 if (is_pointer_going_up && is_mouse_event)
354 UpdateCursorProviderByLastKnownLocation();
355
356 DispatchToPointerTarget(pointer_targets_[pointer_id], event);
357
358 if (is_pointer_going_up) {
359 if (is_mouse_event)
360 pointer_targets_[pointer_id].is_pointer_down = false;
361 else
362 StopTrackingPointer(pointer_id);
363 if (!AreAnyPointersDown())
364 delegate_->ReleaseNativeCapture();
365 }
366 }
367
368 void EventDispatcher::StartTrackingPointer(
369 int32_t pointer_id,
370 const PointerTarget& pointer_target) {
371 DCHECK(!IsTrackingPointer(pointer_id));
372 ObserveWindow(pointer_target.window);
373 pointer_targets_[pointer_id] = pointer_target;
374 }
375
376 void EventDispatcher::StopTrackingPointer(int32_t pointer_id) {
377 DCHECK(IsTrackingPointer(pointer_id));
378 ServerWindow* window = pointer_targets_[pointer_id].window;
379 pointer_targets_.erase(pointer_id);
380 if (window)
381 UnobserveWindow(window);
382 }
383
384 void EventDispatcher::UpdateTargetForPointer(int32_t pointer_id,
385 const ui::LocatedEvent& event) {
386 if (!IsTrackingPointer(pointer_id)) {
387 StartTrackingPointer(pointer_id, PointerTargetForEvent(event));
388 return;
389 }
390
391 const PointerTarget pointer_target = PointerTargetForEvent(event);
392 if (pointer_target.window == pointer_targets_[pointer_id].window &&
393 pointer_target.in_nonclient_area ==
394 pointer_targets_[pointer_id].in_nonclient_area) {
395 // The targets are the same, only set the down state to true if necessary.
396 // Down going to up is handled by ProcessLocatedEvent().
397 if (pointer_target.is_pointer_down)
398 pointer_targets_[pointer_id].is_pointer_down = true;
399 return;
400 }
401
402 // The targets are changing. Send an exit if appropriate.
403 if (event.IsMousePointerEvent()) {
404 ui::PointerEvent exit_event(
405 ui::ET_POINTER_EXITED, event.location(), event.root_location(),
406 event.flags(), ui::PointerEvent::kMousePointerId,
407 ui::PointerDetails(ui::EventPointerType::POINTER_TYPE_MOUSE),
408 event.time_stamp());
409 DispatchToPointerTarget(pointer_targets_[pointer_id], exit_event);
410 }
411
412 // Technically we're updating in place, but calling start then stop makes for
413 // simpler code.
414 StopTrackingPointer(pointer_id);
415 StartTrackingPointer(pointer_id, pointer_target);
416 }
417
418 EventDispatcher::PointerTarget EventDispatcher::PointerTargetForEvent(
419 const ui::LocatedEvent& event) {
420 PointerTarget pointer_target;
421 gfx::Point location(event.location());
422 ServerWindow* target_window = FindDeepestVisibleWindowForEvents(&location);
423 pointer_target.window =
424 modal_window_controller_.GetTargetForWindow(target_window);
425 pointer_target.is_mouse_event = event.IsMousePointerEvent();
426 pointer_target.in_nonclient_area =
427 target_window != pointer_target.window ||
428 IsLocationInNonclientArea(pointer_target.window, location);
429 pointer_target.is_pointer_down = event.type() == ui::ET_POINTER_DOWN;
430 return pointer_target;
431 }
432
433 bool EventDispatcher::AreAnyPointersDown() const {
434 for (const auto& pair : pointer_targets_) {
435 if (pair.second.is_pointer_down)
436 return true;
437 }
438 return false;
439 }
440
441 void EventDispatcher::DispatchToPointerTarget(const PointerTarget& target,
442 const ui::LocatedEvent& event) {
443 if (!target.window) {
444 delegate_->OnEventTargetNotFound(event);
445 return;
446 }
447
448 if (target.is_mouse_event)
449 mouse_cursor_in_non_client_area_ = target.in_nonclient_area;
450
451 DispatchToClient(target.window, delegate_->GetEventTargetClientId(
452 target.window, target.in_nonclient_area),
453 event);
454 }
455
456 void EventDispatcher::DispatchToClient(ServerWindow* window,
457 ClientSpecificId client_id,
458 const ui::LocatedEvent& event) {
459 gfx::Point location(event.location());
460 gfx::Transform transform(GetTransformToWindow(window));
461 transform.TransformPoint(&location);
462 std::unique_ptr<ui::Event> clone = ui::Event::Clone(event);
463 clone->AsLocatedEvent()->set_location(location);
464 // TODO(jonross): add post-target accelerator support once accelerators
465 // support pointer events.
466 delegate_->DispatchInputEventToWindow(window, client_id, *clone, nullptr);
467 }
468
469 void EventDispatcher::CancelPointerEventsToTarget(ServerWindow* window) {
470 if (capture_window_ == window) {
471 UnobserveWindow(window);
472 capture_window_ = nullptr;
473 capture_window_client_id_ = kInvalidClientId;
474 mouse_button_down_ = false;
475 // A window only cares to be informed that it lost capture if it explicitly
476 // requested capture. A window can lose capture if another window gains
477 // explicit capture.
478 delegate_->OnServerWindowCaptureLost(window);
479 delegate_->ReleaseNativeCapture();
480 UpdateCursorProviderByLastKnownLocation();
481 return;
482 }
483
484 for (auto& pair : pointer_targets_) {
485 if (pair.second.window == window) {
486 UnobserveWindow(window);
487 pair.second.window = nullptr;
488 }
489 }
490 }
491
492 void EventDispatcher::ObserveWindow(ServerWindow* window) {
493 auto res = observed_windows_.insert(std::make_pair(window, 0u));
494 res.first->second++;
495 if (res.second)
496 window->AddObserver(this);
497 }
498
499 void EventDispatcher::UnobserveWindow(ServerWindow* window) {
500 auto it = observed_windows_.find(window);
501 DCHECK(it != observed_windows_.end());
502 DCHECK_LT(0u, it->second);
503 it->second--;
504 if (!it->second) {
505 window->RemoveObserver(this);
506 observed_windows_.erase(it);
507 }
508 }
509
510 Accelerator* EventDispatcher::FindAccelerator(
511 const ui::KeyEvent& event,
512 const ui::mojom::AcceleratorPhase phase) {
513 for (const auto& pair : accelerators_) {
514 if (pair.second->MatchesEvent(event, phase)) {
515 return pair.second.get();
516 }
517 }
518 return nullptr;
519 }
520
521 ServerWindow* EventDispatcher::FindDeepestVisibleWindowForEvents(
522 gfx::Point* location) {
523 ServerWindow* root = delegate_->GetRootWindowContaining(*location);
524 if (!root)
525 return nullptr;
526
527 return mus::ws::FindDeepestVisibleWindowForEvents(root, location);
528 }
529
530 void EventDispatcher::OnWillChangeWindowHierarchy(ServerWindow* window,
531 ServerWindow* new_parent,
532 ServerWindow* old_parent) {
533 // TODO(sky): moving to a different root likely needs to transfer capture.
534 // TODO(sky): this isn't quite right, I think the logic should be (assuming
535 // moving in same root and still drawn):
536 // . if there is capture and window is still in the same root, continue
537 // sending to it.
538 // . if there isn't capture, then reevaluate each of the pointer targets
539 // sending exit as necessary.
540 // http://crbug.com/613646 .
541 if (!new_parent || !new_parent->IsDrawn() ||
542 new_parent->GetRoot() != old_parent->GetRoot()) {
543 CancelPointerEventsToTarget(window);
544 }
545 }
546
547 void EventDispatcher::OnWindowVisibilityChanged(ServerWindow* window) {
548 CancelPointerEventsToTarget(window);
549 }
550
551 void EventDispatcher::OnWindowDestroyed(ServerWindow* window) {
552 CancelPointerEventsToTarget(window);
553
554 if (mouse_cursor_source_window_ == window)
555 mouse_cursor_source_window_ = nullptr;
556 }
557
558 } // namespace ws
559 } // namespace mus
OLDNEW
« no previous file with comments | « components/mus/ws/event_dispatcher.h ('k') | components/mus/ws/event_dispatcher_delegate.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698