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 "ash/display/display_highlight_controller.h"
6 
7 #include "ash/display/window_tree_host_manager.h"
8 #include "ash/magnifier/magnification_controller.h"
9 #include "ash/public/cpp/shell_window_ids.h"
10 #include "ash/session/session_controller_impl.h"
11 #include "ash/shell.h"
12 #include "ui/compositor/layer.h"
13 #include "ui/compositor/layer_type.h"
14 #include "ui/compositor/paint_recorder.h"
15 #include "ui/display/manager/display_manager.h"
16 #include "ui/gfx/color_palette.h"
17 #include "ui/views/border.h"
18 #include "ui/wm/core/window_animations.h"
19 
20 namespace ash {
21 
22 namespace {
23 
24 constexpr SkColor kHighlightColor = gfx::kGoogleBlue600;
25 constexpr int kHighlightSizeFactor = 128;
26 
CreateHighlightWidget(const display::Display & display)27 std::unique_ptr<views::Widget> CreateHighlightWidget(
28     const display::Display& display) {
29   const int64_t display_id = display.id();
30 
31   DCHECK_NE(display_id, display::kInvalidDisplayId);
32 
33   views::Widget::InitParams params(
34       views::Widget::InitParams::TYPE_WINDOW_FRAMELESS);
35   params.ownership = views::Widget::InitParams::WIDGET_OWNS_NATIVE_WIDGET;
36   params.activatable = views::Widget::InitParams::ACTIVATABLE_NO;
37   params.accept_events = false;
38   params.opacity = views::Widget::InitParams::WindowOpacity::kTranslucent;
39 
40   aura::Window* root = Shell::GetRootWindowForDisplayId(display_id);
41 
42   params.parent = root->GetChildById(kShellWindowId_ScreenAnimationContainer);
43   params.bounds = root->GetBoundsInRootWindow();
44   params.name = "DisplayIdentificationHighlight";
45 
46   std::unique_ptr<views::Widget> highlight_widget =
47       std::make_unique<views::Widget>();
48 
49   const int highlight_thickness =
50       std::max(params.bounds.width(), params.bounds.height()) /
51       kHighlightSizeFactor;
52 
53   highlight_widget->Init(std::move(params));
54 
55   highlight_widget->GetRootView()->SetBorder(
56       views::CreateSolidBorder(highlight_thickness, kHighlightColor));
57 
58   auto* window = highlight_widget->GetNativeWindow();
59   window->set_id(kShellWindowId_DisplayIdentificationHighlightWindow);
60   ::wm::SetWindowVisibilityAnimationTransition(window, ::wm::ANIMATE_NONE);
61 
62   MagnificationController* magnification_controller =
63       Shell::Get()->magnification_controller();
64 
65   // Forces a redraw of full-screen magnification in order to reverse
66   // magnification on display highlight window performed in
67   // MagnificationController::ReDraw(). If redraw is not forced, then the
68   // highlight may not show up around the edges of the display properly until
69   // the next redraw.
70   if (magnification_controller->IsEnabled()) {
71     magnification_controller->MoveWindow(
72         magnification_controller->GetWindowPosition(), false);
73   }
74 
75   highlight_widget->Show();
76 
77   return highlight_widget;
78 }
79 
80 }  // namespace
81 
DisplayHighlightController()82 DisplayHighlightController::DisplayHighlightController() {
83   Shell* shell = Shell::Get();
84   SessionControllerImpl* session_controller = shell->session_controller();
85 
86   session_controller->AddObserver(this);
87   shell->window_tree_host_manager()->AddObserver(this);
88 
89   is_locked_ = session_controller->IsScreenLocked();
90 }
91 
~DisplayHighlightController()92 DisplayHighlightController::~DisplayHighlightController() {
93   Shell* shell = Shell::Get();
94 
95   shell->window_tree_host_manager()->RemoveObserver(this);
96   shell->session_controller()->RemoveObserver(this);
97 }
98 
UpdateDisplayIdentificationHighlight()99 void DisplayHighlightController::UpdateDisplayIdentificationHighlight() {
100   if (selected_display_id_ == display::kInvalidDisplayId) {
101     highlight_widget_.reset();
102     return;
103   }
104 
105   display::DisplayManager* display_manager = Shell::Get()->display_manager();
106 
107   // If |selected_display_id_| does not correspond to an active display, we
108   // cannot display highlights.
109   if (!display_manager->IsActiveDisplayId(selected_display_id_)) {
110     highlight_widget_.reset();
111     return;
112   }
113 
114   // If there is only one display, we don't need to show a special highlight for
115   // it since there is only one place for the user to look.
116   if (display_manager->GetNumDisplays() == 1) {
117     highlight_widget_.reset();
118     return;
119   }
120 
121   // Don't show a highlight if the device is locked to ensure that the highlight
122   // does not appear on the login screen.
123   if (is_locked_) {
124     highlight_widget_.reset();
125     return;
126   }
127 
128   highlight_widget_ = CreateHighlightWidget(
129       display_manager->GetDisplayForId(selected_display_id_));
130 }
131 
OnLockStateChanged(bool locked)132 void DisplayHighlightController::OnLockStateChanged(bool locked) {
133   is_locked_ = locked;
134   UpdateDisplayIdentificationHighlight();
135 }
136 
OnDisplayConfigurationChanged()137 void DisplayHighlightController::OnDisplayConfigurationChanged() {
138   UpdateDisplayIdentificationHighlight();
139 }
140 
OnDisplaysInitialized()141 void DisplayHighlightController::OnDisplaysInitialized() {
142   UpdateDisplayIdentificationHighlight();
143 }
144 
SetHighlightedDisplay(int64_t display_id)145 void DisplayHighlightController::SetHighlightedDisplay(int64_t display_id) {
146   if (selected_display_id_ != display_id) {
147     selected_display_id_ = display_id;
148     UpdateDisplayIdentificationHighlight();
149   }
150 }
151 
152 }  // namespace ash
153