blob: 022ca0ba0c796d47753b6536e39bc8dac85d94e0 [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_view.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 "grit/ui_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/image/image.h"
#include "ui/views/widget/widget.h"
namespace ash {
namespace {
internal::RootWindowLayoutManager* GetRootWindowLayoutManager(
aura::RootWindow* root_window) {
return static_cast<internal::RootWindowLayoutManager*>(
root_window->layout_manager());
}
} // namespace
// Stores the current wallpaper data.
struct DesktopBackgroundController::WallpaperData {
explicit WallpaperData(int index)
: wallpaper_index(index),
wallpaper_layout(GetWallpaperInfo(index).layout),
wallpaper_image(*(ui::ResourceBundle::GetSharedInstance().GetImageNamed(
GetWallpaperInfo(index).id).ToImageSkia())) {
}
WallpaperData(WallpaperLayout layout, const gfx::ImageSkia& image)
: wallpaper_index(-1),
wallpaper_layout(layout),
wallpaper_image(image) {
}
const int wallpaper_index;
const WallpaperLayout wallpaper_layout;
const gfx::ImageSkia wallpaper_image;
};
// DesktopBackgroundController::WallpaperOperation wraps background wallpaper
// loading.
class DesktopBackgroundController::WallpaperOperation
: public base::RefCountedThreadSafe<
DesktopBackgroundController::WallpaperOperation> {
public:
explicit WallpaperOperation(int index) : index_(index) {
}
static void Run(scoped_refptr<WallpaperOperation> wo) {
wo->LoadingWallpaper();
}
void LoadingWallpaper() {
if (cancel_flag_.IsSet())
return;
wallpaper_data_.reset(new WallpaperData(index_));
}
void Cancel() {
cancel_flag_.Set();
}
WallpaperData* ReleaseWallpaperData() {
return wallpaper_data_.release();
}
private:
friend class base::RefCountedThreadSafe<
DesktopBackgroundController::WallpaperOperation>;
~WallpaperOperation() {}
base::CancellationFlag cancel_flag_;
scoped_ptr<WallpaperData> wallpaper_data_;
int index_;
DISALLOW_COPY_AND_ASSIGN(WallpaperOperation);
};
DesktopBackgroundController::DesktopBackgroundController()
: desktop_background_mode_(BACKGROUND_IMAGE),
background_color_(SK_ColorGRAY),
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();
}
WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
if (current_wallpaper_.get())
return current_wallpaper_->wallpaper_layout;
return CENTER_CROPPED;
}
SkBitmap DesktopBackgroundController::GetCurrentWallpaperImage() {
if (desktop_background_mode_ != BACKGROUND_IMAGE)
return SkBitmap();
return GetWallpaper();
}
void DesktopBackgroundController::OnRootWindowAdded(
aura::RootWindow* root_window) {
switch (desktop_background_mode_) {
case BACKGROUND_IMAGE:
if (current_wallpaper_.get()) {
SetDesktopBackgroundImage(root_window);
} else {
internal::CreateDesktopBackground(root_window);
}
break;
case BACKGROUND_SOLID_COLOR:
SetDesktopBackgroundSolidColorMode(background_color_);
break;
}
}
void DesktopBackgroundController::SetDefaultWallpaper(int index) {
// We should not change background when index is invalid. For instance, at
// login screen or stub_user login.
if (index == ash::GetInvalidWallpaperIndex()) {
CreateEmptyWallpaper();
return;
} else if (index == ash::GetSolidColorIndex()) {
SetDesktopBackgroundSolidColorMode(kLoginWallpaperColor);
return;
}
if (current_wallpaper_.get() && current_wallpaper_->wallpaper_index == index)
return;
CancelPendingWallpaperOperation();
wallpaper_op_ = new WallpaperOperation(index);
base::WorkerPool::PostTaskAndReply(
FROM_HERE,
base::Bind(&WallpaperOperation::Run, wallpaper_op_),
base::Bind(&DesktopBackgroundController::OnWallpaperLoadCompleted,
weak_ptr_factory_.GetWeakPtr(),
wallpaper_op_),
true /* task_is_slow */);
}
void DesktopBackgroundController::SetCustomWallpaper(
const gfx::ImageSkia& wallpaper,
WallpaperLayout layout) {
CancelPendingWallpaperOperation();
current_wallpaper_.reset(new WallpaperData(layout, wallpaper));
desktop_background_mode_ = BACKGROUND_IMAGE;
UpdateDesktopBackgroundImageMode();
}
void DesktopBackgroundController::CancelPendingWallpaperOperation() {
// Set canceled flag of previous request to skip unneeded loading.
if (wallpaper_op_.get())
wallpaper_op_->Cancel();
// Cancel reply callback for previous request.
weak_ptr_factory_.InvalidateWeakPtrs();
}
void DesktopBackgroundController::SetDesktopBackgroundSolidColorMode(
SkColor color) {
// Set a solid black background.
// TODO(derat): Remove this in favor of having the compositor only clear the
// viewport when there are regions not covered by a layer:
// http://crbug.com/113445
current_wallpaper_.reset(NULL);
background_color_ = color;
desktop_background_mode_ = BACKGROUND_SOLID_COLOR;
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
for (Shell::RootWindowList::iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
ui::Layer* background_layer = new ui::Layer(ui::LAYER_SOLID_COLOR);
background_layer->SetColor(color);
aura::RootWindow* root_window = *iter;
Shell::GetContainer(
root_window,
internal::kShellWindowId_DesktopBackgroundContainer)->
layer()->Add(background_layer);
GetRootWindowLayoutManager(root_window)->SetBackgroundLayer(
background_layer);
GetRootWindowLayoutManager(root_window)->SetBackgroundWidget(NULL);
}
}
void DesktopBackgroundController::SetDesktopBackgroundImage(
aura::RootWindow* root_window) {
GetRootWindowLayoutManager(root_window)->SetBackgroundLayer(NULL);
if (current_wallpaper_.get() &&
!current_wallpaper_->wallpaper_image.empty())
internal::CreateDesktopBackground(root_window);
}
void DesktopBackgroundController::UpdateDesktopBackgroundImageMode() {
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
for (Shell::RootWindowList::iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
SetDesktopBackgroundImage(*iter);
}
desktop_background_mode_ = BACKGROUND_IMAGE;
}
void DesktopBackgroundController::OnWallpaperLoadCompleted(
scoped_refptr<WallpaperOperation> wo) {
current_wallpaper_.reset(wo->ReleaseWallpaperData());
UpdateDesktopBackgroundImageMode();
DCHECK(wo.get() == wallpaper_op_.get());
wallpaper_op_ = NULL;
}
void DesktopBackgroundController::CreateEmptyWallpaper() {
current_wallpaper_.reset(NULL);
desktop_background_mode_ = BACKGROUND_IMAGE;
Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
for (Shell::RootWindowList::iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
internal::CreateDesktopBackground(*iter);
}
}
} // namespace ash