blob: 7019e41acfdd83729e59a0421d1ab188dbc71ed7 [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/ash_switches.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/display/display_info.h"
#include "ash/display/display_manager.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/command_line.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/synchronization/cancellation_flag.h"
#include "base/threading/worker_pool.h"
#include "components/wallpaper/wallpaper_resizer.h"
#include "content/public/browser/browser_thread.h"
#include "ui/aura/window.h"
#include "ui/aura/window_event_dispatcher.h"
#include "ui/compositor/layer.h"
#include "ui/gfx/codec/jpeg_codec.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/gfx/image/image_skia.h"
#include "ui/views/widget/widget.h"
using content::BrowserThread;
using wallpaper::WallpaperResizer;
using wallpaper::WallpaperLayout;
using wallpaper::WALLPAPER_LAYOUT_CENTER;
using wallpaper::WALLPAPER_LAYOUT_CENTER_CROPPED;
using wallpaper::WALLPAPER_LAYOUT_STRETCH;
using wallpaper::WALLPAPER_LAYOUT_TILE;
namespace ash {
namespace {
// How long to wait reloading the wallpaper after the max display has
// changed?
const int kWallpaperReloadDelayMs = 2000;
} // namespace
DesktopBackgroundController::DesktopBackgroundController()
: locked_(false),
desktop_background_mode_(BACKGROUND_NONE),
wallpaper_reload_delay_(kWallpaperReloadDelayMs) {
Shell::GetInstance()->display_controller()->AddObserver(this);
Shell::GetInstance()->AddShellObserver(this);
}
DesktopBackgroundController::~DesktopBackgroundController() {
Shell::GetInstance()->display_controller()->RemoveObserver(this);
Shell::GetInstance()->RemoveShellObserver(this);
}
gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const {
if (current_wallpaper_)
return current_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_)
return current_wallpaper_->layout();
return WALLPAPER_LAYOUT_CENTER_CROPPED;
}
bool DesktopBackgroundController::SetWallpaperImage(const gfx::ImageSkia& image,
WallpaperLayout layout) {
VLOG(1) << "SetWallpaper: image_id=" << WallpaperResizer::GetImageId(image)
<< " layout=" << layout;
if (WallpaperIsAlreadyLoaded(image, true /* compare_layouts */, layout)) {
VLOG(1) << "Wallpaper is already loaded";
return false;
}
current_wallpaper_.reset(
new WallpaperResizer(image, GetMaxDisplaySizeInNative(), layout,
BrowserThread::GetBlockingPool()));
current_wallpaper_->StartResize();
FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver,
observers_,
OnWallpaperDataChanged());
SetDesktopBackgroundImageMode();
return true;
}
void DesktopBackgroundController::CreateEmptyWallpaper() {
current_wallpaper_.reset(NULL);
SetDesktopBackgroundImageMode();
}
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::OnDisplayConfigurationChanged() {
gfx::Size max_display_size = GetMaxDisplaySizeInNative();
if (current_max_display_size_ != max_display_size) {
current_max_display_size_ = max_display_size;
if (desktop_background_mode_ == BACKGROUND_IMAGE &&
current_wallpaper_.get()) {
timer_.Stop();
timer_.Start(FROM_HERE,
base::TimeDelta::FromMilliseconds(wallpaper_reload_delay_),
this,
&DesktopBackgroundController::UpdateWallpaper);
}
}
}
void DesktopBackgroundController::OnRootWindowAdded(aura::Window* root_window) {
// The background hasn't been set yet.
if (desktop_background_mode_ == BACKGROUND_NONE)
return;
// Handle resolution change for "built-in" images.
gfx::Size max_display_size = GetMaxDisplaySizeInNative();
if (current_max_display_size_ != max_display_size) {
current_max_display_size_ = max_display_size;
if (desktop_background_mode_ == BACKGROUND_IMAGE &&
current_wallpaper_.get())
UpdateWallpaper();
}
InstallDesktopController(root_window);
}
// static
gfx::Size DesktopBackgroundController::GetMaxDisplaySizeInNative() {
int width = 0;
int height = 0;
std::vector<gfx::Display> displays = Shell::GetScreen()->GetAllDisplays();
DisplayManager* display_manager = Shell::GetInstance()->display_manager();
for (std::vector<gfx::Display>::iterator iter = displays.begin();
iter != displays.end(); ++iter) {
// Don't use size_in_pixel because we want to use the native pixel size.
gfx::Size size_in_pixel =
display_manager->GetDisplayInfo(iter->id()).bounds_in_native().size();
if (iter->rotation() == gfx::Display::ROTATE_90 ||
iter->rotation() == gfx::Display::ROTATE_270) {
size_in_pixel = gfx::Size(size_in_pixel.height(), size_in_pixel.width());
}
width = std::max(size_in_pixel.width(), width);
height = std::max(size_in_pixel.height(), height);
}
return gfx::Size(width, height);
}
bool DesktopBackgroundController::WallpaperIsAlreadyLoaded(
const gfx::ImageSkia& image,
bool compare_layouts,
WallpaperLayout layout) const {
if (!current_wallpaper_.get())
return false;
// Compare layouts only if necessary.
if (compare_layouts && layout != current_wallpaper_->layout())
return false;
return WallpaperResizer::GetImageId(image) ==
current_wallpaper_->original_image_id();
}
void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
desktop_background_mode_ = BACKGROUND_IMAGE;
InstallDesktopControllerForAllWindows();
}
void DesktopBackgroundController::InstallDesktopController(
aura::Window* root_window) {
DesktopBackgroundWidgetController* component = NULL;
int container_id = GetBackgroundContainerId(locked_);
switch (desktop_background_mode_) {
case BACKGROUND_IMAGE: {
views::Widget* widget =
CreateDesktopBackground(root_window, container_id);
component = new DesktopBackgroundWidgetController(widget);
break;
}
case BACKGROUND_NONE:
NOTREACHED();
return;
}
GetRootWindowController(root_window)->SetAnimatingWallpaperController(
new AnimatingDesktopController(component));
component->StartAnimating(GetRootWindowController(root_window));
}
void DesktopBackgroundController::InstallDesktopControllerForAllWindows() {
aura::Window::Windows root_windows = Shell::GetAllRootWindows();
for (aura::Window::Windows::iterator iter = root_windows.begin();
iter != root_windows.end(); ++iter) {
InstallDesktopController(*iter);
}
current_max_display_size_ = GetMaxDisplaySizeInNative();
}
bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container,
int dst_container) {
bool moved = false;
Shell::RootWindowControllerList controllers =
Shell::GetAllRootWindowControllers();
for (Shell::RootWindowControllerList::iterator iter = controllers.begin();
iter != controllers.end(); ++iter) {
RootWindowController* root_window_controller = *iter;
// In the steady state (no animation playing) the background widget
// controller exists in the RootWindowController.
DesktopBackgroundWidgetController* desktop_controller =
root_window_controller->wallpaper_controller();
if (desktop_controller) {
moved |=
desktop_controller->Reparent(root_window_controller->GetRootWindow(),
src_container,
dst_container);
}
// During desktop show animations the controller lives in
// AnimatingDesktopController owned by RootWindowController.
// 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_controller->animating_wallpaper_controller() ?
root_window_controller->animating_wallpaper_controller()->
GetController(false) :
NULL;
if (animating_controller) {
moved |= animating_controller->Reparent(
root_window_controller->GetRootWindow(),
src_container,
dst_container);
}
}
return moved;
}
int DesktopBackgroundController::GetBackgroundContainerId(bool locked) {
return locked ? kShellWindowId_LockScreenBackgroundContainer
: kShellWindowId_DesktopBackgroundContainer;
}
void DesktopBackgroundController::UpdateWallpaper() {
current_wallpaper_.reset(NULL);
ash::Shell::GetInstance()->user_wallpaper_delegate()->
UpdateWallpaper(true /* clear cache */);
}
} // namespace ash