[content shell] implement testRunner.overridePreference
[chromium-blink-merge.git] / ash / display / display_controller.cc
blob2fce25a9bd1209297f5301971c8d1bc838d2af19
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/display/display_controller.h"
7 #include <algorithm>
9 #include "ash/ash_switches.h"
10 #include "ash/display/display_manager.h"
11 #include "ash/root_window_controller.h"
12 #include "ash/shell.h"
13 #include "ash/wm/coordinate_conversion.h"
14 #include "ash/wm/property_util.h"
15 #include "ash/wm/window_util.h"
16 #include "base/command_line.h"
17 #include "base/json/json_value_converter.h"
18 #include "base/string_piece.h"
19 #include "base/stringprintf.h"
20 #include "base/values.h"
21 #include "ui/aura/client/screen_position_client.h"
22 #include "ui/aura/env.h"
23 #include "ui/aura/root_window.h"
24 #include "ui/aura/window.h"
25 #include "ui/compositor/dip_util.h"
26 #include "ui/gfx/display.h"
27 #include "ui/gfx/screen.h"
29 #if defined(OS_CHROMEOS)
30 #include "base/chromeos/chromeos_version.h"
31 #endif
33 namespace ash {
34 namespace {
36 // Primary display stored in global object as it can be
37 // accessed after Shell is deleted. A separate display instance is created
38 // during the shutdown instead of always keeping two display instances
39 // (one here and another one in display_manager) in sync, which is error prone.
40 int64 primary_display_id = gfx::Display::kInvalidDisplayID;
41 gfx::Display* primary_display_for_shutdown = NULL;
42 // Keeps the number of displays during the shutdown after
43 // ash::Shell:: is deleted.
44 int num_displays_for_shutdown = -1;
46 // The maximum value for 'offset' in DisplayLayout in case of outliers. Need
47 // to change this value in case to support even larger displays.
48 const int kMaxValidOffset = 10000;
50 // The number of pixels to overlap between the primary and secondary displays,
51 // in case that the offset value is too large.
52 const int kMinimumOverlapForInvalidOffset = 100;
54 bool GetPositionFromString(const base::StringPiece& position,
55 DisplayLayout::Position* field) {
56 if (position == "top") {
57 *field = DisplayLayout::TOP;
58 return true;
59 } else if (position == "bottom") {
60 *field = DisplayLayout::BOTTOM;
61 return true;
62 } else if (position == "right") {
63 *field = DisplayLayout::RIGHT;
64 return true;
65 } else if (position == "left") {
66 *field = DisplayLayout::LEFT;
67 return true;
69 LOG(ERROR) << "Invalid position value: " << position;
70 return false;
73 std::string GetStringFromPosition(DisplayLayout::Position position) {
74 switch (position) {
75 case DisplayLayout::TOP:
76 return std::string("top");
77 case DisplayLayout::BOTTOM:
78 return std::string("bottom");
79 case DisplayLayout::RIGHT:
80 return std::string("right");
81 case DisplayLayout::LEFT:
82 return std::string("left");
84 return std::string("unknown");
87 internal::DisplayManager* GetDisplayManager() {
88 return Shell::GetInstance()->display_manager();
91 } // namespace
93 DisplayLayout::DisplayLayout()
94 : position(RIGHT),
95 offset(0) {}
97 DisplayLayout::DisplayLayout(DisplayLayout::Position position, int offset)
98 : position(position),
99 offset(offset) {
100 DCHECK_LE(TOP, position);
101 DCHECK_GE(LEFT, position);
103 // Set the default value to |position| in case position is invalid. DCHECKs
104 // above doesn't stop in Release builds.
105 if (TOP > position || LEFT < position)
106 this->position = RIGHT;
108 DCHECK_GE(kMaxValidOffset, abs(offset));
111 DisplayLayout DisplayLayout::Invert() const {
112 Position inverted_position = RIGHT;
113 switch (position) {
114 case TOP:
115 inverted_position = BOTTOM;
116 break;
117 case BOTTOM:
118 inverted_position = TOP;
119 break;
120 case RIGHT:
121 inverted_position = LEFT;
122 break;
123 case LEFT:
124 inverted_position = RIGHT;
125 break;
127 return DisplayLayout(inverted_position, -offset);
130 // static
131 bool DisplayLayout::ConvertFromValue(const base::Value& value,
132 DisplayLayout* layout) {
133 base::JSONValueConverter<DisplayLayout> converter;
134 return converter.Convert(value, layout);
137 // static
138 bool DisplayLayout::ConvertToValue(const DisplayLayout& layout,
139 base::Value* value) {
140 base::DictionaryValue* dict_value = NULL;
141 if (!value->GetAsDictionary(&dict_value) || dict_value == NULL)
142 return false;
144 const std::string position_str = GetStringFromPosition(layout.position);
145 dict_value->SetString("position", position_str);
146 dict_value->SetInteger("offset", layout.offset);
147 return true;
150 std::string DisplayLayout::ToString() const {
151 const std::string position_str = GetStringFromPosition(position);
152 return StringPrintf("%s, %d", position_str.c_str(), offset);
155 // static
156 void DisplayLayout::RegisterJSONConverter(
157 base::JSONValueConverter<DisplayLayout>* converter) {
158 converter->RegisterCustomField<Position>(
159 "position", &DisplayLayout::position, &GetPositionFromString);
160 converter->RegisterIntField("offset", &DisplayLayout::offset);
163 DisplayController::DisplayController()
164 : desired_primary_display_id_(gfx::Display::kInvalidDisplayID) {
165 // Reset primary display to make sure that tests don't use
166 // stale display info from previous tests.
167 primary_display_id = gfx::Display::kInvalidDisplayID;
168 delete primary_display_for_shutdown;
169 primary_display_for_shutdown = NULL;
170 num_displays_for_shutdown = -1;
172 Shell::GetScreen()->AddObserver(this);
175 DisplayController::~DisplayController() {
176 DCHECK(!primary_display_for_shutdown);
177 primary_display_for_shutdown = new gfx::Display(
178 GetDisplayManager()->GetDisplayForId(primary_display_id));
179 num_displays_for_shutdown = GetDisplayManager()->GetNumDisplays();
181 Shell::GetScreen()->RemoveObserver(this);
182 // Delete all root window controllers, which deletes root window
183 // from the last so that the primary root window gets deleted last.
184 for (std::map<int64, aura::RootWindow*>::const_reverse_iterator it =
185 root_windows_.rbegin(); it != root_windows_.rend(); ++it) {
186 internal::RootWindowController* controller =
187 GetRootWindowController(it->second);
188 DCHECK(controller);
189 delete controller;
192 // static
193 const gfx::Display& DisplayController::GetPrimaryDisplay() {
194 DCHECK_NE(primary_display_id, gfx::Display::kInvalidDisplayID);
195 if (primary_display_for_shutdown)
196 return *primary_display_for_shutdown;
197 return GetDisplayManager()->GetDisplayForId(primary_display_id);
200 // static
201 int DisplayController::GetNumDisplays() {
202 if (num_displays_for_shutdown >= 0)
203 return num_displays_for_shutdown;
204 return GetDisplayManager()->GetNumDisplays();
207 // static
208 bool DisplayController::HasPrimaryDisplay() {
209 return primary_display_id != gfx::Display::kInvalidDisplayID;
212 void DisplayController::InitPrimaryDisplay() {
213 const gfx::Display* primary_candidate = GetDisplayManager()->GetDisplayAt(0);
214 #if defined(OS_CHROMEOS)
215 if (base::chromeos::IsRunningOnChromeOS()) {
216 internal::DisplayManager* display_manager = GetDisplayManager();
217 // On ChromeOS device, root windows are stacked vertically, and
218 // default primary is the one on top.
219 int count = display_manager->GetNumDisplays();
220 int y = primary_candidate->bounds_in_pixel().y();
221 for (int i = 1; i < count; ++i) {
222 const gfx::Display* display = display_manager->GetDisplayAt(i);
223 if (display_manager->IsInternalDisplayId(display->id())) {
224 primary_candidate = display;
225 break;
226 } else if (display->bounds_in_pixel().y() < y) {
227 primary_candidate = display;
228 y = display->bounds_in_pixel().y();
232 #endif
233 primary_display_id = primary_candidate->id();
234 aura::RootWindow* root = AddRootWindowForDisplay(*primary_candidate);
235 root->SetHostBounds(primary_candidate->bounds_in_pixel());
236 UpdateDisplayBoundsForLayout();
239 void DisplayController::InitSecondaryDisplays() {
240 internal::DisplayManager* display_manager = GetDisplayManager();
241 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
242 const gfx::Display* display = display_manager->GetDisplayAt(i);
243 if (primary_display_id != display->id()) {
244 aura::RootWindow* root = AddRootWindowForDisplay(*display);
245 Shell::GetInstance()->InitRootWindowForSecondaryDisplay(root);
248 CommandLine* command_line = CommandLine::ForCurrentProcess();
249 if (command_line->HasSwitch(switches::kAshSecondaryDisplayLayout)) {
250 std::string value = command_line->GetSwitchValueASCII(
251 switches::kAshSecondaryDisplayLayout);
252 char layout;
253 int offset;
254 if (sscanf(value.c_str(), "%c,%d", &layout, &offset) == 2) {
255 if (layout == 't')
256 default_display_layout_.position = DisplayLayout::TOP;
257 else if (layout == 'b')
258 default_display_layout_.position = DisplayLayout::BOTTOM;
259 else if (layout == 'r')
260 default_display_layout_.position = DisplayLayout::RIGHT;
261 else if (layout == 'l')
262 default_display_layout_.position = DisplayLayout::LEFT;
263 default_display_layout_.offset = offset;
266 UpdateDisplayBoundsForLayout();
269 void DisplayController::AddObserver(Observer* observer) {
270 observers_.AddObserver(observer);
273 void DisplayController::RemoveObserver(Observer* observer) {
274 observers_.RemoveObserver(observer);
277 aura::RootWindow* DisplayController::GetPrimaryRootWindow() {
278 DCHECK(!root_windows_.empty());
279 return root_windows_[primary_display_id];
282 aura::RootWindow* DisplayController::GetRootWindowForDisplayId(int64 id) {
283 return root_windows_[id];
286 void DisplayController::CloseChildWindows() {
287 for (std::map<int64, aura::RootWindow*>::const_iterator it =
288 root_windows_.begin(); it != root_windows_.end(); ++it) {
289 aura::RootWindow* root_window = it->second;
290 internal::RootWindowController* controller =
291 GetRootWindowController(root_window);
292 if (controller) {
293 controller->CloseChildWindows();
294 } else {
295 while (!root_window->children().empty()) {
296 aura::Window* child = root_window->children()[0];
297 delete child;
303 std::vector<aura::RootWindow*> DisplayController::GetAllRootWindows() {
304 std::vector<aura::RootWindow*> windows;
305 for (std::map<int64, aura::RootWindow*>::const_iterator it =
306 root_windows_.begin(); it != root_windows_.end(); ++it) {
307 DCHECK(it->second);
308 if (GetRootWindowController(it->second))
309 windows.push_back(it->second);
311 return windows;
314 gfx::Insets DisplayController::GetOverscanInsets(int64 display_id) const {
315 return GetDisplayManager()->GetOverscanInsets(display_id);
318 void DisplayController::SetOverscanInsets(int64 display_id,
319 const gfx::Insets& insets_in_dip) {
320 GetDisplayManager()->SetOverscanInsets(display_id, insets_in_dip);
323 std::vector<internal::RootWindowController*>
324 DisplayController::GetAllRootWindowControllers() {
325 std::vector<internal::RootWindowController*> controllers;
326 for (std::map<int64, aura::RootWindow*>::const_iterator it =
327 root_windows_.begin(); it != root_windows_.end(); ++it) {
328 internal::RootWindowController* controller =
329 GetRootWindowController(it->second);
330 if (controller)
331 controllers.push_back(controller);
333 return controllers;
336 void DisplayController::SetDefaultDisplayLayout(const DisplayLayout& layout) {
337 CommandLine* command_line = CommandLine::ForCurrentProcess();
338 if (!command_line->HasSwitch(switches::kAshSecondaryDisplayLayout) &&
339 (default_display_layout_.position != layout.position ||
340 default_display_layout_.offset != layout.offset)) {
341 default_display_layout_ = layout;
342 NotifyDisplayConfigurationChanging();
343 UpdateDisplayBoundsForLayout();
347 void DisplayController::SetLayoutForDisplayName(const std::string& name,
348 const DisplayLayout& layout) {
349 DisplayLayout& display_for_name = secondary_layouts_[name];
350 if (display_for_name.position != layout.position ||
351 display_for_name.offset != layout.offset) {
352 secondary_layouts_[name] = layout;
353 NotifyDisplayConfigurationChanging();
354 UpdateDisplayBoundsForLayout();
358 const DisplayLayout& DisplayController::GetLayoutForDisplay(
359 const gfx::Display& display) const {
360 const std::string& name = GetDisplayManager()->GetDisplayNameFor(display);
361 std::map<std::string, DisplayLayout>::const_iterator it =
362 secondary_layouts_.find(name);
364 if (it != secondary_layouts_.end())
365 return it->second;
366 return default_display_layout_;
369 const DisplayLayout& DisplayController::GetCurrentDisplayLayout() const {
370 DCHECK_EQ(2U, GetDisplayManager()->GetNumDisplays());
371 if (GetDisplayManager()->GetNumDisplays() > 1) {
372 DisplayController* non_const = const_cast<DisplayController*>(this);
373 return GetLayoutForDisplay(*(non_const->GetSecondaryDisplay()));
375 // On release build, just fallback to default instead of blowing up.
376 return default_display_layout_;
379 void DisplayController::SetPrimaryDisplayId(int64 id) {
380 desired_primary_display_id_ = id;
382 if (desired_primary_display_id_ == primary_display_id)
383 return;
385 internal::DisplayManager* display_manager = GetDisplayManager();
386 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) {
387 gfx::Display* display = display_manager->GetDisplayAt(i);
388 if (display->id() == id) {
389 SetPrimaryDisplay(*display);
390 break;
395 void DisplayController::SetPrimaryDisplay(
396 const gfx::Display& new_primary_display) {
397 internal::DisplayManager* display_manager = GetDisplayManager();
398 DCHECK(new_primary_display.is_valid());
399 DCHECK(display_manager->IsActiveDisplay(new_primary_display));
401 if (!new_primary_display.is_valid() ||
402 !display_manager->IsActiveDisplay(new_primary_display)) {
403 LOG(ERROR) << "Invalid or non-existent display is requested:"
404 << new_primary_display.ToString();
405 return;
408 if (primary_display_id == new_primary_display.id() ||
409 root_windows_.size() < 2) {
410 return;
413 aura::RootWindow* non_primary_root = root_windows_[new_primary_display.id()];
414 LOG_IF(ERROR, !non_primary_root)
415 << "Unknown display is requested in SetPrimaryDisplay: id="
416 << new_primary_display.id();
417 if (!non_primary_root)
418 return;
420 gfx::Display old_primary_display = GetPrimaryDisplay();
422 // Swap root windows between current and new primary display.
423 aura::RootWindow* primary_root = root_windows_[primary_display_id];
424 DCHECK(primary_root);
425 DCHECK_NE(primary_root, non_primary_root);
427 root_windows_[new_primary_display.id()] = primary_root;
428 primary_root->SetProperty(internal::kDisplayIdKey, new_primary_display.id());
430 root_windows_[old_primary_display.id()] = non_primary_root;
431 non_primary_root->SetProperty(internal::kDisplayIdKey,
432 old_primary_display.id());
434 primary_display_id = new_primary_display.id();
435 desired_primary_display_id_ = primary_display_id;
437 display_manager->UpdateWorkAreaOfDisplayNearestWindow(
438 primary_root, old_primary_display.GetWorkAreaInsets());
439 display_manager->UpdateWorkAreaOfDisplayNearestWindow(
440 non_primary_root, new_primary_display.GetWorkAreaInsets());
442 // Update the layout.
443 SetLayoutForDisplayName(
444 display_manager->GetDisplayNameFor(old_primary_display),
445 GetLayoutForDisplay(new_primary_display).Invert());
447 // Update the dispay manager with new display info.
448 std::vector<gfx::Display> displays;
449 displays.push_back(display_manager->GetDisplayForId(primary_display_id));
450 displays.push_back(*GetSecondaryDisplay());
451 GetDisplayManager()->set_force_bounds_changed(true);
452 GetDisplayManager()->UpdateDisplays(displays);
453 GetDisplayManager()->set_force_bounds_changed(false);
456 gfx::Display* DisplayController::GetSecondaryDisplay() {
457 internal::DisplayManager* display_manager = GetDisplayManager();
458 CHECK_EQ(2U, display_manager->GetNumDisplays());
459 return display_manager->GetDisplayAt(0)->id() == primary_display_id ?
460 display_manager->GetDisplayAt(1) : display_manager->GetDisplayAt(0);
463 void DisplayController::OnDisplayBoundsChanged(const gfx::Display& display) {
464 NotifyDisplayConfigurationChanging();
465 UpdateDisplayBoundsForLayout();
466 root_windows_[display.id()]->SetHostBounds(display.bounds_in_pixel());
469 void DisplayController::OnDisplayAdded(const gfx::Display& display) {
470 DCHECK(!root_windows_.empty());
471 NotifyDisplayConfigurationChanging();
472 aura::RootWindow* root = AddRootWindowForDisplay(display);
473 UpdateDisplayBoundsForLayout();
474 if (desired_primary_display_id_ == display.id())
475 SetPrimaryDisplay(display);
477 Shell::GetInstance()->InitRootWindowForSecondaryDisplay(root);
480 void DisplayController::OnDisplayRemoved(const gfx::Display& display) {
481 aura::RootWindow* root_to_delete = root_windows_[display.id()];
482 DCHECK(root_to_delete) << display.ToString();
483 NotifyDisplayConfigurationChanging();
485 // Display for root window will be deleted when the Primary RootWindow
486 // is deleted by the Shell.
487 root_windows_.erase(display.id());
489 // When the primary root window's display is removed, move the primary
490 // root to the other display.
491 if (primary_display_id == display.id()) {
492 DCHECK_EQ(1U, root_windows_.size());
493 primary_display_id = GetSecondaryDisplay()->id();
494 aura::RootWindow* primary_root = root_to_delete;
496 // Delete the other root instead.
497 root_to_delete = root_windows_[primary_display_id];
498 root_to_delete->SetProperty(internal::kDisplayIdKey, display.id());
500 // Setup primary root.
501 root_windows_[primary_display_id] = primary_root;
502 primary_root->SetProperty(internal::kDisplayIdKey, primary_display_id);
504 OnDisplayBoundsChanged(
505 GetDisplayManager()->GetDisplayForId(primary_display_id));
507 internal::RootWindowController* controller =
508 GetRootWindowController(root_to_delete);
509 DCHECK(controller);
510 controller->MoveWindowsTo(GetPrimaryRootWindow());
511 // Delete most of root window related objects, but don't delete
512 // root window itself yet because the stack may be using it.
513 controller->Shutdown();
514 MessageLoop::current()->DeleteSoon(FROM_HERE, controller);
517 aura::RootWindow* DisplayController::AddRootWindowForDisplay(
518 const gfx::Display& display) {
519 aura::RootWindow* root =
520 GetDisplayManager()->CreateRootWindowForDisplay(display);
521 root_windows_[display.id()] = root;
523 #if defined(OS_CHROMEOS)
524 static bool force_constrain_pointer_to_root =
525 CommandLine::ForCurrentProcess()->HasSwitch(
526 switches::kAshConstrainPointerToRoot);
527 if (base::chromeos::IsRunningOnChromeOS() || force_constrain_pointer_to_root)
528 root->ConfineCursorToWindow();
529 #endif
530 return root;
533 void DisplayController::UpdateDisplayBoundsForLayout() {
534 if (Shell::GetScreen()->GetNumDisplays() <= 1)
535 return;
537 DCHECK_EQ(2, Shell::GetScreen()->GetNumDisplays());
538 const gfx::Rect& primary_bounds = GetPrimaryDisplay().bounds();
540 gfx::Display* secondary_display = GetSecondaryDisplay();
541 const gfx::Rect& secondary_bounds = secondary_display->bounds();
542 gfx::Point new_secondary_origin = primary_bounds.origin();
544 const DisplayLayout& layout = GetLayoutForDisplay(*secondary_display);
545 DisplayLayout::Position position = layout.position;
547 // Ignore the offset in case the secondary display doesn't share edges with
548 // the primary display.
549 int offset = layout.offset;
550 if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) {
551 offset = std::min(
552 offset, primary_bounds.width() - kMinimumOverlapForInvalidOffset);
553 offset = std::max(
554 offset, -secondary_bounds.width() + kMinimumOverlapForInvalidOffset);
555 } else {
556 offset = std::min(
557 offset, primary_bounds.height() - kMinimumOverlapForInvalidOffset);
558 offset = std::max(
559 offset, -secondary_bounds.height() + kMinimumOverlapForInvalidOffset);
561 switch (position) {
562 case DisplayLayout::TOP:
563 new_secondary_origin.Offset(offset, -secondary_bounds.height());
564 break;
565 case DisplayLayout::RIGHT:
566 new_secondary_origin.Offset(primary_bounds.width(), offset);
567 break;
568 case DisplayLayout::BOTTOM:
569 new_secondary_origin.Offset(offset, primary_bounds.height());
570 break;
571 case DisplayLayout::LEFT:
572 new_secondary_origin.Offset(-secondary_bounds.width(), offset);
573 break;
575 gfx::Insets insets = secondary_display->GetWorkAreaInsets();
576 secondary_display->set_bounds(
577 gfx::Rect(new_secondary_origin, secondary_bounds.size()));
578 secondary_display->UpdateWorkAreaFromInsets(insets);
581 void DisplayController::NotifyDisplayConfigurationChanging() {
582 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayConfigurationChanging());
585 } // namespace ash