Separate projection mode from rest of touch HUD
[chromium-blink-merge.git] / ash / desktop_background / desktop_background_controller.cc
bloba4f367ba1b07a4c8789c6f80ae9c6499d563568f
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.
5 #include "ash/desktop_background/desktop_background_controller.h"
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/root_window_controller.h"
14 #include "ash/shell.h"
15 #include "ash/shell_factory.h"
16 #include "ash/shell_window_ids.h"
17 #include "ash/wm/root_window_layout_manager.h"
18 #include "base/bind.h"
19 #include "base/command_line.h"
20 #include "base/file_util.h"
21 #include "base/logging.h"
22 #include "base/synchronization/cancellation_flag.h"
23 #include "base/threading/worker_pool.h"
24 #include "content/public/browser/browser_thread.h"
25 #include "grit/ash_wallpaper_resources.h"
26 #include "ui/aura/root_window.h"
27 #include "ui/aura/window.h"
28 #include "ui/compositor/layer.h"
29 #include "ui/gfx/codec/jpeg_codec.h"
30 #include "ui/gfx/image/image_skia.h"
31 #include "ui/gfx/rect.h"
32 #include "ui/views/widget/widget.h"
34 using ash::internal::DesktopBackgroundWidgetController;
35 using ash::internal::kAnimatingDesktopController;
36 using ash::internal::kDesktopController;
37 using content::BrowserThread;
39 namespace ash {
40 namespace {
42 const SkColor kTransparentColor = SkColorSetARGB(0x00, 0x00, 0x00, 0x00);
44 internal::RootWindowLayoutManager* GetRootWindowLayoutManager(
45 aura::RootWindow* root_window) {
46 return static_cast<internal::RootWindowLayoutManager*>(
47 root_window->layout_manager());
50 // Returns the maximum width and height of all root windows.
51 gfx::Size GetRootWindowsSize() {
52 int width = 0;
53 int height = 0;
54 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
55 for (Shell::RootWindowList::iterator iter = root_windows.begin();
56 iter != root_windows.end(); ++iter) {
57 gfx::Size root_window_size = (*iter)->GetHostSize();
58 if (root_window_size.width() > width)
59 width = root_window_size.width();
60 if (root_window_size.height() > height)
61 height = root_window_size.height();
63 return gfx::Size(width, height);
66 } // namespace
68 const int kSmallWallpaperMaxWidth = 1366;
69 const int kSmallWallpaperMaxHeight = 800;
70 const int kLargeWallpaperMaxWidth = 2560;
71 const int kLargeWallpaperMaxHeight = 1700;
72 const int kWallpaperThumbnailWidth = 108;
73 const int kWallpaperThumbnailHeight = 68;
75 // DesktopBackgroundController::WallpaperLoader wraps background wallpaper
76 // loading.
77 class DesktopBackgroundController::WallpaperLoader
78 : public base::RefCountedThreadSafe<
79 DesktopBackgroundController::WallpaperLoader> {
80 public:
81 // If set, |file_path| must be a trusted (i.e. read-only,
82 // non-user-controlled) file containing a JPEG image.
83 WallpaperLoader(const base::FilePath& file_path,
84 WallpaperLayout file_layout,
85 int resource_id,
86 WallpaperLayout resource_layout)
87 : file_path_(file_path),
88 file_layout_(file_layout),
89 resource_id_(resource_id),
90 resource_layout_(resource_layout) {
93 static void LoadOnWorkerPoolThread(scoped_refptr<WallpaperLoader> loader) {
94 DCHECK(!BrowserThread::CurrentlyOn(BrowserThread::UI));
95 loader->LoadWallpaper();
98 const base::FilePath& file_path() const { return file_path_; }
99 int resource_id() const { return resource_id_; }
101 void Cancel() {
102 cancel_flag_.Set();
105 WallpaperResizer* ReleaseWallpaperResizer() {
106 return wallpaper_resizer_.release();
109 private:
110 friend class base::RefCountedThreadSafe<
111 DesktopBackgroundController::WallpaperLoader>;
113 // Loads a JPEG image from |path|, a trusted file -- note that the image
114 // is not loaded in a sandboxed process. Returns an empty pointer on
115 // error.
116 static scoped_ptr<SkBitmap> LoadSkBitmapFromJPEGFile(
117 const base::FilePath& path) {
118 std::string data;
119 if (!file_util::ReadFileToString(path, &data)) {
120 LOG(ERROR) << "Unable to read data from " << path.value();
121 return scoped_ptr<SkBitmap>();
124 scoped_ptr<SkBitmap> bitmap(gfx::JPEGCodec::Decode(
125 reinterpret_cast<const unsigned char*>(data.data()), data.size()));
126 if (!bitmap)
127 LOG(ERROR) << "Unable to decode JPEG data from " << path.value();
128 return bitmap.Pass();
131 void LoadWallpaper() {
132 if (cancel_flag_.IsSet())
133 return;
135 if (!file_path_.empty())
136 file_bitmap_ = LoadSkBitmapFromJPEGFile(file_path_);
138 if (cancel_flag_.IsSet())
139 return;
141 if (file_bitmap_) {
142 gfx::ImageSkia image = gfx::ImageSkia::CreateFrom1xBitmap(*file_bitmap_);
143 wallpaper_resizer_.reset(new WallpaperResizer(
144 image, GetRootWindowsSize(), file_layout_));
145 } else {
146 wallpaper_resizer_.reset(new WallpaperResizer(
147 resource_id_, GetRootWindowsSize(), resource_layout_));
151 ~WallpaperLoader() {}
153 base::CancellationFlag cancel_flag_;
155 // Bitmap loaded from |file_path_|.
156 scoped_ptr<SkBitmap> file_bitmap_;
158 scoped_ptr<WallpaperResizer> wallpaper_resizer_;
160 // Path to a trusted JPEG file.
161 base::FilePath file_path_;
163 // Layout to be used when displaying the image from |file_path_|.
164 WallpaperLayout file_layout_;
166 // ID of an image resource to use if |file_path_| is empty or unloadable.
167 int resource_id_;
169 // Layout to be used when displaying |resource_id_|.
170 WallpaperLayout resource_layout_;
172 DISALLOW_COPY_AND_ASSIGN(WallpaperLoader);
175 DesktopBackgroundController::DesktopBackgroundController()
176 : command_line_for_testing_(NULL),
177 locked_(false),
178 desktop_background_mode_(BACKGROUND_NONE),
179 background_color_(kTransparentColor),
180 current_default_wallpaper_resource_id_(-1),
181 weak_ptr_factory_(this) {
184 DesktopBackgroundController::~DesktopBackgroundController() {
185 CancelPendingWallpaperOperation();
188 gfx::ImageSkia DesktopBackgroundController::GetWallpaper() const {
189 if (current_wallpaper_)
190 return current_wallpaper_->wallpaper_image();
191 return gfx::ImageSkia();
194 void DesktopBackgroundController::AddObserver(
195 DesktopBackgroundControllerObserver* observer) {
196 observers_.AddObserver(observer);
199 void DesktopBackgroundController::RemoveObserver(
200 DesktopBackgroundControllerObserver* observer) {
201 observers_.RemoveObserver(observer);
204 WallpaperLayout DesktopBackgroundController::GetWallpaperLayout() const {
205 if (current_wallpaper_)
206 return current_wallpaper_->layout();
207 return WALLPAPER_LAYOUT_CENTER_CROPPED;
210 void DesktopBackgroundController::OnRootWindowAdded(
211 aura::RootWindow* root_window) {
212 // The background hasn't been set yet.
213 if (desktop_background_mode_ == BACKGROUND_NONE)
214 return;
216 // Handle resolution change for "built-in" images.
217 if (BACKGROUND_IMAGE == desktop_background_mode_ &&
218 current_wallpaper_.get()) {
219 gfx::Size root_window_size = root_window->GetHostSize();
220 int width = current_wallpaper_->wallpaper_image().width();
221 int height = current_wallpaper_->wallpaper_image().height();
222 // Reloads wallpaper if current wallpaper is smaller than the new added root
223 // window.
224 if (width < root_window_size.width() ||
225 height < root_window_size.height()) {
226 current_wallpaper_.reset(NULL);
227 current_default_wallpaper_path_ = base::FilePath();
228 current_default_wallpaper_resource_id_ = -1;
229 ash::Shell::GetInstance()->user_wallpaper_delegate()->
230 UpdateWallpaper();
234 InstallDesktopController(root_window);
237 bool DesktopBackgroundController::SetDefaultWallpaper(bool is_guest) {
238 const bool use_large =
239 GetAppropriateResolution() == WALLPAPER_RESOLUTION_LARGE;
241 base::FilePath file_path;
242 WallpaperLayout file_layout = use_large ? WALLPAPER_LAYOUT_CENTER_CROPPED :
243 WALLPAPER_LAYOUT_CENTER;
244 int resource_id = -1;
245 WallpaperLayout resource_layout = WALLPAPER_LAYOUT_TILE;
247 #if defined(GOOGLE_CHROME_BUILD)
248 if (use_large) {
249 resource_id = is_guest ? IDR_AURA_WALLPAPERS_2_LANDSCAPE7_LARGE :
250 IDR_AURA_WALLPAPERS_2_LANDSCAPE8_LARGE;
251 resource_layout = WALLPAPER_LAYOUT_CENTER_CROPPED;
252 } else {
253 resource_id = is_guest ? IDR_AURA_WALLPAPERS_2_LANDSCAPE7_SMALL :
254 IDR_AURA_WALLPAPERS_2_LANDSCAPE8_SMALL;
255 resource_layout = WALLPAPER_LAYOUT_CENTER;
257 #else
258 resource_id = use_large ? IDR_AURA_WALLPAPERS_5_GRADIENT5_LARGE :
259 IDR_AURA_WALLPAPERS_5_GRADIENT5_SMALL;
260 resource_layout = WALLPAPER_LAYOUT_TILE;
261 #endif
263 const char* switch_name = is_guest ?
264 (use_large ? switches::kAshDefaultGuestWallpaperLarge :
265 switches::kAshDefaultGuestWallpaperSmall) :
266 (use_large ? switches::kAshDefaultWallpaperLarge :
267 switches::kAshDefaultWallpaperSmall);
268 CommandLine* command_line = command_line_for_testing_ ?
269 command_line_for_testing_ : CommandLine::ForCurrentProcess();
270 file_path = command_line->GetSwitchValuePath(switch_name);
272 if (DefaultWallpaperIsAlreadyLoadingOrLoaded(file_path, resource_id))
273 return false;
275 CancelPendingWallpaperOperation();
276 wallpaper_loader_ = new WallpaperLoader(
277 file_path, file_layout, resource_id, resource_layout);
278 base::WorkerPool::PostTaskAndReply(
279 FROM_HERE,
280 base::Bind(&WallpaperLoader::LoadOnWorkerPoolThread, wallpaper_loader_),
281 base::Bind(&DesktopBackgroundController::OnDefaultWallpaperLoadCompleted,
282 weak_ptr_factory_.GetWeakPtr(),
283 wallpaper_loader_),
284 true /* task_is_slow */);
285 return true;
288 void DesktopBackgroundController::SetCustomWallpaper(
289 const gfx::ImageSkia& image,
290 WallpaperLayout layout) {
291 CancelPendingWallpaperOperation();
292 if (CustomWallpaperIsAlreadyLoaded(image))
293 return;
295 current_wallpaper_.reset(new WallpaperResizer(
296 image, GetRootWindowsSize(), layout));
297 current_wallpaper_->StartResize();
299 current_default_wallpaper_path_ = base::FilePath();
300 current_default_wallpaper_resource_id_ = -1;
302 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_,
303 OnWallpaperDataChanged());
304 SetDesktopBackgroundImageMode();
307 void DesktopBackgroundController::CancelPendingWallpaperOperation() {
308 // Set canceled flag of previous request to skip unneeded loading.
309 if (wallpaper_loader_.get())
310 wallpaper_loader_->Cancel();
312 // Cancel reply callback for previous request.
313 weak_ptr_factory_.InvalidateWeakPtrs();
316 void DesktopBackgroundController::SetDesktopBackgroundSolidColorMode(
317 SkColor color) {
318 background_color_ = color;
319 desktop_background_mode_ = BACKGROUND_SOLID_COLOR;
321 InstallDesktopControllerForAllWindows();
324 void DesktopBackgroundController::CreateEmptyWallpaper() {
325 current_wallpaper_.reset(NULL);
326 SetDesktopBackgroundImageMode();
329 WallpaperResolution DesktopBackgroundController::GetAppropriateResolution() {
330 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
331 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
332 for (Shell::RootWindowList::iterator iter = root_windows.begin();
333 iter != root_windows.end(); ++iter) {
334 // Compare to host size as constants are defined in terms of
335 // physical pixel size.
336 // TODO(oshima): This may not be ideal for fractional scaling
337 // scenario. Revisit and fix if necessary.
338 gfx::Size host_window_size = (*iter)->GetHostSize();
339 if (host_window_size.width() > kSmallWallpaperMaxWidth ||
340 host_window_size.height() > kSmallWallpaperMaxHeight)
341 return WALLPAPER_RESOLUTION_LARGE;
343 return WALLPAPER_RESOLUTION_SMALL;
346 bool DesktopBackgroundController::MoveDesktopToLockedContainer() {
347 if (locked_)
348 return false;
349 locked_ = true;
350 return ReparentBackgroundWidgets(GetBackgroundContainerId(false),
351 GetBackgroundContainerId(true));
354 bool DesktopBackgroundController::MoveDesktopToUnlockedContainer() {
355 if (!locked_)
356 return false;
357 locked_ = false;
358 return ReparentBackgroundWidgets(GetBackgroundContainerId(true),
359 GetBackgroundContainerId(false));
362 void DesktopBackgroundController::OnWindowDestroying(aura::Window* window) {
363 window->SetProperty(kDesktopController,
364 static_cast<internal::DesktopBackgroundWidgetController*>(NULL));
365 window->SetProperty(kAnimatingDesktopController,
366 static_cast<internal::AnimatingDesktopController*>(NULL));
369 bool DesktopBackgroundController::DefaultWallpaperIsAlreadyLoadingOrLoaded(
370 const base::FilePath& image_file, int image_resource_id) const {
371 return (wallpaper_loader_ &&
372 wallpaper_loader_->file_path() == image_file &&
373 wallpaper_loader_->resource_id() == image_resource_id) ||
374 (current_wallpaper_.get() &&
375 current_default_wallpaper_path_ == image_file &&
376 current_default_wallpaper_resource_id_ == image_resource_id);
379 bool DesktopBackgroundController::CustomWallpaperIsAlreadyLoaded(
380 const gfx::ImageSkia& image) const {
381 return current_wallpaper_.get() &&
382 current_wallpaper_->wallpaper_image().BackedBySameObjectAs(image);
385 void DesktopBackgroundController::SetDesktopBackgroundImageMode() {
386 desktop_background_mode_ = BACKGROUND_IMAGE;
387 InstallDesktopControllerForAllWindows();
390 void DesktopBackgroundController::OnDefaultWallpaperLoadCompleted(
391 scoped_refptr<WallpaperLoader> loader) {
392 current_wallpaper_.reset(loader->ReleaseWallpaperResizer());
393 current_wallpaper_->StartResize();
394 current_default_wallpaper_path_ = loader->file_path();
395 current_default_wallpaper_resource_id_ = loader->resource_id();
396 FOR_EACH_OBSERVER(DesktopBackgroundControllerObserver, observers_,
397 OnWallpaperDataChanged());
399 SetDesktopBackgroundImageMode();
401 DCHECK(loader.get() == wallpaper_loader_.get());
402 wallpaper_loader_ = NULL;
405 void DesktopBackgroundController::NotifyAnimationFinished() {
406 Shell* shell = Shell::GetInstance();
407 shell->GetPrimaryRootWindowController()->HandleDesktopBackgroundVisible();
408 shell->user_wallpaper_delegate()->OnWallpaperAnimationFinished();
411 ui::Layer* DesktopBackgroundController::SetColorLayerForContainer(
412 SkColor color,
413 aura::RootWindow* root_window,
414 int container_id) {
415 ui::Layer* background_layer = new ui::Layer(ui::LAYER_SOLID_COLOR);
416 background_layer->SetColor(color);
418 Shell::GetContainer(root_window,container_id)->
419 layer()->Add(background_layer);
421 base::MessageLoop::current()->PostTask(
422 FROM_HERE,
423 base::Bind(&DesktopBackgroundController::NotifyAnimationFinished,
424 weak_ptr_factory_.GetWeakPtr()));
426 return background_layer;
429 void DesktopBackgroundController::InstallDesktopController(
430 aura::RootWindow* root_window) {
431 internal::DesktopBackgroundWidgetController* component = NULL;
432 int container_id = GetBackgroundContainerId(locked_);
434 switch (desktop_background_mode_) {
435 case BACKGROUND_IMAGE: {
436 views::Widget* widget = internal::CreateDesktopBackground(root_window,
437 container_id);
438 component = new internal::DesktopBackgroundWidgetController(widget);
439 break;
441 case BACKGROUND_SOLID_COLOR: {
442 ui::Layer* layer = SetColorLayerForContainer(background_color_,
443 root_window,
444 container_id);
445 component = new internal::DesktopBackgroundWidgetController(layer);
446 break;
448 case BACKGROUND_NONE:
449 NOTREACHED();
450 return;
452 // Ensure we're only observing the root window once. Don't rely on a window
453 // property check as those can be cleared by tests resetting the background.
454 if (!root_window->HasObserver(this))
455 root_window->AddObserver(this);
457 internal::AnimatingDesktopController* animating_controller =
458 root_window->GetProperty(kAnimatingDesktopController);
459 if (animating_controller)
460 animating_controller->StopAnimating();
461 root_window->SetProperty(kAnimatingDesktopController,
462 new internal::AnimatingDesktopController(component));
465 void DesktopBackgroundController::InstallDesktopControllerForAllWindows() {
466 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
467 for (Shell::RootWindowList::iterator iter = root_windows.begin();
468 iter != root_windows.end(); ++iter) {
469 InstallDesktopController(*iter);
473 bool DesktopBackgroundController::ReparentBackgroundWidgets(int src_container,
474 int dst_container) {
475 bool moved = false;
476 Shell::RootWindowList root_windows = Shell::GetAllRootWindows();
477 for (Shell::RootWindowList::iterator iter = root_windows.begin();
478 iter != root_windows.end(); ++iter) {
479 aura::RootWindow* root_window = *iter;
480 // In the steady state (no animation playing) the background widget
481 // controller exists in the kDesktopController property.
482 DesktopBackgroundWidgetController* desktop_controller = root_window->
483 GetProperty(kDesktopController);
484 if (desktop_controller) {
485 moved |= desktop_controller->Reparent(root_window,
486 src_container,
487 dst_container);
489 // During desktop show animations the controller lives in
490 // kAnimatingDesktopController.
491 // NOTE: If a wallpaper load happens during a desktop show animation there
492 // can temporarily be two desktop background widgets. We must reparent
493 // both of them - one above and one here.
494 DesktopBackgroundWidgetController* animating_controller =
495 root_window->GetProperty(kAnimatingDesktopController) ?
496 root_window->GetProperty(kAnimatingDesktopController)->
497 GetController(false) :
498 NULL;
499 if (animating_controller) {
500 moved |= animating_controller->Reparent(root_window,
501 src_container,
502 dst_container);
505 return moved;
508 int DesktopBackgroundController::GetBackgroundContainerId(bool locked) {
509 return locked ? internal::kShellWindowId_LockScreenBackgroundContainer :
510 internal::kShellWindowId_DesktopBackgroundContainer;
513 } // namespace ash