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.
5 #include "ash/desktop_background/desktop_background_controller.h"
7 #include "ash/desktop_background/desktop_background_controller_observer.h"
8 #include "ash/desktop_background/desktop_background_view.h"
9 #include "ash/desktop_background/desktop_background_widget_controller.h"
10 #include "ash/desktop_background/user_wallpaper_delegate.h"
11 #include "ash/desktop_background/wallpaper_resizer.h"
12 #include "ash/root_window_controller.h"
13 #include "ash/shell.h"
14 #include "ash/shell_factory.h"
15 #include "ash/shell_window_ids.h"
16 #include "ash/wm/root_window_layout_manager.h"
17 #include "base/bind.h"
18 #include "base/logging.h"
19 #include "base/synchronization/cancellation_flag.h"
20 #include "base/threading/worker_pool.h"
21 #include "content/public/browser/browser_thread.h"
22 #include "grit/ash_wallpaper_resources.h"
23 #include "ui/aura/root_window.h"
24 #include "ui/aura/window.h"
25 #include "ui/compositor/layer.h"
26 #include "ui/gfx/rect.h"
27 #include "ui/gfx/image/image.h"
28 #include "ui/views/widget/widget.h"
30 using ash::internal::DesktopBackgroundWidgetController
;
31 using ash::internal::kAnimatingDesktopController
;
32 using ash::internal::kDesktopController
;
33 using content::BrowserThread
;
38 const SkColor kTransparentColor
= SkColorSetARGB(0x00, 0x00, 0x00, 0x00);
40 internal::RootWindowLayoutManager
* GetRootWindowLayoutManager(
41 aura::RootWindow
* root_window
) {
42 return static_cast<internal::RootWindowLayoutManager
*>(
43 root_window
->layout_manager());
46 // Returns the maximum width and height of all root windows.
47 gfx::Size
GetRootWindowsSize() {
50 Shell::RootWindowList root_windows
= Shell::GetAllRootWindows();
51 for (Shell::RootWindowList::iterator iter
= root_windows
.begin();
52 iter
!= root_windows
.end(); ++iter
) {
53 gfx::Size root_window_size
= (*iter
)->GetHostSize();
54 if (root_window_size
.width() > width
)
55 width
= root_window_size
.width();
56 if (root_window_size
.height() > height
)
57 height
= root_window_size
.height();
59 return gfx::Size(width
, height
);
64 #if defined(GOOGLE_CHROME_BUILD)
65 const WallpaperInfo kDefaultLargeWallpaper
=
66 { IDR_AURA_WALLPAPERS_2_LANDSCAPE8_LARGE
, WALLPAPER_LAYOUT_CENTER_CROPPED
};
67 const WallpaperInfo kDefaultSmallWallpaper
=
68 { IDR_AURA_WALLPAPERS_2_LANDSCAPE8_SMALL
, WALLPAPER_LAYOUT_CENTER
};
69 const WallpaperInfo kGuestLargeWallpaper
=
70 { IDR_AURA_WALLPAPERS_2_LANDSCAPE7_LARGE
, WALLPAPER_LAYOUT_CENTER_CROPPED
};
71 const WallpaperInfo kGuestSmallWallpaper
=
72 { IDR_AURA_WALLPAPERS_2_LANDSCAPE7_SMALL
, WALLPAPER_LAYOUT_CENTER
};
74 const WallpaperInfo kDefaultLargeWallpaper
=
75 { IDR_AURA_WALLPAPERS_5_GRADIENT5_LARGE
, WALLPAPER_LAYOUT_TILE
};
76 const WallpaperInfo kDefaultSmallWallpaper
=
77 { IDR_AURA_WALLPAPERS_5_GRADIENT5_SMALL
, WALLPAPER_LAYOUT_TILE
};
78 const WallpaperInfo kGuestLargeWallpaper
= kDefaultLargeWallpaper
;
79 const WallpaperInfo kGuestSmallWallpaper
= kDefaultSmallWallpaper
;
82 const int kSmallWallpaperMaxWidth
= 1366;
83 const int kSmallWallpaperMaxHeight
= 800;
84 const int kLargeWallpaperMaxWidth
= 2560;
85 const int kLargeWallpaperMaxHeight
= 1700;
86 const int kWallpaperThumbnailWidth
= 108;
87 const int kWallpaperThumbnailHeight
= 68;
89 // DesktopBackgroundController::WallpaperLoader wraps background wallpaper
91 class DesktopBackgroundController::WallpaperLoader
92 : public base::RefCountedThreadSafe
<
93 DesktopBackgroundController::WallpaperLoader
> {
95 explicit WallpaperLoader(const WallpaperInfo
& info
)
99 static void LoadOnWorkerPoolThread(scoped_refptr
<WallpaperLoader
> wl
) {
100 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
101 wl
->LoadingWallpaper();
112 WallpaperResizer
* ReleaseWallpaperResizer() {
113 return wallpaper_resizer_
.release();
117 friend class base::RefCountedThreadSafe
<
118 DesktopBackgroundController::WallpaperLoader
>;
120 void LoadingWallpaper() {
121 if (cancel_flag_
.IsSet())
123 wallpaper_resizer_
.reset(new WallpaperResizer(info_
, GetRootWindowsSize()));
126 ~WallpaperLoader() {}
128 base::CancellationFlag cancel_flag_
;
130 scoped_ptr
<WallpaperResizer
> wallpaper_resizer_
;
132 const WallpaperInfo info_
;
134 DISALLOW_COPY_AND_ASSIGN(WallpaperLoader
);
137 DesktopBackgroundController::DesktopBackgroundController()
139 desktop_background_mode_(BACKGROUND_NONE
),
140 background_color_(kTransparentColor
),
141 weak_ptr_factory_(this) {
144 DesktopBackgroundController::~DesktopBackgroundController() {
145 CancelPendingWallpaperOperation();
148 gfx::ImageSkia
DesktopBackgroundController::GetWallpaper() const {
149 if (current_wallpaper_
)
150 return current_wallpaper_
->wallpaper_image();
151 return gfx::ImageSkia();
154 void DesktopBackgroundController::AddObserver(
155 DesktopBackgroundControllerObserver
* observer
) {
156 observers_
.AddObserver(observer
);
159 void DesktopBackgroundController::RemoveObserver(
160 DesktopBackgroundControllerObserver
* observer
) {
161 observers_
.RemoveObserver(observer
);
164 WallpaperLayout
DesktopBackgroundController::GetWallpaperLayout() const {
165 if (current_wallpaper_
)
166 return current_wallpaper_
->wallpaper_info().layout
;
167 return WALLPAPER_LAYOUT_CENTER_CROPPED
;
170 gfx::ImageSkia
DesktopBackgroundController::GetCurrentWallpaperImage() {
171 if (desktop_background_mode_
!= BACKGROUND_IMAGE
)
172 return gfx::ImageSkia();
173 return GetWallpaper();
176 int DesktopBackgroundController::GetWallpaperIDR() const {
177 if (wallpaper_loader_
.get())
178 return wallpaper_loader_
->idr();
179 else if (current_wallpaper_
)
180 return current_wallpaper_
->wallpaper_info().idr
;
185 void DesktopBackgroundController::OnRootWindowAdded(
186 aura::RootWindow
* root_window
) {
187 // The background hasn't been set yet.
188 if (desktop_background_mode_
== BACKGROUND_NONE
)
191 // Handle resolution change for "built-in" images.
192 if (BACKGROUND_IMAGE
== desktop_background_mode_
&&
193 current_wallpaper_
.get()) {
194 gfx::Size root_window_size
= root_window
->GetHostSize();
195 int width
= current_wallpaper_
->wallpaper_image().width();
196 int height
= current_wallpaper_
->wallpaper_image().height();
197 // Reloads wallpaper if current wallpaper is smaller than the new added root
199 if (width
< root_window_size
.width() ||
200 height
< root_window_size
.height()) {
201 current_wallpaper_
.reset(NULL
);
202 ash::Shell::GetInstance()->user_wallpaper_delegate()->
207 InstallDesktopController(root_window
);
210 void DesktopBackgroundController::SetDefaultWallpaper(
211 const WallpaperInfo
& info
) {
212 DCHECK_NE(GetWallpaperIDR(), info
.idr
);
214 CancelPendingWallpaperOperation();
215 wallpaper_loader_
= new WallpaperLoader(info
);
216 base::WorkerPool::PostTaskAndReply(
218 base::Bind(&WallpaperLoader::LoadOnWorkerPoolThread
, wallpaper_loader_
),
219 base::Bind(&DesktopBackgroundController::OnWallpaperLoadCompleted
,
220 weak_ptr_factory_
.GetWeakPtr(),
222 true /* task_is_slow */);
225 void DesktopBackgroundController::SetCustomWallpaper(
226 const gfx::ImageSkia
& wallpaper
,
227 WallpaperLayout layout
) {
228 CancelPendingWallpaperOperation();
229 if (current_wallpaper_
.get() &&
230 current_wallpaper_
->wallpaper_image().BackedBySameObjectAs(wallpaper
)) {
234 WallpaperInfo info
= { -1, layout
};
235 current_wallpaper_
.reset(new WallpaperResizer(info
, GetRootWindowsSize(),
237 current_wallpaper_
->StartResize();
238 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver
, observers_
,
239 OnWallpaperDataChanged());
240 SetDesktopBackgroundImageMode();
243 void DesktopBackgroundController::CancelPendingWallpaperOperation() {
244 // Set canceled flag of previous request to skip unneeded loading.
245 if (wallpaper_loader_
.get())
246 wallpaper_loader_
->Cancel();
248 // Cancel reply callback for previous request.
249 weak_ptr_factory_
.InvalidateWeakPtrs();
252 void DesktopBackgroundController::SetDesktopBackgroundSolidColorMode(
254 background_color_
= color
;
255 desktop_background_mode_
= BACKGROUND_SOLID_COLOR
;
257 InstallDesktopControllerForAllWindows();
260 void DesktopBackgroundController::CreateEmptyWallpaper() {
261 current_wallpaper_
.reset(NULL
);
262 SetDesktopBackgroundImageMode();
265 WallpaperResolution
DesktopBackgroundController::GetAppropriateResolution() {
266 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
267 WallpaperResolution resolution
= WALLPAPER_RESOLUTION_SMALL
;
268 Shell::RootWindowList root_windows
= Shell::GetAllRootWindows();
269 for (Shell::RootWindowList::iterator iter
= root_windows
.begin();
270 iter
!= root_windows
.end(); ++iter
) {
271 // Compare to host size as constants are defined in terms of
272 // physical pixel size.
273 // TODO(oshima): This may not be ideal for fractional scaling
274 // scenario. Revisit and fix if necessary.
275 gfx::Size host_window_size
= (*iter
)->GetHostSize();
276 if (host_window_size
.width() > kSmallWallpaperMaxWidth
||
277 host_window_size
.height() > kSmallWallpaperMaxHeight
) {
278 resolution
= WALLPAPER_RESOLUTION_LARGE
;
284 bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
288 return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
289 GetBackgroundContainerId(true));
292 bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
296 return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
297 GetBackgroundContainerId(false));
300 void DesktopBackgroundController::OnWindowDestroying(aura::Window
* window
) {
301 window
->SetProperty(kDesktopController
,
302 static_cast<internal::DesktopBackgroundWidgetController
*>(NULL
));
303 window
->SetProperty(kAnimatingDesktopController
,
304 static_cast<internal::AnimatingDesktopController
*>(NULL
));
307 void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
308 desktop_background_mode_
= BACKGROUND_IMAGE
;
309 InstallDesktopControllerForAllWindows();
312 void DesktopBackgroundController::OnWallpaperLoadCompleted(
313 scoped_refptr
<WallpaperLoader
> wl
) {
314 current_wallpaper_
.reset(wl
->ReleaseWallpaperResizer());
315 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver
, observers_
,
316 OnWallpaperDataChanged());
318 SetDesktopBackgroundImageMode();
320 DCHECK(wl
.get() == wallpaper_loader_
.get());
321 wallpaper_loader_
= NULL
;
324 void DesktopBackgroundController::NotifyAnimationFinished() {
325 Shell
* shell
= Shell::GetInstance();
326 shell
->GetPrimaryRootWindowController()->HandleDesktopBackgroundVisible();
327 shell
->user_wallpaper_delegate()->OnWallpaperAnimationFinished();
330 ui::Layer
* DesktopBackgroundController::SetColorLayerForContainer(
332 aura::RootWindow
* root_window
,
334 ui::Layer
* background_layer
= new ui::Layer(ui::LAYER_SOLID_COLOR
);
335 background_layer
->SetColor(color
);
337 Shell::GetContainer(root_window
,container_id
)->
338 layer()->Add(background_layer
);
340 base::MessageLoop::current()->PostTask(
342 base::Bind(&DesktopBackgroundController::NotifyAnimationFinished
,
343 weak_ptr_factory_
.GetWeakPtr()));
345 return background_layer
;
348 void DesktopBackgroundController::InstallDesktopController(
349 aura::RootWindow
* root_window
) {
350 internal::DesktopBackgroundWidgetController
* component
= NULL
;
351 int container_id
= GetBackgroundContainerId(locked_
);
353 switch (desktop_background_mode_
) {
354 case BACKGROUND_IMAGE
: {
355 views::Widget
* widget
= internal::CreateDesktopBackground(root_window
,
357 component
= new internal::DesktopBackgroundWidgetController(widget
);
360 case BACKGROUND_SOLID_COLOR
: {
361 ui::Layer
* layer
= SetColorLayerForContainer(background_color_
,
364 component
= new internal::DesktopBackgroundWidgetController(layer
);
367 case BACKGROUND_NONE
:
371 // Ensure we're only observing the root window once. Don't rely on a window
372 // property check as those can be cleared by tests resetting the background.
373 if (!root_window
->HasObserver(this))
374 root_window
->AddObserver(this);
376 internal::AnimatingDesktopController
* animating_controller
=
377 root_window
->GetProperty(kAnimatingDesktopController
);
378 if (animating_controller
)
379 animating_controller
->StopAnimating();
380 root_window
->SetProperty(kAnimatingDesktopController
,
381 new internal::AnimatingDesktopController(component
));
384 void DesktopBackgroundController::InstallDesktopControllerForAllWindows() {
385 Shell::RootWindowList root_windows
= Shell::GetAllRootWindows();
386 for (Shell::RootWindowList::iterator iter
= root_windows
.begin();
387 iter
!= root_windows
.end(); ++iter
) {
388 InstallDesktopController(*iter
);
392 bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container
,
395 Shell::RootWindowList root_windows
= Shell::GetAllRootWindows();
396 for (Shell::RootWindowList::iterator iter
= root_windows
.begin();
397 iter
!= root_windows
.end(); ++iter
) {
398 aura::RootWindow
* root_window
= *iter
;
399 // In the steady state (no animation playing) the background widget
400 // controller exists in the kDesktopController property.
401 DesktopBackgroundWidgetController
* desktop_controller
= root_window
->
402 GetProperty(kDesktopController
);
403 if (desktop_controller
) {
404 moved
|= desktop_controller
->Reparent(root_window
,
408 // During desktop show animations the controller lives in
409 // kAnimatingDesktopController.
410 // NOTE: If a wallpaper load happens during a desktop show animation there
411 // can temporarily be two desktop background widgets. We must reparent
412 // both of them - one above and one here.
413 DesktopBackgroundWidgetController
* animating_controller
=
414 root_window
->GetProperty(kAnimatingDesktopController
) ?
415 root_window
->GetProperty(kAnimatingDesktopController
)->
416 GetController(false) :
418 if (animating_controller
) {
419 moved
|= animating_controller
->Reparent(root_window
,
427 int DesktopBackgroundController::GetBackgroundContainerId(bool locked
) {
428 return locked
? internal::kShellWindowId_LockScreenBackgroundContainer
:
429 internal::kShellWindowId_DesktopBackgroundContainer
;