| // 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 |