blob: 84da8b0d635c4c345ab0546492e3ef67b9a44722 [file] [log] [blame]
// Copyright (c) 2012 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 "ash/desktop_background/desktop_background_controller.h"
#include "ash/desktop_background/desktop_background_controller_observer.h"
#include "ash/desktop_background/desktop_background_view.h"
#include "ash/desktop_background/desktop_background_widget_controller.h"
#include "ash/desktop_background/user_wallpaper_delegate.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/shell_factory.h"
#include "ash/shell_window_ids.h"
#include "ash/wm/root_window_layout_manager.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/synchronization/cancellation_flag.h"
#include "base/threading/worker_pool.h"
#include "content/public/browser/browser_thread.h"
#include "grit/ash_wallpaper_resources.h"
#include "ui/aura/root_window.h"
#include "ui/aura/window.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/image/image.h"
#include "ui/views/widget/widget.h"
using ash::internal::DesktopBackgroundWidgetController;
using ash::internal::kAnimatingDesktopController;
using ash::internal::kDesktopController;
using content::BrowserThread;
namespace ash {
namespace {
const SkColor kTransparentColor = SkColorSetARGB(0x00, 0x00, 0x00, 0x00);
internal::RootWindowLayoutManager* GetRootWindowLayoutManager(
aura::RootWindow* root_window) {
return static_cast<internal::RootWindowLayoutManager*>(
root_window->layout_manager());
}
} // namespace
#if defined(GOOGLE_CHROME_BUILD)
const WallpaperInfo kDefaultLargeWallpaper =
{ IDR_AURA_WALLPAPERS_2_LANDSCAPE8_LARGE, WALLPAPER_LAYOUT_CENTER_CROPPED };
const WallpaperInfo kDefaultSmallWallpaper =
{ IDR_AURA_WALLPAPERS_2_LANDSCAPE8_SMALL, WALLPAPER_LAYOUT_CENTER };
const WallpaperInfo kGuestLargeWallpaper =
{ IDR_AURA_WALLPAPERS_2_LANDSCAPE7_LARGE, WALLPAPER_LAYOUT_CENTER_CROPPED };
const WallpaperInfo kGuestSmallWallpaper =
{ IDR_AURA_WALLPAPERS_2_LANDSCAPE7_SMALL, WALLPAPER_LAYOUT_CENTER };
#else
const WallpaperInfo kDefaultLargeWallpaper =
{ IDR_AURA_WALLPAPERS_5_GRADIENT5_LARGE, WALLPAPER_LAYOUT_TILE };
const WallpaperInfo kDefaultSmallWallpaper =
{ IDR_AURA_WALLPAPERS_5_GRADIENT5_SMALL, WALLPAPER_LAYOUT_TILE };
const WallpaperInfo kGuestLargeWallpaper = kDefaultLargeWallpaper;
const WallpaperInfo kGuestSmallWallpaper = kDefaultSmallWallpaper;
#endif
const int kSmallWallpaperMaxWidth = 1366;
const int kSmallWallpaperMaxHeight = 800;
const int kLargeWallpaperMaxWidth = 2560;
const int kLargeWallpaperMaxHeight = 1700;
// Stores the current wallpaper data.
struct DesktopBackgroundController::WallpaperData {
explicit WallpaperData(const WallpaperInfo& info)
: wallpaper_info(info),
wallpaper_image(*(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
info.idr).ToImageSkia())) {
}
WallpaperData(const WallpaperInfo& info, const gfx::ImageSkia& image)
: wallpaper_info(info),
wallpaper_image(image) {
}
const WallpaperInfo wallpaper_info;
const gfx::ImageSkia wallpaper_image;
};
// DesktopBackgroundController::WallpaperLoader wraps background wallpaper
// loading.
class DesktopBackgroundController::WallpaperLoader
: public base::RefCountedThreadSafe<
DesktopBackgroundController::WallpaperLoader> {
public:
explicit WallpaperLoader(const WallpaperInfo& info)
: info_(info) {
}
static void LoadOnWorkerPoolThread(scoped_refptr<WallpaperLoader> wl) {
DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
wl->LoadingWallpaper();
}
void Cancel() {
cancel_flag_.Set();
}
int idr() const {
return info_.idr;
}
WallpaperData* ReleaseWallpaperData() {
return wallpaper_data_.release();
}
private:
friend class base::RefCountedThreadSafe<
DesktopBackgroundController::WallpaperLoader>;
void LoadingWallpaper() {
if (cancel_flag_.IsSet())
return;
wallpaper_data_.reset(new WallpaperData(info_));
}
~WallpaperLoader() {}
base::CancellationFlag cancel_flag_;
scoped_ptr<WallpaperData> wallpaper_data_;
const WallpaperInfo info_;
DISALLOW_COPY_AND_ASSIGN(WallpaperLoader);
};
DesktopBackgroundController::DesktopBackgroundController()
: locked_(false),
desktop_background_mode_(BACKGROUND_SOLID_COLOR),
background_color_(kTransparentColor),
weak_ptr_factory_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) {
}
DesktopBackgroundController::~DesktopBackgroundController() {
CancelPendingWallpaperOperation();
}
gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const {
if (current_wallpaper_.get())
return current_wallpaper_->wallpaper_image;
return gfx::ImageSkia();
}
void DesktopBackgroundController::AddObserver(
DesktopBackgroundControllerObserver* observer) {
observers_.AddObserver(observer);
}
void DesktopBackgroundController::RemoveObserver(
DesktopBackgroundControllerObserver* observer) {
observers_.RemoveObserver(observer);
}
WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
if (current_wallpaper_.get())
return current_wallpaper_->wallpaper_info.layout;
return WALLPAPER_LAYOUT_CENTER_CROPPED;
}
gfx::ImageSkia DesktopBackgroundController::GetCurrentWallpaperImage() {
if (desktop_background_mode_ != BACKGROUND_IMAGE)
return gfx::ImageSkia();
return GetWallpaper();
}
int DesktopBackgroundController::GetWallpaperIDR() const {
if (wallpaper_loader_.get())
return wallpaper_loader_->idr();
else if (current_wallpaper_.get())
return current_wallpaper_->wallpaper_info.idr;
else
return -1;
}
void DesktopBackgroundController::OnRootWindowAdded(
aura::RootWindow* root_window) {
// Handle resolution change for "built-in" images.
if (BACKGROUND_IMAGE == desktop_background_mode_ &&
current_wallpaper_.get()) {
gfx::Size root_window_size = root_window->GetHostSize();
// Loads a higher resolution wallpaper if new root window is larger than
// small screen.
if (kSmallWallpaperMaxWidth < root_window_size.width() ||
kSmallWallpaperMaxHeight < root_window_size.height()) {
current_wallpaper_.reset(NULL);
ash::Shell::GetInstance()->user_wallpaper_delegate()->
UpdateWallpaper();
}
}
InstallDesktopController(root_window);
}
void DesktopBackgroundController::SetDefaultWallpaper(
const WallpaperInfo& info) {
DCHECK_NE(GetWallpaperIDR(), info.idr);
CancelPendingWallpaperOperation();
wallpaper_loader_ = new WallpaperLoader(info);
base::WorkerPool::PostTaskAndReply(
FROM_HERE,
base::Bind(&WallpaperLoader::LoadOnWorkerPoolThread, wallpaper_loader_),
base::Bind(&DesktopBackgroundController::OnWallpaperLoadCompleted,
weak_ptr_factory_.GetWeakPtr(),
wallpaper_loader_),
true /* task_is_slow */);
}
void DesktopBackgroundController::SetCustomWallpaper(
const gfx::ImageSkia& wallpaper,
WallpaperLayout layout) {
CancelPendingWallpaperOperation();
if (current_wallpaper_.get() &&
current_wallpaper_->wallpaper_image.BackedBySameObjectAs(wallpaper)) {
return;
}
WallpaperInfo info = { -1, layout };
current_wallpaper_.reset(new WallpaperData(info, wallpaper));
FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_,
OnWallpaperDataChanged());
SetDesktopBackgroundImageMode();
}
void DesktopBackgroundController::CancelPendingWallpaperOperation() {
// Set canceled flag of previous request to skip unneeded loading.
if (wallpaper_loader_.get())
wallpaper_loader_->Cancel();
// Cancel reply callback for previous request.
weak_ptr_factory_.InvalidateWeakPtrs();
}
void DesktopBackgroundController::SetDesktopBackgroundSolidColorMode(
SkColor color) {
background_color_ = color;
desktop_background_mode_ = BACKGROUND_SOLID_COLOR;
InstallDesktopControllerForAllWindows();
}
void DesktopBackgroundController::CreateEmptyWallpaper() {
current_wallpaper_.reset(NULL);
SetDesktopBackgroundImageMode();
}
WallpaperResolution DesktopBackgroundController::GetAppropriateResolution() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
WallpaperResolution resolution = WALLPAPER_RESOLUTION_SMALL;
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
for (Shell::RootWindowList::iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
gfx::Size root_window_size = (*iter)->GetHostSize();
if (root_window_size.width() > kSmallWallpaperMaxWidth ||
root_window_size.height() > kSmallWallpaperMaxHeight) {
resolution = WALLPAPER_RESOLUTION_LARGE;
}
}
return resolution;
}
bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
if (locked_)
return false;
locked_ = true;
return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
GetBackgroundContainerId(true));
}
bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
if (!locked_)
return false;
locked_ = false;
return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
GetBackgroundContainerId(false));
}
void DesktopBackgroundController::OnWindowDestroying(aura::Window* window) {
window->SetProperty(kDesktopController,
static_cast<internal::DesktopBackgroundWidgetController*>(NULL));
window->SetProperty(kAnimatingDesktopController,
static_cast<internal::AnimatingDesktopController*>(NULL));
}
void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
desktop_background_mode_ = BACKGROUND_IMAGE;
InstallDesktopControllerForAllWindows();
}
void DesktopBackgroundController::OnWallpaperLoadCompleted(
scoped_refptr<WallpaperLoader> wl) {
current_wallpaper_.reset(wl->ReleaseWallpaperData());
FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_,
OnWallpaperDataChanged());
SetDesktopBackgroundImageMode();
DCHECK(wl.get() == wallpaper_loader_.get());
wallpaper_loader_ = NULL;
}
void DesktopBackgroundController::NotifyAnimationFinished() {
Shell* shell = Shell::GetInstance();
shell->GetPrimaryRootWindowController()->HandleDesktopBackgroundVisible();
shell->user_wallpaper_delegate()->OnWallpaperAnimationFinished();
}
ui::Layer* DesktopBackgroundController::SetColorLayerForContainer(
SkColor color,
aura::RootWindow* root_window,
int container_id) {
ui::Layer* background_layer = new ui::Layer(ui::LAYER_SOLID_COLOR);
background_layer->SetColor(color);
Shell::GetContainer(root_window,container_id)->
layer()->Add(background_layer);
MessageLoop::current()->PostTask(FROM_HERE,
base::Bind(&DesktopBackgroundController::NotifyAnimationFinished,
weak_ptr_factory_.GetWeakPtr()));
return background_layer;
}
void DesktopBackgroundController::InstallDesktopController(
aura::RootWindow* root_window) {
internal::DesktopBackgroundWidgetController* component = NULL;
int container_id = GetBackgroundContainerId(locked_);
switch (desktop_background_mode_) {
case BACKGROUND_IMAGE: {
views::Widget* widget = internal::CreateDesktopBackground(root_window,
container_id);
component = new internal::DesktopBackgroundWidgetController(widget);
break;
}
case BACKGROUND_SOLID_COLOR: {
ui::Layer* layer = SetColorLayerForContainer(background_color_,
root_window,
container_id);
component = new internal::DesktopBackgroundWidgetController(layer);
break;
}
default: {
NOTREACHED();
}
}
// Ensure we're only observing the root window once. Don't rely on a window
// property check as those can be cleared by tests resetting the background.
if (!root_window->HasObserver(this))
root_window->AddObserver(this);
internal::AnimatingDesktopController* animating_controller =
root_window->GetProperty(kAnimatingDesktopController);
if (animating_controller)
animating_controller->StopAnimating();
root_window->SetProperty(kAnimatingDesktopController,
new internal::AnimatingDesktopController(component));
}
void DesktopBackgroundController::InstallDesktopControllerForAllWindows() {
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
for (Shell::RootWindowList::iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
InstallDesktopController(*iter);
}
}
bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container,
int dst_container) {
bool moved = false;
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
for (Shell::RootWindowList::iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
aura::RootWindow* root_window = *iter;
// In the steady state (no animation playing) the background widget
// controller exists in the kDesktopController property.
DesktopBackgroundWidgetController* desktop_controller = root_window->
GetProperty(kDesktopController);
if (desktop_controller) {
moved |= desktop_controller->Reparent(root_window,
src_container,
dst_container);
}
// During desktop show animations the controller lives in
// kAnimatingDesktopController.
// NOTE: If a wallpaper load happens during a desktop show animation there
// can temporarily be two desktop background widgets. We must reparent
// both of them - one above and one here.
DesktopBackgroundWidgetController* animating_controller =
root_window->GetProperty(kAnimatingDesktopController) ?
root_window->GetProperty(kAnimatingDesktopController)->
GetController(false) :
NULL;
if (animating_controller) {
moved |= animating_controller->Reparent(root_window,
src_container,
dst_container);
}
}
return moved;
}
int DesktopBackgroundController::GetBackgroundContainerId(bool locked) {
return locked ? internal::kShellWindowId_LockScreenBackgroundContainer :
internal::kShellWindowId_DesktopBackgroundContainer;
}
} // namespace ash