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
35using content::BrowserThread;
36
37namespace ash {
38namespace {
39
40// How long to wait reloading the wallpaper after the max display has
41// changed?
42const int kWallpaperReloadDelayMs = 2000;
43
44}  // namespace
45
46DesktopBackgroundController::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
54DesktopBackgroundController::~DesktopBackgroundController() {
55  Shell::GetInstance()->display_controller()->RemoveObserver(this);
56  Shell::GetInstance()->RemoveShellObserver(this);
57}
58
59gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const {
60  if (current_wallpaper_)
61    return current_wallpaper_->image();
62  return gfx::ImageSkia();
63}
64
65void DesktopBackgroundController::AddObserver(
66    DesktopBackgroundControllerObserver* observer) {
67  observers_.AddObserver(observer);
68}
69
70void DesktopBackgroundController::RemoveObserver(
71    DesktopBackgroundControllerObserver* observer) {
72  observers_.RemoveObserver(observer);
73}
74
75WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
76  if (current_wallpaper_)
77    return current_wallpaper_->layout();
78  return WALLPAPER_LAYOUT_CENTER_CROPPED;
79}
80
81bool 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
102void DesktopBackgroundController::CreateEmptyWallpaper() {
103  current_wallpaper_.reset(NULL);
104  SetDesktopBackgroundImageMode();
105}
106
107bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
108  if (locked_)
109    return false;
110  locked_ = true;
111  return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
112                                   GetBackgroundContainerId(true));
113}
114
115bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
116  if (!locked_)
117    return false;
118  locked_ = false;
119  return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
120                                   GetBackgroundContainerId(false));
121}
122
123void 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
138void 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
156gfx::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
177bool 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
192void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
193  desktop_background_mode_ = BACKGROUND_IMAGE;
194  InstallDesktopControllerForAllWindows();
195}
196
197void 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
219void 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
228bool 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
266int DesktopBackgroundController::GetBackgroundContainerId(bool locked) {
267  return locked ? kShellWindowId_LockScreenBackgroundContainer
268                : kShellWindowId_DesktopBackgroundContainer;
269}
270
271void 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