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/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/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 "grit/ash_resources.h"
28 #include "ui/aura/root_window.h"
29 #include "ui/aura/window.h"
30 #include "ui/compositor/layer.h"
31 #include "ui/gfx/codec/jpeg_codec.h"
32 #include "ui/gfx/image/image_skia.h"
33 #include "ui/gfx/rect.h"
34 #include "ui/views/widget/widget.h"
36 using ash::internal::DesktopBackgroundWidgetController
;
37 using content::BrowserThread
;
42 // How long to wait reloading the wallpaper after the max display has
44 const int kWallpaperReloadDelayMs
= 2000;
48 const int kSmallWallpaperMaxWidth
= 1366;
49 const int kSmallWallpaperMaxHeight
= 800;
50 const int kLargeWallpaperMaxWidth
= 2560;
51 const int kLargeWallpaperMaxHeight
= 1700;
52 const int kWallpaperThumbnailWidth
= 108;
53 const int kWallpaperThumbnailHeight
= 68;
55 // DesktopBackgroundController::WallpaperLoader wraps background wallpaper
57 class DesktopBackgroundController::WallpaperLoader
58 : public base::RefCountedThreadSafe
<
59 DesktopBackgroundController::WallpaperLoader
> {
61 // If set, |file_path| must be a trusted (i.e. read-only,
62 // non-user-controlled) file containing a JPEG image.
63 WallpaperLoader(const base::FilePath
& file_path
,
64 WallpaperLayout file_layout
,
66 WallpaperLayout resource_layout
)
67 : file_path_(file_path
),
68 file_layout_(file_layout
),
69 resource_id_(resource_id
),
70 resource_layout_(resource_layout
) {
73 void LoadOnWorkerPoolThread() {
74 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI
));
75 if (cancel_flag_
.IsSet())
78 if (!file_path_
.empty())
79 file_bitmap_
= LoadSkBitmapFromJPEGFile(file_path_
);
81 if (cancel_flag_
.IsSet())
85 gfx::ImageSkia image
= gfx::ImageSkia::CreateFrom1xBitmap(*file_bitmap_
);
86 wallpaper_resizer_
.reset(new WallpaperResizer(
87 image
, GetMaxDisplaySizeInNative(), file_layout_
));
89 wallpaper_resizer_
.reset(new WallpaperResizer(
90 resource_id_
, GetMaxDisplaySizeInNative(), resource_layout_
));
94 const base::FilePath
& file_path() const { return file_path_
; }
95 int resource_id() const { return resource_id_
; }
102 return cancel_flag_
.IsSet();
105 WallpaperResizer
* ReleaseWallpaperResizer() {
106 return wallpaper_resizer_
.release();
110 friend class base::RefCountedThreadSafe
<
111 DesktopBackgroundController::WallpaperLoader
>;
113 // Loads a JPEG image from |path|, a trusted file -- note that the image
114 // is not loaded in a sandboxed process. Returns an empty pointer on
116 static scoped_ptr
<SkBitmap
> LoadSkBitmapFromJPEGFile(
117 const base::FilePath
& path
) {
119 if (!base::ReadFileToString(path
, &data
)) {
120 LOG(ERROR
) << "Unable to read data from " << path
.value();
121 return scoped_ptr
<SkBitmap
>();
124 scoped_ptr
<SkBitmap
> bitmap(gfx::JPEGCodec::Decode(
125 reinterpret_cast<const unsigned char*>(data
.data()), data
.size()));
127 LOG(ERROR
) << "Unable to decode JPEG data from " << path
.value();
128 return bitmap
.Pass();
131 ~WallpaperLoader() {}
133 base::CancellationFlag cancel_flag_
;
135 // Bitmap loaded from |file_path_|.
136 scoped_ptr
<SkBitmap
> file_bitmap_
;
138 scoped_ptr
<WallpaperResizer
> wallpaper_resizer_
;
140 // Path to a trusted JPEG file.
141 base::FilePath file_path_
;
143 // Layout to be used when displaying the image from |file_path_|.
144 WallpaperLayout file_layout_
;
146 // ID of an image resource to use if |file_path_| is empty or unloadable.
149 // Layout to be used when displaying |resource_id_|.
150 WallpaperLayout resource_layout_
;
152 DISALLOW_COPY_AND_ASSIGN(WallpaperLoader
);
155 DesktopBackgroundController::DesktopBackgroundController()
156 : command_line_for_testing_(NULL
),
158 desktop_background_mode_(BACKGROUND_NONE
),
159 current_default_wallpaper_resource_id_(-1),
160 weak_ptr_factory_(this),
161 wallpaper_reload_delay_(kWallpaperReloadDelayMs
) {
162 Shell::GetInstance()->display_controller()->AddObserver(this);
165 DesktopBackgroundController::~DesktopBackgroundController() {
166 CancelDefaultWallpaperLoader();
167 Shell::GetInstance()->display_controller()->RemoveObserver(this);
170 gfx::ImageSkia
DesktopBackgroundController::GetWallpaper() const {
171 if (current_wallpaper_
)
172 return current_wallpaper_
->image();
173 return gfx::ImageSkia();
176 void DesktopBackgroundController::AddObserver(
177 DesktopBackgroundControllerObserver
* observer
) {
178 observers_
.AddObserver(observer
);
181 void DesktopBackgroundController::RemoveObserver(
182 DesktopBackgroundControllerObserver
* observer
) {
183 observers_
.RemoveObserver(observer
);
186 WallpaperLayout
DesktopBackgroundController::GetWallpaperLayout() const {
187 if (current_wallpaper_
)
188 return current_wallpaper_
->layout();
189 return WALLPAPER_LAYOUT_CENTER_CROPPED
;
192 void DesktopBackgroundController::OnRootWindowAdded(aura::Window
* root_window
) {
193 // The background hasn't been set yet.
194 if (desktop_background_mode_
== BACKGROUND_NONE
)
197 // Handle resolution change for "built-in" images.
198 gfx::Size max_display_size
= GetMaxDisplaySizeInNative();
199 if (current_max_display_size_
!= max_display_size
) {
200 current_max_display_size_
= max_display_size
;
201 if (desktop_background_mode_
== BACKGROUND_IMAGE
&&
202 current_wallpaper_
.get())
206 InstallDesktopController(root_window
);
209 bool DesktopBackgroundController::SetDefaultWallpaper(bool is_guest
) {
210 const bool use_large
=
211 GetAppropriateResolution() == WALLPAPER_RESOLUTION_LARGE
;
213 base::FilePath file_path
;
214 WallpaperLayout file_layout
= use_large
? WALLPAPER_LAYOUT_CENTER_CROPPED
:
215 WALLPAPER_LAYOUT_CENTER
;
216 int resource_id
= use_large
? IDR_AURA_WALLPAPER_DEFAULT_LARGE
:
217 IDR_AURA_WALLPAPER_DEFAULT_SMALL
;
218 WallpaperLayout resource_layout
= WALLPAPER_LAYOUT_TILE
;
220 CommandLine
* command_line
= command_line_for_testing_
?
221 command_line_for_testing_
: CommandLine::ForCurrentProcess();
222 const char* switch_name
= NULL
;
224 switch_name
= use_large
? switches::kAshGuestWallpaperLarge
:
225 switches::kAshGuestWallpaperSmall
;
227 switch_name
= use_large
? switches::kAshDefaultWallpaperLarge
:
228 switches::kAshDefaultWallpaperSmall
;
230 file_path
= command_line
->GetSwitchValuePath(switch_name
);
232 if (DefaultWallpaperIsAlreadyLoadingOrLoaded(file_path
, resource_id
))
235 CancelDefaultWallpaperLoader();
236 default_wallpaper_loader_
= new WallpaperLoader(
237 file_path
, file_layout
, resource_id
, resource_layout
);
238 base::WorkerPool::PostTaskAndReply(
240 base::Bind(&WallpaperLoader::LoadOnWorkerPoolThread
,
241 default_wallpaper_loader_
),
242 base::Bind(&DesktopBackgroundController::OnDefaultWallpaperLoadCompleted
,
243 weak_ptr_factory_
.GetWeakPtr(),
244 default_wallpaper_loader_
),
245 true /* task_is_slow */);
249 void DesktopBackgroundController::SetCustomWallpaper(
250 const gfx::ImageSkia
& image
,
251 WallpaperLayout layout
) {
252 CancelDefaultWallpaperLoader();
254 if (CustomWallpaperIsAlreadyLoaded(image
))
257 current_wallpaper_
.reset(new WallpaperResizer(
258 image
, GetMaxDisplaySizeInNative(), layout
));
259 current_wallpaper_
->StartResize();
261 current_default_wallpaper_path_
= base::FilePath();
262 current_default_wallpaper_resource_id_
= -1;
264 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver
, observers_
,
265 OnWallpaperDataChanged());
266 SetDesktopBackgroundImageMode();
269 void DesktopBackgroundController::CancelDefaultWallpaperLoader() {
270 // Set canceled flag of previous request to skip unneeded loading.
271 if (default_wallpaper_loader_
.get())
272 default_wallpaper_loader_
->Cancel();
274 // Cancel reply callback for previous request.
275 weak_ptr_factory_
.InvalidateWeakPtrs();
278 void DesktopBackgroundController::CreateEmptyWallpaper() {
279 current_wallpaper_
.reset(NULL
);
280 SetDesktopBackgroundImageMode();
283 WallpaperResolution
DesktopBackgroundController::GetAppropriateResolution() {
284 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI
));
285 gfx::Size size
= GetMaxDisplaySizeInNative();
286 return (size
.width() > kSmallWallpaperMaxWidth
||
287 size
.height() > kSmallWallpaperMaxHeight
) ?
288 WALLPAPER_RESOLUTION_LARGE
: WALLPAPER_RESOLUTION_SMALL
;
291 bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
295 return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
296 GetBackgroundContainerId(true));
299 bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
303 return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
304 GetBackgroundContainerId(false));
307 void DesktopBackgroundController::OnDisplayConfigurationChanged() {
308 gfx::Size max_display_size
= GetMaxDisplaySizeInNative();
309 if (current_max_display_size_
!= max_display_size
) {
310 current_max_display_size_
= max_display_size
;
311 if (desktop_background_mode_
== BACKGROUND_IMAGE
&&
312 current_wallpaper_
.get()) {
314 timer_
.Start(FROM_HERE
,
315 base::TimeDelta::FromMilliseconds(wallpaper_reload_delay_
),
317 &DesktopBackgroundController::UpdateWallpaper
);
322 bool DesktopBackgroundController::DefaultWallpaperIsAlreadyLoadingOrLoaded(
323 const base::FilePath
& image_file
,
324 int image_resource_id
) const {
325 return (default_wallpaper_loader_
.get() &&
326 !default_wallpaper_loader_
->IsCanceled() &&
327 default_wallpaper_loader_
->file_path() == image_file
&&
328 default_wallpaper_loader_
->resource_id() == image_resource_id
) ||
329 (current_wallpaper_
.get() &&
330 current_default_wallpaper_path_
== image_file
&&
331 current_default_wallpaper_resource_id_
== image_resource_id
);
334 bool DesktopBackgroundController::CustomWallpaperIsAlreadyLoaded(
335 const gfx::ImageSkia
& image
) const {
336 return current_wallpaper_
.get() &&
337 (WallpaperResizer::GetImageId(image
) ==
338 current_wallpaper_
->original_image_id());
341 void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
342 desktop_background_mode_
= BACKGROUND_IMAGE
;
343 InstallDesktopControllerForAllWindows();
346 void DesktopBackgroundController::OnDefaultWallpaperLoadCompleted(
347 scoped_refptr
<WallpaperLoader
> loader
) {
348 current_wallpaper_
.reset(loader
->ReleaseWallpaperResizer());
349 current_wallpaper_
->StartResize();
350 current_default_wallpaper_path_
= loader
->file_path();
351 current_default_wallpaper_resource_id_
= loader
->resource_id();
352 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver
, observers_
,
353 OnWallpaperDataChanged());
355 SetDesktopBackgroundImageMode();
357 DCHECK(loader
.get() == default_wallpaper_loader_
.get());
358 default_wallpaper_loader_
= NULL
;
361 void DesktopBackgroundController::InstallDesktopController(
362 aura::Window
* root_window
) {
363 internal::DesktopBackgroundWidgetController
* component
= NULL
;
364 int container_id
= GetBackgroundContainerId(locked_
);
366 switch (desktop_background_mode_
) {
367 case BACKGROUND_IMAGE
: {
368 views::Widget
* widget
= internal::CreateDesktopBackground(root_window
,
370 component
= new internal::DesktopBackgroundWidgetController(widget
);
373 case BACKGROUND_NONE
:
377 internal::GetRootWindowController(root_window
)->
378 SetAnimatingWallpaperController(
379 new internal::AnimatingDesktopController(component
));
381 component
->StartAnimating(internal::GetRootWindowController(root_window
));
384 void DesktopBackgroundController::InstallDesktopControllerForAllWindows() {
385 aura::Window::Windows root_windows
= Shell::GetAllRootWindows();
386 for (aura::Window::Windows::iterator iter
= root_windows
.begin();
387 iter
!= root_windows
.end(); ++iter
) {
388 InstallDesktopController(*iter
);
390 current_max_display_size_
= GetMaxDisplaySizeInNative();
393 bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container
,
396 Shell::RootWindowControllerList controllers
=
397 Shell::GetAllRootWindowControllers();
398 for (Shell::RootWindowControllerList::iterator iter
= controllers
.begin();
399 iter
!= controllers
.end(); ++iter
) {
400 internal::RootWindowController
* root_window_controller
= *iter
;
401 // In the steady state (no animation playing) the background widget
402 // controller exists in the RootWindowController.
403 DesktopBackgroundWidgetController
* desktop_controller
=
404 root_window_controller
->wallpaper_controller();
405 if (desktop_controller
) {
406 moved
|= desktop_controller
->Reparent(
407 root_window_controller
->root_window(),
411 // During desktop show animations the controller lives in
412 // AnimatingDesktopController owned by RootWindowController.
413 // NOTE: If a wallpaper load happens during a desktop show animation there
414 // can temporarily be two desktop background widgets. We must reparent
415 // both of them - one above and one here.
416 DesktopBackgroundWidgetController
* animating_controller
=
417 root_window_controller
->animating_wallpaper_controller() ?
418 root_window_controller
->animating_wallpaper_controller()->
419 GetController(false) :
421 if (animating_controller
) {
422 moved
|= animating_controller
->Reparent(
423 root_window_controller
->root_window(),
431 int DesktopBackgroundController::GetBackgroundContainerId(bool locked
) {
432 return locked
? internal::kShellWindowId_LockScreenBackgroundContainer
:
433 internal::kShellWindowId_DesktopBackgroundContainer
;
436 void DesktopBackgroundController::UpdateWallpaper() {
437 current_wallpaper_
.reset(NULL
);
438 current_default_wallpaper_path_
= base::FilePath();
439 current_default_wallpaper_resource_id_
= -1;
440 ash::Shell::GetInstance()->user_wallpaper_delegate()->
445 gfx::Size
DesktopBackgroundController::GetMaxDisplaySizeInNative() {
448 std::vector
<gfx::Display
> displays
= Shell::GetScreen()->GetAllDisplays();
449 internal::DisplayManager
* display_manager
=
450 Shell::GetInstance()->display_manager();
452 for (std::vector
<gfx::Display
>::iterator iter
= displays
.begin();
453 iter
!= displays
.end(); ++iter
) {
454 // Don't use size_in_pixel because we want to use the native pixel size.
455 gfx::Size size_in_pixel
=
456 display_manager
->GetDisplayInfo(iter
->id()).bounds_in_native().size();
457 if (iter
->rotation() == gfx::Display::ROTATE_90
||
458 iter
->rotation() == gfx::Display::ROTATE_270
) {
459 size_in_pixel
= gfx::Size(size_in_pixel
.height(), size_in_pixel
.width());
461 width
= std::max(size_in_pixel
.width(), width
);
462 height
= std::max(size_in_pixel
.height(), height
);
464 return gfx::Size(width
, height
);