| // Copyright 2015 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/mus/ws/display.h" |
| |
| #include <set> |
| #include <vector> |
| |
| #include "base/debug/debugger.h" |
| #include "base/strings/utf_string_conversions.h" |
| #include "components/mus/common/types.h" |
| #include "components/mus/ws/display_binding.h" |
| #include "components/mus/ws/display_manager.h" |
| #include "components/mus/ws/focus_controller.h" |
| #include "components/mus/ws/platform_display.h" |
| #include "components/mus/ws/platform_display_init_params.h" |
| #include "components/mus/ws/window_manager_factory_service.h" |
| #include "components/mus/ws/window_manager_state.h" |
| #include "components/mus/ws/window_server.h" |
| #include "components/mus/ws/window_server_delegate.h" |
| #include "components/mus/ws/window_tree.h" |
| #include "components/mus/ws/window_tree_binding.h" |
| #include "mojo/common/common_type_converters.h" |
| #include "mojo/converters/geometry/geometry_type_converters.h" |
| #include "services/shell/public/interfaces/connector.mojom.h" |
| #include "ui/base/cursor/cursor.h" |
| |
| namespace mus { |
| namespace ws { |
| |
| Display::Display(WindowServer* window_server, |
| const PlatformDisplayInitParams& platform_display_init_params) |
| : id_(window_server->display_manager()->GetAndAdvanceNextDisplayId()), |
| window_server_(window_server), |
| platform_display_(PlatformDisplay::Create(platform_display_init_params)), |
| last_cursor_(ui::kCursorNone) { |
| platform_display_->Init(this); |
| |
| window_server_->window_manager_factory_registry()->AddObserver(this); |
| window_server_->user_id_tracker()->AddObserver(this); |
| } |
| |
| Display::~Display() { |
| window_server_->user_id_tracker()->RemoveObserver(this); |
| |
| window_server_->window_manager_factory_registry()->RemoveObserver(this); |
| |
| if (!focus_controller_) { |
| focus_controller_->RemoveObserver(this); |
| focus_controller_.reset(); |
| } |
| |
| for (ServerWindow* window : windows_needing_frame_destruction_) |
| window->RemoveObserver(this); |
| |
| // Destroy any trees, which triggers destroying the WindowManagerState. Copy |
| // off the WindowManagerStates as destruction mutates |
| // |window_manager_state_map_|. |
| std::set<WindowManagerState*> states; |
| for (auto& pair : window_manager_state_map_) |
| states.insert(pair.second.get()); |
| for (WindowManagerState* state : states) |
| window_server_->DestroyTree(state->tree()); |
| } |
| |
| void Display::Init(std::unique_ptr<DisplayBinding> binding) { |
| init_called_ = true; |
| binding_ = std::move(binding); |
| display_manager()->AddDisplay(this); |
| InitWindowManagersIfNecessary(); |
| } |
| |
| DisplayManager* Display::display_manager() { |
| return window_server_->display_manager(); |
| } |
| |
| const DisplayManager* Display::display_manager() const { |
| return window_server_->display_manager(); |
| } |
| |
| mojom::DisplayPtr Display::ToMojomDisplay() const { |
| mojom::DisplayPtr display_ptr = mojom::Display::New(); |
| display_ptr = mojom::Display::New(); |
| display_ptr->id = id_; |
| display_ptr->bounds = mojo::Rect::New(); |
| // TODO(sky): Display should know it's origin. |
| display_ptr->bounds->x = 0; |
| display_ptr->bounds->y = 0; |
| display_ptr->bounds->width = root_->bounds().size().width(); |
| display_ptr->bounds->height = root_->bounds().size().height(); |
| // TODO(sky): window manager needs an API to set the work area. |
| display_ptr->work_area = display_ptr->bounds.Clone(); |
| display_ptr->device_pixel_ratio = |
| platform_display_->GetViewportMetrics().device_pixel_ratio; |
| display_ptr->rotation = platform_display_->GetRotation(); |
| // TODO(sky): make this real. |
| display_ptr->is_primary = true; |
| // TODO(sky): make this real. |
| display_ptr->touch_support = mojom::TouchSupport::UNKNOWN; |
| display_ptr->frame_decoration_values = mojom::FrameDecorationValues::New(); |
| display_ptr->frame_decoration_values->normal_client_area_insets = |
| mojo::Insets::New(); |
| display_ptr->frame_decoration_values->maximized_client_area_insets = |
| mojo::Insets::New(); |
| return display_ptr; |
| } |
| |
| void Display::SchedulePaint(const ServerWindow* window, |
| const gfx::Rect& bounds) { |
| DCHECK(root_->Contains(window)); |
| platform_display_->SchedulePaint(window, bounds); |
| } |
| |
| void Display::ScheduleSurfaceDestruction(ServerWindow* window) { |
| if (!platform_display_->IsFramePending()) { |
| window->DestroySurfacesScheduledForDestruction(); |
| return; |
| } |
| if (windows_needing_frame_destruction_.count(window)) |
| return; |
| windows_needing_frame_destruction_.insert(window); |
| window->AddObserver(this); |
| } |
| |
| const mojom::ViewportMetrics& Display::GetViewportMetrics() const { |
| return platform_display_->GetViewportMetrics(); |
| } |
| |
| ServerWindow* Display::GetRootWithId(const WindowId& id) { |
| if (id == root_->id()) |
| return root_.get(); |
| for (auto& pair : window_manager_state_map_) { |
| if (pair.second->root()->id() == id) |
| return pair.second->root(); |
| } |
| return nullptr; |
| } |
| |
| WindowManagerState* Display::GetWindowManagerStateWithRoot( |
| const ServerWindow* window) { |
| for (auto& pair : window_manager_state_map_) { |
| if (pair.second->root() == window) |
| return pair.second.get(); |
| } |
| return nullptr; |
| } |
| |
| const WindowManagerState* Display::GetWindowManagerStateForUser( |
| const UserId& user_id) const { |
| auto iter = window_manager_state_map_.find(user_id); |
| return iter == window_manager_state_map_.end() ? nullptr : iter->second.get(); |
| } |
| |
| mojom::Rotation Display::GetRotation() const { |
| return platform_display_->GetRotation(); |
| } |
| |
| const WindowManagerState* Display::GetActiveWindowManagerState() const { |
| return GetWindowManagerStateForUser( |
| window_server_->user_id_tracker()->active_id()); |
| } |
| |
| bool Display::SetFocusedWindow(ServerWindow* new_focused_window) { |
| ServerWindow* old_focused_window = focus_controller_->GetFocusedWindow(); |
| if (old_focused_window == new_focused_window) |
| return true; |
| DCHECK(!new_focused_window || root_window()->Contains(new_focused_window)); |
| return focus_controller_->SetFocusedWindow(new_focused_window); |
| } |
| |
| ServerWindow* Display::GetFocusedWindow() { |
| return focus_controller_->GetFocusedWindow(); |
| } |
| |
| void Display::ActivateNextWindow() { |
| // TODO(sky): this is wrong, needs to figure out the next window to activate |
| // and then route setting through WindowServer. |
| focus_controller_->ActivateNextWindow(); |
| } |
| |
| void Display::AddActivationParent(ServerWindow* window) { |
| activation_parents_.Add(window); |
| } |
| |
| void Display::RemoveActivationParent(ServerWindow* window) { |
| activation_parents_.Remove(window); |
| } |
| |
| void Display::UpdateTextInputState(ServerWindow* window, |
| const ui::TextInputState& state) { |
| // Do not need to update text input for unfocused windows. |
| if (!platform_display_ || focus_controller_->GetFocusedWindow() != window) |
| return; |
| platform_display_->UpdateTextInputState(state); |
| } |
| |
| void Display::SetImeVisibility(ServerWindow* window, bool visible) { |
| // Do not need to show or hide IME for unfocused window. |
| if (focus_controller_->GetFocusedWindow() != window) |
| return; |
| platform_display_->SetImeVisibility(visible); |
| } |
| |
| void Display::OnWillDestroyTree(WindowTree* tree) { |
| for (auto it = window_manager_state_map_.begin(); |
| it != window_manager_state_map_.end(); ++it) { |
| if (it->second->tree() == tree) { |
| window_manager_state_map_.erase(it); |
| break; |
| } |
| } |
| |
| for (const auto& pair : window_manager_state_map_) |
| pair.second->OnWillDestroyTree(tree); |
| } |
| |
| void Display::UpdateNativeCursor(int32_t cursor_id) { |
| if (cursor_id != last_cursor_) { |
| platform_display_->SetCursorById(cursor_id); |
| last_cursor_ = cursor_id; |
| } |
| } |
| |
| void Display::SetSize(mojo::SizePtr size) { |
| platform_display_->SetViewportSize(size.To<gfx::Size>()); |
| } |
| |
| void Display::SetTitle(const mojo::String& title) { |
| platform_display_->SetTitle(title.To<base::string16>()); |
| } |
| |
| void Display::InitWindowManagersIfNecessary() { |
| if (!init_called_ || !root_) |
| return; |
| |
| display_manager()->OnDisplayAcceleratedWidgetAvailable(this); |
| if (binding_) { |
| std::unique_ptr<WindowManagerState> wms_ptr( |
| new WindowManagerState(this, platform_display_.get())); |
| WindowManagerState* wms = wms_ptr.get(); |
| // For this case we never create additional WindowManagerStates, so any |
| // id works. |
| window_manager_state_map_[shell::mojom::kRootUserID] = std::move(wms_ptr); |
| wms->tree_ = binding_->CreateWindowTree(wms->root()); |
| } else { |
| CreateWindowManagerStatesFromRegistry(); |
| } |
| } |
| |
| void Display::CreateWindowManagerStatesFromRegistry() { |
| std::vector<WindowManagerFactoryService*> services = |
| window_server_->window_manager_factory_registry()->GetServices(); |
| for (WindowManagerFactoryService* service : services) { |
| if (service->window_manager_factory()) |
| CreateWindowManagerStateFromService(service); |
| } |
| } |
| |
| void Display::CreateWindowManagerStateFromService( |
| WindowManagerFactoryService* service) { |
| std::unique_ptr<WindowManagerState> wms_ptr(new WindowManagerState( |
| this, platform_display_.get(), service->user_id())); |
| WindowManagerState* wms = wms_ptr.get(); |
| window_manager_state_map_[service->user_id()] = std::move(wms_ptr); |
| wms->tree_ = window_server_->CreateTreeForWindowManager( |
| this, service->window_manager_factory(), wms->root(), service->user_id()); |
| if (!binding_) { |
| const bool is_active = |
| service->user_id() == window_server_->user_id_tracker()->active_id(); |
| wms->root()->SetVisible(is_active); |
| } |
| } |
| |
| ServerWindow* Display::GetRootWindow() { |
| return root_.get(); |
| } |
| |
| void Display::OnEvent(const ui::Event& event) { |
| WindowManagerState* wms = GetActiveWindowManagerState(); |
| if (wms) |
| wms->ProcessEvent(event); |
| } |
| |
| void Display::OnNativeCaptureLost() { |
| WindowManagerState* state = GetActiveWindowManagerState(); |
| if (state) |
| state->SetCapture(nullptr, false); |
| } |
| |
| void Display::OnDisplayClosed() { |
| display_manager()->DestroyDisplay(this); |
| } |
| |
| void Display::OnViewportMetricsChanged( |
| const mojom::ViewportMetrics& old_metrics, |
| const mojom::ViewportMetrics& new_metrics) { |
| if (!root_) { |
| root_.reset(window_server_->CreateServerWindow( |
| display_manager()->GetAndAdvanceNextRootId(), |
| ServerWindow::Properties())); |
| root_->SetBounds(gfx::Rect(new_metrics.size_in_pixels.To<gfx::Size>())); |
| root_->SetVisible(true); |
| focus_controller_.reset(new FocusController(this, root_.get())); |
| focus_controller_->AddObserver(this); |
| InitWindowManagersIfNecessary(); |
| } else { |
| root_->SetBounds(gfx::Rect(new_metrics.size_in_pixels.To<gfx::Size>())); |
| const gfx::Rect wm_bounds(root_->bounds().size()); |
| for (auto& pair : window_manager_state_map_) |
| pair.second->root()->SetBounds(wm_bounds); |
| } |
| // TODO(sky): if bounds changed, then need to update |
| // Display/WindowManagerState appropriately (e.g. notify observers). |
| window_server_->ProcessViewportMetricsChanged(this, old_metrics, new_metrics); |
| } |
| |
| void Display::OnCompositorFrameDrawn() { |
| std::set<ServerWindow*> windows; |
| windows.swap(windows_needing_frame_destruction_); |
| for (ServerWindow* window : windows) { |
| window->RemoveObserver(this); |
| window->DestroySurfacesScheduledForDestruction(); |
| } |
| } |
| |
| bool Display::CanHaveActiveChildren(ServerWindow* window) const { |
| return window && activation_parents_.Contains(window); |
| } |
| |
| void Display::OnActivationChanged(ServerWindow* old_active_window, |
| ServerWindow* new_active_window) { |
| DCHECK_NE(new_active_window, old_active_window); |
| if (new_active_window && new_active_window->parent()) { |
| // Raise the new active window. |
| // TODO(sad): Let the WM dictate whether to raise the window or not? |
| new_active_window->parent()->StackChildAtTop(new_active_window); |
| } |
| } |
| |
| void Display::OnFocusChanged(FocusControllerChangeSource change_source, |
| ServerWindow* old_focused_window, |
| ServerWindow* new_focused_window) { |
| // TODO(sky): focus is global, not per windowtreehost. Move. |
| |
| // There are up to four connections that need to be notified: |
| // . the connection containing |old_focused_window|. |
| // . the connection with |old_focused_window| as its root. |
| // . the connection containing |new_focused_window|. |
| // . the connection with |new_focused_window| as its root. |
| // Some of these connections may be the same. The following takes care to |
| // notify each only once. |
| WindowTree* owning_tree_old = nullptr; |
| WindowTree* embedded_tree_old = nullptr; |
| |
| if (old_focused_window) { |
| owning_tree_old = |
| window_server_->GetTreeWithId(old_focused_window->id().connection_id); |
| if (owning_tree_old) { |
| owning_tree_old->ProcessFocusChanged(old_focused_window, |
| new_focused_window); |
| } |
| embedded_tree_old = window_server_->GetTreeWithRoot(old_focused_window); |
| if (embedded_tree_old) { |
| DCHECK_NE(owning_tree_old, embedded_tree_old); |
| embedded_tree_old->ProcessFocusChanged(old_focused_window, |
| new_focused_window); |
| } |
| } |
| WindowTree* owning_tree_new = nullptr; |
| WindowTree* embedded_tree_new = nullptr; |
| if (new_focused_window) { |
| owning_tree_new = |
| window_server_->GetTreeWithId(new_focused_window->id().connection_id); |
| if (owning_tree_new && owning_tree_new != owning_tree_old && |
| owning_tree_new != embedded_tree_old) { |
| owning_tree_new->ProcessFocusChanged(old_focused_window, |
| new_focused_window); |
| } |
| embedded_tree_new = window_server_->GetTreeWithRoot(new_focused_window); |
| if (embedded_tree_new && embedded_tree_new != owning_tree_old && |
| embedded_tree_new != embedded_tree_old) { |
| DCHECK_NE(owning_tree_new, embedded_tree_new); |
| embedded_tree_new->ProcessFocusChanged(old_focused_window, |
| new_focused_window); |
| } |
| } |
| |
| // WindowManagers are always notified of focus changes. |
| WindowTree* wms_tree_with_old_focused_window = nullptr; |
| if (old_focused_window) { |
| WindowManagerState* wms = |
| display_manager() |
| ->GetWindowManagerAndDisplay(old_focused_window) |
| .window_manager_state; |
| wms_tree_with_old_focused_window = wms ? wms->tree() : nullptr; |
| if (wms_tree_with_old_focused_window && |
| wms_tree_with_old_focused_window != owning_tree_old && |
| wms_tree_with_old_focused_window != embedded_tree_old && |
| wms_tree_with_old_focused_window != owning_tree_new && |
| wms_tree_with_old_focused_window != embedded_tree_new) { |
| wms_tree_with_old_focused_window->ProcessFocusChanged(old_focused_window, |
| new_focused_window); |
| } |
| } |
| if (new_focused_window) { |
| WindowManagerState* wms = |
| display_manager() |
| ->GetWindowManagerAndDisplay(new_focused_window) |
| .window_manager_state; |
| WindowTree* wms_tree = wms ? wms->tree() : nullptr; |
| if (wms_tree && wms_tree != wms_tree_with_old_focused_window && |
| wms_tree != owning_tree_old && wms_tree != embedded_tree_old && |
| wms_tree != owning_tree_new && wms_tree != embedded_tree_new) { |
| wms_tree->ProcessFocusChanged(old_focused_window, new_focused_window); |
| } |
| } |
| |
| UpdateTextInputState(new_focused_window, |
| new_focused_window->text_input_state()); |
| } |
| |
| void Display::OnWindowDestroyed(ServerWindow* window) { |
| windows_needing_frame_destruction_.erase(window); |
| window->RemoveObserver(this); |
| } |
| |
| void Display::OnActiveUserIdChanged(const UserId& previously_active_id, |
| const UserId& active_id) { |
| if (binding_) |
| return; |
| |
| WindowManagerState* previous_wms = |
| GetWindowManagerStateForUser(previously_active_id); |
| const gfx::Point mouse_location_on_screen = |
| previous_wms |
| ? previous_wms->event_dispatcher()->mouse_pointer_last_location() |
| : gfx::Point(); |
| if (previous_wms) |
| previous_wms->Deactivate(); |
| |
| WindowManagerState* active_wms = GetWindowManagerStateForUser(active_id); |
| if (active_wms) |
| active_wms->Activate(mouse_location_on_screen); |
| } |
| |
| void Display::OnUserIdAdded(const UserId& id) {} |
| |
| void Display::OnUserIdRemoved(const UserId& id) { |
| if (binding_) |
| return; |
| |
| WindowManagerState* state = GetWindowManagerStateForUser(id); |
| if (!state) |
| return; |
| |
| // DestroyTree() calls back to OnWillDestroyTree() and the WindowManagerState |
| // is destroyed (and removed). |
| window_server_->DestroyTree(state->tree()); |
| DCHECK_EQ(0u, window_manager_state_map_.count(id)); |
| } |
| |
| void Display::OnWindowManagerFactorySet(WindowManagerFactoryService* service) { |
| if (!binding_) |
| CreateWindowManagerStateFromService(service); |
| } |
| |
| } // namespace ws |
| } // namespace mus |