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. 4 5 #include "ash/desktop_background/desktop_background_controller.h" 6 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/files/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 "ui/aura/window.h" 28 #include "ui/aura/window_event_dispatcher.h" 29 #include "ui/compositor/layer.h" 30 #include "ui/gfx/codec/jpeg_codec.h" 31 #include "ui/gfx/image/image_skia.h" 32 #include "ui/gfx/rect.h" 33 #include "ui/views/widget/widget.h" 34 35 using content::BrowserThread; 36 37 namespace ash { 38 namespace { 39 40 // How long to wait reloading the wallpaper after the max display has 41 // changed? 42 const int kWallpaperReloadDelayMs = 2000; 43 44 } // namespace 45 46 DesktopBackgroundController::DesktopBackgroundController() 47 : locked_(false), 48 desktop_background_mode_(BACKGROUND_NONE), 49 wallpaper_reload_delay_(kWallpaperReloadDelayMs) { 50 Shell::GetInstance()->display_controller()->AddObserver(this); 51 Shell::GetInstance()->AddShellObserver(this); 52 } 53 54 DesktopBackgroundController::~DesktopBackgroundController() { 55 Shell::GetInstance()->display_controller()->RemoveObserver(this); 56 Shell::GetInstance()->RemoveShellObserver(this); 57 } 58 59 gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const { 60 if (current_wallpaper_) 61 return current_wallpaper_->image(); 62 return gfx::ImageSkia(); 63 } 64 65 void DesktopBackgroundController::AddObserver( 66 DesktopBackgroundControllerObserver* observer) { 67 observers_.AddObserver(observer); 68 } 69 70 void DesktopBackgroundController::RemoveObserver( 71 DesktopBackgroundControllerObserver* observer) { 72 observers_.RemoveObserver(observer); 73 } 74 75 WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const { 76 if (current_wallpaper_) 77 return current_wallpaper_->layout(); 78 return WALLPAPER_LAYOUT_CENTER_CROPPED; 79 } 80 81 bool DesktopBackgroundController::SetWallpaperImage(const gfx::ImageSkia& image, 82 WallpaperLayout layout) { 83 VLOG(1) << "SetWallpaper: image_id=" << WallpaperResizer::GetImageId(image) 84 << " layout=" << layout; 85 86 if (WallpaperIsAlreadyLoaded(image, true /* compare_layouts */, layout)) { 87 VLOG(1) << "Wallpaper is already loaded"; 88 return false; 89 } 90 91 current_wallpaper_.reset( 92 new WallpaperResizer(image, GetMaxDisplaySizeInNative(), layout)); 93 current_wallpaper_->StartResize(); 94 95 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, 96 observers_, 97 OnWallpaperDataChanged()); 98 SetDesktopBackgroundImageMode(); 99 return true; 100 } 101 102 void DesktopBackgroundController::CreateEmptyWallpaper() { 103 current_wallpaper_.reset(NULL); 104 SetDesktopBackgroundImageMode(); 105 } 106 107 bool DesktopBackgroundController::MoveDesktopToLockedContainer() { 108 if (locked_) 109 return false; 110 locked_ = true; 111 return ReparentBackgroundWidgets(GetBackgroundContainerId(false), 112 GetBackgroundContainerId(true)); 113 } 114 115 bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() { 116 if (!locked_) 117 return false; 118 locked_ = false; 119 return ReparentBackgroundWidgets(GetBackgroundContainerId(true), 120 GetBackgroundContainerId(false)); 121 } 122 123 void DesktopBackgroundController::OnDisplayConfigurationChanged() { 124 gfx::Size max_display_size = GetMaxDisplaySizeInNative(); 125 if (current_max_display_size_ != max_display_size) { 126 current_max_display_size_ = max_display_size; 127 if (desktop_background_mode_ == BACKGROUND_IMAGE && 128 current_wallpaper_.get()) { 129 timer_.Stop(); 130 timer_.Start(FROM_HERE, 131 base::TimeDelta::FromMilliseconds(wallpaper_reload_delay_), 132 this, 133 &DesktopBackgroundController::UpdateWallpaper); 134 } 135 } 136 } 137 138 void DesktopBackgroundController::OnRootWindowAdded(aura::Window* root_window) { 139 // The background hasn't been set yet. 140 if (desktop_background_mode_ == BACKGROUND_NONE) 141 return; 142 143 // Handle resolution change for "built-in" images. 144 gfx::Size max_display_size = GetMaxDisplaySizeInNative(); 145 if (current_max_display_size_ != max_display_size) { 146 current_max_display_size_ = max_display_size; 147 if (desktop_background_mode_ == BACKGROUND_IMAGE && 148 current_wallpaper_.get()) 149 UpdateWallpaper(); 150 } 151 152 InstallDesktopController(root_window); 153 } 154 155 // static 156 gfx::Size DesktopBackgroundController::GetMaxDisplaySizeInNative() { 157 int width = 0; 158 int height = 0; 159 std::vector<gfx::Display> displays = Shell::GetScreen()->GetAllDisplays(); 160 DisplayManager* display_manager = Shell::GetInstance()->display_manager(); 161 162 for (std::vector<gfx::Display>::iterator iter = displays.begin(); 163 iter != displays.end(); ++iter) { 164 // Don't use size_in_pixel because we want to use the native pixel size. 165 gfx::Size size_in_pixel = 166 display_manager->GetDisplayInfo(iter->id()).bounds_in_native().size(); 167 if (iter->rotation() == gfx::Display::ROTATE_90 || 168 iter->rotation() == gfx::Display::ROTATE_270) { 169 size_in_pixel = gfx::Size(size_in_pixel.height(), size_in_pixel.width()); 170 } 171 width = std::max(size_in_pixel.width(), width); 172 height = std::max(size_in_pixel.height(), height); 173 } 174 return gfx::Size(width, height); 175 } 176 177 bool DesktopBackgroundController::WallpaperIsAlreadyLoaded( 178 const gfx::ImageSkia& image, 179 bool compare_layouts, 180 WallpaperLayout layout) const { 181 if (!current_wallpaper_.get()) 182 return false; 183 184 // Compare layouts only if necessary. 185 if (compare_layouts && layout != current_wallpaper_->layout()) 186 return false; 187 188 return WallpaperResizer::GetImageId(image) == 189 current_wallpaper_->original_image_id(); 190 } 191 192 void DesktopBackgroundController::SetDesktopBackgroundImageMode() { 193 desktop_background_mode_ = BACKGROUND_IMAGE; 194 InstallDesktopControllerForAllWindows(); 195 } 196 197 void DesktopBackgroundController::InstallDesktopController( 198 aura::Window* root_window) { 199 DesktopBackgroundWidgetController* component = NULL; 200 int container_id = GetBackgroundContainerId(locked_); 201 202 switch (desktop_background_mode_) { 203 case BACKGROUND_IMAGE: { 204 views::Widget* widget = 205 CreateDesktopBackground(root_window, container_id); 206 component = new DesktopBackgroundWidgetController(widget); 207 break; 208 } 209 case BACKGROUND_NONE: 210 NOTREACHED(); 211 return; 212 } 213 GetRootWindowController(root_window)->SetAnimatingWallpaperController( 214 new AnimatingDesktopController(component)); 215 216 component->StartAnimating(GetRootWindowController(root_window)); 217 } 218 219 void DesktopBackgroundController::InstallDesktopControllerForAllWindows() { 220 aura::Window::Windows root_windows = Shell::GetAllRootWindows(); 221 for (aura::Window::Windows::iterator iter = root_windows.begin(); 222 iter != root_windows.end(); ++iter) { 223 InstallDesktopController(*iter); 224 } 225 current_max_display_size_ = GetMaxDisplaySizeInNative(); 226 } 227 228 bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container, 229 int dst_container) { 230 bool moved = false; 231 Shell::RootWindowControllerList controllers = 232 Shell::GetAllRootWindowControllers(); 233 for (Shell::RootWindowControllerList::iterator iter = controllers.begin(); 234 iter != controllers.end(); ++iter) { 235 RootWindowController* root_window_controller = *iter; 236 // In the steady state (no animation playing) the background widget 237 // controller exists in the RootWindowController. 238 DesktopBackgroundWidgetController* desktop_controller = 239 root_window_controller->wallpaper_controller(); 240 if (desktop_controller) { 241 moved |= 242 desktop_controller->Reparent(root_window_controller->GetRootWindow(), 243 src_container, 244 dst_container); 245 } 246 // During desktop show animations the controller lives in 247 // AnimatingDesktopController owned by RootWindowController. 248 // NOTE: If a wallpaper load happens during a desktop show animation there 249 // can temporarily be two desktop background widgets. We must reparent 250 // both of them - one above and one here. 251 DesktopBackgroundWidgetController* animating_controller = 252 root_window_controller->animating_wallpaper_controller() ? 253 root_window_controller->animating_wallpaper_controller()-> 254 GetController(false) : 255 NULL; 256 if (animating_controller) { 257 moved |= animating_controller->Reparent( 258 root_window_controller->GetRootWindow(), 259 src_container, 260 dst_container); 261 } 262 } 263 return moved; 264 } 265 266 int DesktopBackgroundController::GetBackgroundContainerId(bool locked) { 267 return locked ? kShellWindowId_LockScreenBackgroundContainer 268 : kShellWindowId_DesktopBackgroundContainer; 269 } 270 271 void DesktopBackgroundController::UpdateWallpaper() { 272 current_wallpaper_.reset(NULL); 273 ash::Shell::GetInstance()->user_wallpaper_delegate()-> 274 UpdateWallpaper(true /* clear cache */); 275 } 276 277 } // namespace ash 278