| 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 |