OLD | NEW |
| (Empty) |
1 // Copyright 2016 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/window_manager_state.h" | |
6 | |
7 #include "base/memory/weak_ptr.h" | |
8 #include "components/mus/common/event_matcher_util.h" | |
9 #include "components/mus/ws/accelerator.h" | |
10 #include "components/mus/ws/display_manager.h" | |
11 #include "components/mus/ws/platform_display.h" | |
12 #include "components/mus/ws/server_window.h" | |
13 #include "components/mus/ws/user_display_manager.h" | |
14 #include "components/mus/ws/user_id_tracker.h" | |
15 #include "components/mus/ws/window_manager_display_root.h" | |
16 #include "components/mus/ws/window_server.h" | |
17 #include "components/mus/ws/window_tree.h" | |
18 #include "services/shell/public/interfaces/connector.mojom.h" | |
19 #include "ui/events/event.h" | |
20 | |
21 namespace mus { | |
22 namespace ws { | |
23 namespace { | |
24 | |
25 // Debug accelerator IDs start far above the highest valid Windows command ID | |
26 // (0xDFFF) and Chrome's highest IDC command ID. | |
27 const uint32_t kPrintWindowsDebugAcceleratorId = 1u << 31; | |
28 | |
29 base::TimeDelta GetDefaultAckTimerDelay() { | |
30 #if defined(NDEBUG) | |
31 return base::TimeDelta::FromMilliseconds(100); | |
32 #else | |
33 return base::TimeDelta::FromMilliseconds(1000); | |
34 #endif | |
35 } | |
36 | |
37 bool EventsCanBeCoalesced(const ui::Event& one, const ui::Event& two) { | |
38 if (one.type() != two.type() || one.flags() != two.flags()) | |
39 return false; | |
40 | |
41 // TODO(sad): wheel events can also be merged. | |
42 if (one.type() != ui::ET_POINTER_MOVED) | |
43 return false; | |
44 | |
45 return one.AsPointerEvent()->pointer_id() == | |
46 two.AsPointerEvent()->pointer_id(); | |
47 } | |
48 | |
49 std::unique_ptr<ui::Event> CoalesceEvents(std::unique_ptr<ui::Event> first, | |
50 std::unique_ptr<ui::Event> second) { | |
51 DCHECK(first->type() == ui::ET_POINTER_MOVED) | |
52 << " Non-move events cannot be merged yet."; | |
53 // For mouse moves, the new event just replaces the old event. | |
54 return second; | |
55 } | |
56 | |
57 const ServerWindow* GetEmbedRoot(const ServerWindow* window) { | |
58 DCHECK(window); | |
59 const ServerWindow* embed_root = window->parent(); | |
60 while (embed_root && embed_root->id().client_id == window->id().client_id) | |
61 embed_root = embed_root->parent(); | |
62 return embed_root; | |
63 } | |
64 | |
65 } // namespace | |
66 | |
67 class WindowManagerState::ProcessedEventTarget { | |
68 public: | |
69 ProcessedEventTarget(ServerWindow* window, | |
70 ClientSpecificId client_id, | |
71 Accelerator* accelerator) | |
72 : client_id_(client_id) { | |
73 tracker_.Add(window); | |
74 if (accelerator) | |
75 accelerator_ = accelerator->GetWeakPtr(); | |
76 } | |
77 | |
78 ~ProcessedEventTarget() {} | |
79 | |
80 // Return true if the event is still valid. The event becomes invalid if | |
81 // the window is destroyed while waiting to dispatch. | |
82 bool IsValid() const { return !tracker_.windows().empty(); } | |
83 | |
84 ServerWindow* window() { | |
85 DCHECK(IsValid()); | |
86 return tracker_.windows().front(); | |
87 } | |
88 | |
89 ClientSpecificId client_id() const { return client_id_; } | |
90 | |
91 base::WeakPtr<Accelerator> accelerator() { return accelerator_; } | |
92 | |
93 private: | |
94 ServerWindowTracker tracker_; | |
95 const ClientSpecificId client_id_; | |
96 base::WeakPtr<Accelerator> accelerator_; | |
97 | |
98 DISALLOW_COPY_AND_ASSIGN(ProcessedEventTarget); | |
99 }; | |
100 | |
101 WindowManagerState::QueuedEvent::QueuedEvent() {} | |
102 WindowManagerState::QueuedEvent::~QueuedEvent() {} | |
103 | |
104 WindowManagerState::WindowManagerState(WindowTree* window_tree) | |
105 : window_tree_(window_tree), event_dispatcher_(this), weak_factory_(this) { | |
106 frame_decoration_values_ = mojom::FrameDecorationValues::New(); | |
107 frame_decoration_values_->max_title_bar_button_width = 0u; | |
108 | |
109 AddDebugAccelerators(); | |
110 } | |
111 | |
112 WindowManagerState::~WindowManagerState() {} | |
113 | |
114 void WindowManagerState::SetFrameDecorationValues( | |
115 mojom::FrameDecorationValuesPtr values) { | |
116 got_frame_decoration_values_ = true; | |
117 frame_decoration_values_ = values.Clone(); | |
118 display_manager() | |
119 ->GetUserDisplayManager(user_id()) | |
120 ->OnFrameDecorationValuesChanged(); | |
121 } | |
122 | |
123 bool WindowManagerState::SetCapture(ServerWindow* window, | |
124 ClientSpecificId client_id) { | |
125 DCHECK(IsActive()); | |
126 if (capture_window() == window && | |
127 client_id == event_dispatcher_.capture_window_client_id()) { | |
128 return true; | |
129 } | |
130 #if !defined(NDEBUG) | |
131 if (window) { | |
132 WindowManagerDisplayRoot* display_root = | |
133 display_manager()->GetWindowManagerDisplayRoot(window); | |
134 DCHECK(display_root && display_root->window_manager_state() == this); | |
135 } | |
136 #endif | |
137 return event_dispatcher_.SetCaptureWindow(window, client_id); | |
138 } | |
139 | |
140 void WindowManagerState::ReleaseCaptureBlockedByModalWindow( | |
141 const ServerWindow* modal_window) { | |
142 event_dispatcher_.ReleaseCaptureBlockedByModalWindow(modal_window); | |
143 } | |
144 | |
145 void WindowManagerState::ReleaseCaptureBlockedByAnyModalWindow() { | |
146 event_dispatcher_.ReleaseCaptureBlockedByAnyModalWindow(); | |
147 } | |
148 | |
149 void WindowManagerState::AddSystemModalWindow(ServerWindow* window) { | |
150 DCHECK(!window->transient_parent()); | |
151 event_dispatcher_.AddSystemModalWindow(window); | |
152 } | |
153 | |
154 const UserId& WindowManagerState::user_id() const { | |
155 return window_tree_->user_id(); | |
156 } | |
157 | |
158 void WindowManagerState::OnWillDestroyTree(WindowTree* tree) { | |
159 if (tree_awaiting_input_ack_ != tree) | |
160 return; | |
161 | |
162 // The WindowTree is dying. So it's not going to ack the event. | |
163 // If the dying tree matches the root |tree_| marked as handled so we don't | |
164 // notify it of accelerators. | |
165 OnEventAck(tree_awaiting_input_ack_, tree == window_tree_ | |
166 ? mojom::EventResult::HANDLED | |
167 : mojom::EventResult::UNHANDLED); | |
168 } | |
169 | |
170 bool WindowManagerState::IsActive() const { | |
171 return window_server()->user_id_tracker()->active_id() == user_id(); | |
172 } | |
173 | |
174 void WindowManagerState::Activate(const gfx::Point& mouse_location_on_screen) { | |
175 SetAllRootWindowsVisible(true); | |
176 event_dispatcher_.Reset(); | |
177 event_dispatcher_.SetMousePointerScreenLocation(mouse_location_on_screen); | |
178 } | |
179 | |
180 void WindowManagerState::Deactivate() { | |
181 SetAllRootWindowsVisible(false); | |
182 event_dispatcher_.Reset(); | |
183 // The tree is no longer active, so no point in dispatching any further | |
184 // events. | |
185 std::queue<std::unique_ptr<QueuedEvent>> event_queue; | |
186 event_queue.swap(event_queue_); | |
187 } | |
188 | |
189 void WindowManagerState::ProcessEvent(const ui::Event& event) { | |
190 // If this is still waiting for an ack from a previously sent event, then | |
191 // queue up the event to be dispatched once the ack is received. | |
192 if (event_ack_timer_.IsRunning()) { | |
193 if (!event_queue_.empty() && !event_queue_.back()->processed_target && | |
194 EventsCanBeCoalesced(*event_queue_.back()->event, event)) { | |
195 event_queue_.back()->event = CoalesceEvents( | |
196 std::move(event_queue_.back()->event), ui::Event::Clone(event)); | |
197 return; | |
198 } | |
199 QueueEvent(event, nullptr); | |
200 return; | |
201 } | |
202 event_dispatcher_.ProcessEvent(event); | |
203 } | |
204 | |
205 void WindowManagerState::OnEventAck(mojom::WindowTree* tree, | |
206 mojom::EventResult result) { | |
207 if (tree_awaiting_input_ack_ != tree) { | |
208 // TODO(sad): The ack must have arrived after the timeout. We should do | |
209 // something here, and in OnEventAckTimeout(). | |
210 return; | |
211 } | |
212 tree_awaiting_input_ack_ = nullptr; | |
213 event_ack_timer_.Stop(); | |
214 | |
215 if (result == mojom::EventResult::UNHANDLED && post_target_accelerator_) | |
216 OnAccelerator(post_target_accelerator_->id(), *event_awaiting_input_ack_); | |
217 | |
218 ProcessNextEventFromQueue(); | |
219 } | |
220 | |
221 const WindowServer* WindowManagerState::window_server() const { | |
222 return window_tree_->window_server(); | |
223 } | |
224 | |
225 WindowServer* WindowManagerState::window_server() { | |
226 return window_tree_->window_server(); | |
227 } | |
228 | |
229 DisplayManager* WindowManagerState::display_manager() { | |
230 return window_tree_->display_manager(); | |
231 } | |
232 | |
233 const DisplayManager* WindowManagerState::display_manager() const { | |
234 return window_tree_->display_manager(); | |
235 } | |
236 | |
237 void WindowManagerState::SetAllRootWindowsVisible(bool value) { | |
238 for (Display* display : display_manager()->displays()) { | |
239 WindowManagerDisplayRoot* display_root = | |
240 display->GetWindowManagerDisplayRootForUser(user_id()); | |
241 if (display_root) | |
242 display_root->root()->SetVisible(value); | |
243 } | |
244 } | |
245 | |
246 ServerWindow* WindowManagerState::GetWindowManagerRoot(ServerWindow* window) { | |
247 for (Display* display : display_manager()->displays()) { | |
248 WindowManagerDisplayRoot* display_root = | |
249 display->GetWindowManagerDisplayRootForUser(user_id()); | |
250 if (display_root && display_root->root()->parent() == window) | |
251 return display_root->root(); | |
252 } | |
253 NOTREACHED(); | |
254 return nullptr; | |
255 } | |
256 | |
257 void WindowManagerState::OnEventAckTimeout(ClientSpecificId client_id) { | |
258 WindowTree* hung_tree = window_server()->GetTreeWithId(client_id); | |
259 if (hung_tree && !hung_tree->janky()) | |
260 window_tree_->ClientJankinessChanged(hung_tree); | |
261 OnEventAck(tree_awaiting_input_ack_, mojom::EventResult::UNHANDLED); | |
262 } | |
263 | |
264 void WindowManagerState::QueueEvent( | |
265 const ui::Event& event, | |
266 std::unique_ptr<ProcessedEventTarget> processed_event_target) { | |
267 std::unique_ptr<QueuedEvent> queued_event(new QueuedEvent); | |
268 queued_event->event = ui::Event::Clone(event); | |
269 queued_event->processed_target = std::move(processed_event_target); | |
270 event_queue_.push(std::move(queued_event)); | |
271 } | |
272 | |
273 void WindowManagerState::ProcessNextEventFromQueue() { | |
274 // Loop through |event_queue_| stopping after dispatching the first valid | |
275 // event. | |
276 while (!event_queue_.empty()) { | |
277 std::unique_ptr<QueuedEvent> queued_event = std::move(event_queue_.front()); | |
278 event_queue_.pop(); | |
279 if (!queued_event->processed_target) { | |
280 event_dispatcher_.ProcessEvent(*queued_event->event); | |
281 return; | |
282 } | |
283 if (queued_event->processed_target->IsValid()) { | |
284 DispatchInputEventToWindowImpl( | |
285 queued_event->processed_target->window(), | |
286 queued_event->processed_target->client_id(), *queued_event->event, | |
287 queued_event->processed_target->accelerator()); | |
288 return; | |
289 } | |
290 } | |
291 } | |
292 | |
293 void WindowManagerState::DispatchInputEventToWindowImpl( | |
294 ServerWindow* target, | |
295 ClientSpecificId client_id, | |
296 const ui::Event& event, | |
297 base::WeakPtr<Accelerator> accelerator) { | |
298 if (target && target->parent() == nullptr) | |
299 target = GetWindowManagerRoot(target); | |
300 | |
301 if (event.IsMousePointerEvent()) { | |
302 DCHECK(event_dispatcher_.mouse_cursor_source_window()); | |
303 | |
304 int32_t cursor_id = 0; | |
305 if (event_dispatcher_.GetCurrentMouseCursor(&cursor_id)) { | |
306 WindowManagerDisplayRoot* display_root = | |
307 display_manager()->GetWindowManagerDisplayRoot(target); | |
308 display_root->display()->UpdateNativeCursor(cursor_id); | |
309 } | |
310 } | |
311 | |
312 WindowTree* tree = window_server()->GetTreeWithId(client_id); | |
313 | |
314 // TOOD(sad): Adjust this delay, possibly make this dynamic. | |
315 const base::TimeDelta max_delay = base::debug::BeingDebugged() | |
316 ? base::TimeDelta::FromDays(1) | |
317 : GetDefaultAckTimerDelay(); | |
318 event_ack_timer_.Start( | |
319 FROM_HERE, max_delay, | |
320 base::Bind(&WindowManagerState::OnEventAckTimeout, | |
321 weak_factory_.GetWeakPtr(), tree->id())); | |
322 | |
323 tree_awaiting_input_ack_ = tree; | |
324 if (accelerator) { | |
325 event_awaiting_input_ack_ = ui::Event::Clone(event); | |
326 post_target_accelerator_ = accelerator; | |
327 } | |
328 | |
329 // Ignore |tree| because it will receive the event via normal dispatch. | |
330 window_server()->SendToEventObservers(event, user_id(), tree); | |
331 | |
332 tree->DispatchInputEvent(target, event); | |
333 } | |
334 | |
335 void WindowManagerState::AddDebugAccelerators() { | |
336 // Always register the accelerators, even if they only work in debug, so that | |
337 // keyboard behavior is the same in release and debug builds. | |
338 mojom::EventMatcherPtr matcher = CreateKeyMatcher( | |
339 ui::mojom::KeyboardCode::S, ui::mojom::kEventFlagControlDown | | |
340 ui::mojom::kEventFlagAltDown | | |
341 ui::mojom::kEventFlagShiftDown); | |
342 event_dispatcher_.AddAccelerator(kPrintWindowsDebugAcceleratorId, | |
343 std::move(matcher)); | |
344 } | |
345 | |
346 bool WindowManagerState::HandleDebugAccelerator(uint32_t accelerator_id) { | |
347 #if !defined(NDEBUG) | |
348 if (accelerator_id == kPrintWindowsDebugAcceleratorId) { | |
349 // Error so it will be collected in system logs. | |
350 for (Display* display : display_manager()->displays()) { | |
351 WindowManagerDisplayRoot* display_root = | |
352 display->GetWindowManagerDisplayRootForUser(user_id()); | |
353 if (display_root) { | |
354 LOG(ERROR) << "ServerWindow hierarchy:\n" | |
355 << display_root->root()->GetDebugWindowHierarchy(); | |
356 } | |
357 } | |
358 return true; | |
359 } | |
360 #endif | |
361 return false; | |
362 } | |
363 | |
364 //////////////////////////////////////////////////////////////////////////////// | |
365 // EventDispatcherDelegate: | |
366 | |
367 void WindowManagerState::OnAccelerator(uint32_t accelerator_id, | |
368 const ui::Event& event) { | |
369 DCHECK(IsActive()); | |
370 if (HandleDebugAccelerator(accelerator_id)) | |
371 return; | |
372 window_tree_->OnAccelerator(accelerator_id, event); | |
373 } | |
374 | |
375 void WindowManagerState::SetFocusedWindowFromEventDispatcher( | |
376 ServerWindow* new_focused_window) { | |
377 DCHECK(IsActive()); | |
378 window_server()->SetFocusedWindow(new_focused_window); | |
379 } | |
380 | |
381 ServerWindow* WindowManagerState::GetFocusedWindowForEventDispatcher() { | |
382 return window_server()->GetFocusedWindow(); | |
383 } | |
384 | |
385 void WindowManagerState::SetNativeCapture(ServerWindow* window) { | |
386 DCHECK(IsActive()); | |
387 WindowManagerDisplayRoot* display_root = | |
388 display_manager()->GetWindowManagerDisplayRoot(window); | |
389 DCHECK(display_root); | |
390 platform_display_with_capture_ = display_root->display()->platform_display(); | |
391 platform_display_with_capture_->SetCapture(); | |
392 } | |
393 | |
394 void WindowManagerState::ReleaseNativeCapture() { | |
395 // Tests trigger calling this without a corresponding SetNativeCapture(). | |
396 // TODO(sky): maybe abstract this away so that DCHECK can be added? | |
397 if (!platform_display_with_capture_) | |
398 return; | |
399 | |
400 platform_display_with_capture_->ReleaseCapture(); | |
401 platform_display_with_capture_ = nullptr; | |
402 } | |
403 | |
404 void WindowManagerState::OnServerWindowCaptureLost(ServerWindow* window) { | |
405 DCHECK(window); | |
406 window_server()->ProcessLostCapture(window); | |
407 } | |
408 | |
409 void WindowManagerState::OnMouseCursorLocationChanged(const gfx::Point& point) { | |
410 window_server() | |
411 ->display_manager() | |
412 ->GetUserDisplayManager(user_id()) | |
413 ->OnMouseCursorLocationChanged(point); | |
414 } | |
415 | |
416 void WindowManagerState::DispatchInputEventToWindow(ServerWindow* target, | |
417 ClientSpecificId client_id, | |
418 const ui::Event& event, | |
419 Accelerator* accelerator) { | |
420 DCHECK(IsActive()); | |
421 // TODO(sky): this needs to see if another wms has capture and if so forward | |
422 // to it. | |
423 if (event_ack_timer_.IsRunning()) { | |
424 std::unique_ptr<ProcessedEventTarget> processed_event_target( | |
425 new ProcessedEventTarget(target, client_id, accelerator)); | |
426 QueueEvent(event, std::move(processed_event_target)); | |
427 return; | |
428 } | |
429 | |
430 base::WeakPtr<Accelerator> weak_accelerator; | |
431 if (accelerator) | |
432 weak_accelerator = accelerator->GetWeakPtr(); | |
433 DispatchInputEventToWindowImpl(target, client_id, event, weak_accelerator); | |
434 } | |
435 | |
436 ClientSpecificId WindowManagerState::GetEventTargetClientId( | |
437 const ServerWindow* window, | |
438 bool in_nonclient_area) { | |
439 // If the event is in the non-client area the event goes to the owner of | |
440 // the window. | |
441 WindowTree* tree = nullptr; | |
442 if (in_nonclient_area) { | |
443 tree = window_server()->GetTreeWithId(window->id().client_id); | |
444 } else { | |
445 // If the window is an embed root, forward to the embedded window. | |
446 tree = window_server()->GetTreeWithRoot(window); | |
447 if (!tree) | |
448 tree = window_server()->GetTreeWithId(window->id().client_id); | |
449 } | |
450 | |
451 const ServerWindow* embed_root = | |
452 tree->HasRoot(window) ? window : GetEmbedRoot(window); | |
453 while (tree && tree->embedder_intercepts_events()) { | |
454 DCHECK(tree->HasRoot(embed_root)); | |
455 tree = window_server()->GetTreeWithId(embed_root->id().client_id); | |
456 embed_root = GetEmbedRoot(embed_root); | |
457 } | |
458 | |
459 if (!tree) { | |
460 DCHECK(in_nonclient_area); | |
461 tree = window_tree_; | |
462 } | |
463 return tree->id(); | |
464 } | |
465 | |
466 ServerWindow* WindowManagerState::GetRootWindowContaining( | |
467 const gfx::Point& location) { | |
468 if (display_manager()->displays().empty()) | |
469 return nullptr; | |
470 | |
471 // TODO(sky): this isn't right. To correctly implement need bounds of | |
472 // Display, which we aren't tracking yet. For now, use the first display. | |
473 Display* display = *(display_manager()->displays().begin()); | |
474 WindowManagerDisplayRoot* display_root = | |
475 display->GetWindowManagerDisplayRootForUser(user_id()); | |
476 return display_root ? display_root->root() : nullptr; | |
477 } | |
478 | |
479 void WindowManagerState::OnEventTargetNotFound(const ui::Event& event) { | |
480 window_server()->SendToEventObservers(event, user_id(), | |
481 nullptr /* ignore_tree */); | |
482 } | |
483 | |
484 } // namespace ws | |
485 } // namespace mus | |
OLD | NEW |