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"
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"
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
;
59 } else if (position
== "bottom") {
60 *field
= DisplayLayout::BOTTOM
;
62 } else if (position
== "right") {
63 *field
= DisplayLayout::RIGHT
;
65 } else if (position
== "left") {
66 *field
= DisplayLayout::LEFT
;
69 LOG(ERROR
) << "Invalid position value: " << position
;
73 std::string
GetStringFromPosition(DisplayLayout::Position 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();
93 DisplayLayout::DisplayLayout()
97 DisplayLayout::DisplayLayout(DisplayLayout::Position position
, int 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
;
115 inverted_position
= BOTTOM
;
118 inverted_position
= TOP
;
121 inverted_position
= LEFT
;
124 inverted_position
= RIGHT
;
127 return DisplayLayout(inverted_position
, -offset
);
131 bool DisplayLayout::ConvertFromValue(const base::Value
& value
,
132 DisplayLayout
* layout
) {
133 base::JSONValueConverter
<DisplayLayout
> converter
;
134 return converter
.Convert(value
, layout
);
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
)
144 const std::string position_str
= GetStringFromPosition(layout
.position
);
145 dict_value
->SetString("position", position_str
);
146 dict_value
->SetInteger("offset", layout
.offset
);
150 std::string
DisplayLayout::ToString() const {
151 const std::string position_str
= GetStringFromPosition(position
);
152 return StringPrintf("%s, %d", position_str
.c_str(), offset
);
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
);
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
);
201 int DisplayController::GetNumDisplays() {
202 if (num_displays_for_shutdown
>= 0)
203 return num_displays_for_shutdown
;
204 return GetDisplayManager()->GetNumDisplays();
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
;
226 } else if (display
->bounds_in_pixel().y() < y
) {
227 primary_candidate
= display
;
228 y
= display
->bounds_in_pixel().y();
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
);
254 if (sscanf(value
.c_str(), "%c,%d", &layout
, &offset
) == 2) {
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
);
293 controller
->CloseChildWindows();
295 while (!root_window
->children().empty()) {
296 aura::Window
* child
= root_window
->children()[0];
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
) {
308 if (GetRootWindowController(it
->second
))
309 windows
.push_back(it
->second
);
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
);
331 controllers
.push_back(controller
);
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())
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
)
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
);
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();
408 if (primary_display_id
== new_primary_display
.id() ||
409 root_windows_
.size() < 2) {
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
)
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
);
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();
533 void DisplayController::UpdateDisplayBoundsForLayout() {
534 if (Shell::GetScreen()->GetNumDisplays() <= 1)
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
) {
552 offset
, primary_bounds
.width() - kMinimumOverlapForInvalidOffset
);
554 offset
, -secondary_bounds
.width() + kMinimumOverlapForInvalidOffset
);
557 offset
, primary_bounds
.height() - kMinimumOverlapForInvalidOffset
);
559 offset
, -secondary_bounds
.height() + kMinimumOverlapForInvalidOffset
);
562 case DisplayLayout::TOP
:
563 new_secondary_origin
.Offset(offset
, -secondary_bounds
.height());
565 case DisplayLayout::RIGHT
:
566 new_secondary_origin
.Offset(primary_bounds
.width(), offset
);
568 case DisplayLayout::BOTTOM
:
569 new_secondary_origin
.Offset(offset
, primary_bounds
.height());
571 case DisplayLayout::LEFT
:
572 new_secondary_origin
.Offset(-secondary_bounds
.width(), offset
);
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());