1 // Copyright (c) 2012 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/desktop_background/desktop_background_controller.h"
6
7 #include "ash/ash_switches.h"
8 #include "ash/desktop_background/desktop_background_controller_observer.h"
9 #include "ash/desktop_background/desktop_background_view.h"
10 #include "ash/desktop_background/desktop_background_widget_controller.h"
11 #include "ash/desktop_background/user_wallpaper_delegate.h"
12 #include "ash/desktop_background/wallpaper_resizer.h"
13 #include "ash/display/display_info.h"
14 #include "ash/display/display_manager.h"
15 #include "ash/root_window_controller.h"
16 #include "ash/shell.h"
17 #include "ash/shell_factory.h"
18 #include "ash/shell_window_ids.h"
19 #include "ash/wm/root_window_layout_manager.h"
20 #include "base/bind.h"
21 #include "base/command_line.h"
22 #include "base/files/file_util.h"
23 #include "base/logging.h"
24 #include "base/synchronization/cancellation_flag.h"
25 #include "base/threading/worker_pool.h"
26 #include "content/public/browser/browser_thread.h"
27 #include "ui/aura/window.h"
28 #include "ui/aura/window_event_dispatcher.h"
29 #include "ui/compositor/layer.h"
30 #include "ui/gfx/codec/jpeg_codec.h"
31 #include "ui/gfx/image/image_skia.h"
32 #include "ui/gfx/rect.h"
33 #include "ui/views/widget/widget.h"
34
35 using content::BrowserThread;
36
37 namespace ash {
38 namespace {
39
40 // How long to wait reloading the wallpaper after the max display has
41 // changed?
42 const int kWallpaperReloadDelayMs = 2000;
43
44 } // namespace
45
DesktopBackgroundController()46 DesktopBackgroundController::DesktopBackgroundController()
47 : locked_(false),
48 desktop_background_mode_(BACKGROUND_NONE),
49 wallpaper_reload_delay_(kWallpaperReloadDelayMs) {
50 Shell::GetInstance()->display_controller()->AddObserver(this);
51 Shell::GetInstance()->AddShellObserver(this);
52 }
53
~DesktopBackgroundController()54 DesktopBackgroundController::~DesktopBackgroundController() {
55 Shell::GetInstance()->display_controller()->RemoveObserver(this);
56 Shell::GetInstance()->RemoveShellObserver(this);
57 }
58
GetWallpaper() const59 gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const {
60 if (current_wallpaper_)
61 return current_wallpaper_->image();
62 return gfx::ImageSkia();
63 }
64
AddObserver(DesktopBackgroundControllerObserver * observer)65 void DesktopBackgroundController::AddObserver(
66 DesktopBackgroundControllerObserver* observer) {
67 observers_.AddObserver(observer);
68 }
69
RemoveObserver(DesktopBackgroundControllerObserver * observer)70 void DesktopBackgroundController::RemoveObserver(
71 DesktopBackgroundControllerObserver* observer) {
72 observers_.RemoveObserver(observer);
73 }
74
GetWallpaperLayout() const75 WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
76 if (current_wallpaper_)
77 return current_wallpaper_->layout();
78 return WALLPAPER_LAYOUT_CENTER_CROPPED;
79 }
80
SetWallpaperImage(const gfx::ImageSkia & image,WallpaperLayout layout)81 bool DesktopBackgroundController::SetWallpaperImage(const gfx::ImageSkia& image,
82 WallpaperLayout layout) {
83 VLOG(1) << "SetWallpaper: image_id=" << WallpaperResizer::GetImageId(image)
84 << " layout=" << layout;
85
86 if (WallpaperIsAlreadyLoaded(image, true /* compare_layouts */, layout)) {
87 VLOG(1) << "Wallpaper is already loaded";
88 return false;
89 }
90
91 current_wallpaper_.reset(
92 new WallpaperResizer(image, GetMaxDisplaySizeInNative(), layout));
93 current_wallpaper_->StartResize();
94
95 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver,
96 observers_,
97 OnWallpaperDataChanged());
98 SetDesktopBackgroundImageMode();
99 return true;
100 }
101
CreateEmptyWallpaper()102 void DesktopBackgroundController::CreateEmptyWallpaper() {
103 current_wallpaper_.reset(NULL);
104 SetDesktopBackgroundImageMode();
105 }
106
MoveDesktopToLockedContainer()107 bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
108 if (locked_)
109 return false;
110 locked_ = true;
111 return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
112 GetBackgroundContainerId(true));
113 }
114
MoveDesktopToUnlockedContainer()115 bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
116 if (!locked_)
117 return false;
118 locked_ = false;
119 return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
120 GetBackgroundContainerId(false));
121 }
122
OnDisplayConfigurationChanged()123 void DesktopBackgroundController::OnDisplayConfigurationChanged() {
124 gfx::Size max_display_size = GetMaxDisplaySizeInNative();
125 if (current_max_display_size_ != max_display_size) {
126 current_max_display_size_ = max_display_size;
127 if (desktop_background_mode_ == BACKGROUND_IMAGE &&
128 current_wallpaper_.get()) {
129 timer_.Stop();
130 timer_.Start(FROM_HERE,
131 base::TimeDelta::FromMilliseconds(wallpaper_reload_delay_),
132 this,
133 &DesktopBackgroundController::UpdateWallpaper);
134 }
135 }
136 }
137
OnRootWindowAdded(aura::Window * root_window)138 void DesktopBackgroundController::OnRootWindowAdded(aura::Window* root_window) {
139 // The background hasn't been set yet.
140 if (desktop_background_mode_ == BACKGROUND_NONE)
141 return;
142
143 // Handle resolution change for "built-in" images.
144 gfx::Size max_display_size = GetMaxDisplaySizeInNative();
145 if (current_max_display_size_ != max_display_size) {
146 current_max_display_size_ = max_display_size;
147 if (desktop_background_mode_ == BACKGROUND_IMAGE &&
148 current_wallpaper_.get())
149 UpdateWallpaper();
150 }
151
152 InstallDesktopController(root_window);
153 }
154
155 // static
GetMaxDisplaySizeInNative()156 gfx::Size DesktopBackgroundController::GetMaxDisplaySizeInNative() {
157 int width = 0;
158 int height = 0;
159 std::vector<gfx::Display> displays = Shell::GetScreen()->GetAllDisplays();
160 DisplayManager* display_manager = Shell::GetInstance()->display_manager();
161
162 for (std::vector<gfx::Display>::iterator iter = displays.begin();
163 iter != displays.end(); ++iter) {
164 // Don't use size_in_pixel because we want to use the native pixel size.
165 gfx::Size size_in_pixel =
166 display_manager->GetDisplayInfo(iter->id()).bounds_in_native().size();
167 if (iter->rotation() == gfx::Display::ROTATE_90 ||
168 iter->rotation() == gfx::Display::ROTATE_270) {
169 size_in_pixel = gfx::Size(size_in_pixel.height(), size_in_pixel.width());
170 }
171 width = std::max(size_in_pixel.width(), width);
172 height = std::max(size_in_pixel.height(), height);
173 }
174 return gfx::Size(width, height);
175 }
176
WallpaperIsAlreadyLoaded(const gfx::ImageSkia & image,bool compare_layouts,WallpaperLayout layout) const177 bool DesktopBackgroundController::WallpaperIsAlreadyLoaded(
178 const gfx::ImageSkia& image,
179 bool compare_layouts,
180 WallpaperLayout layout) const {
181 if (!current_wallpaper_.get())
182 return false;
183
184 // Compare layouts only if necessary.
185 if (compare_layouts && layout != current_wallpaper_->layout())
186 return false;
187
188 return WallpaperResizer::GetImageId(image) ==
189 current_wallpaper_->original_image_id();
190 }
191
SetDesktopBackgroundImageMode()192 void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
193 desktop_background_mode_ = BACKGROUND_IMAGE;
194 InstallDesktopControllerForAllWindows();
195 }
196
InstallDesktopController(aura::Window * root_window)197 void DesktopBackgroundController::InstallDesktopController(
198 aura::Window* root_window) {
199 DesktopBackgroundWidgetController* component = NULL;
200 int container_id = GetBackgroundContainerId(locked_);
201
202 switch (desktop_background_mode_) {
203 case BACKGROUND_IMAGE: {
204 views::Widget* widget =
205 CreateDesktopBackground(root_window, container_id);
206 component = new DesktopBackgroundWidgetController(widget);
207 break;
208 }
209 case BACKGROUND_NONE:
210 NOTREACHED();
211 return;
212 }
213 GetRootWindowController(root_window)->SetAnimatingWallpaperController(
214 new AnimatingDesktopController(component));
215
216 component->StartAnimating(GetRootWindowController(root_window));
217 }
218
InstallDesktopControllerForAllWindows()219 void DesktopBackgroundController::InstallDesktopControllerForAllWindows() {
220 aura::Window::Windows root_windows = Shell::GetAllRootWindows();
221 for (aura::Window::Windows::iterator iter = root_windows.begin();
222 iter != root_windows.end(); ++iter) {
223 InstallDesktopController(*iter);
224 }
225 current_max_display_size_ = GetMaxDisplaySizeInNative();
226 }
227
ReparentBackgroundWidgets(int src_container,int dst_container)228 bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container,
229 int dst_container) {
230 bool moved = false;
231 Shell::RootWindowControllerList controllers =
232 Shell::GetAllRootWindowControllers();
233 for (Shell::RootWindowControllerList::iterator iter = controllers.begin();
234 iter != controllers.end(); ++iter) {
235 RootWindowController* root_window_controller = *iter;
236 // In the steady state (no animation playing) the background widget
237 // controller exists in the RootWindowController.
238 DesktopBackgroundWidgetController* desktop_controller =
239 root_window_controller->wallpaper_controller();
240 if (desktop_controller) {
241 moved |=
242 desktop_controller->Reparent(root_window_controller->GetRootWindow(),
243 src_container,
244 dst_container);
245 }
246 // During desktop show animations the controller lives in
247 // AnimatingDesktopController owned by RootWindowController.
248 // NOTE: If a wallpaper load happens during a desktop show animation there
249 // can temporarily be two desktop background widgets. We must reparent
250 // both of them - one above and one here.
251 DesktopBackgroundWidgetController* animating_controller =
252 root_window_controller->animating_wallpaper_controller() ?
253 root_window_controller->animating_wallpaper_controller()->
254 GetController(false) :
255 NULL;
256 if (animating_controller) {
257 moved |= animating_controller->Reparent(
258 root_window_controller->GetRootWindow(),
259 src_container,
260 dst_container);
261 }
262 }
263 return moved;
264 }
265
GetBackgroundContainerId(bool locked)266 int DesktopBackgroundController::GetBackgroundContainerId(bool locked) {
267 return locked ? kShellWindowId_LockScreenBackgroundContainer
268 : kShellWindowId_DesktopBackgroundContainer;
269 }
270
UpdateWallpaper()271 void DesktopBackgroundController::UpdateWallpaper() {
272 current_wallpaper_.reset(NULL);
273 ash::Shell::GetInstance()->user_wallpaper_delegate()->
274 UpdateWallpaper(true /* clear cache */);
275 }
276
277 } // namespace ash
278