display_controller.cc revision 5821806d5e7f356e8fa4b058a389a808ea183019
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/display/display_controller.h" 6 7#include <algorithm> 8 9#include "ash/ash_switches.h" 10#include "ash/display/multi_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" 28 29#if defined(OS_CHROMEOS) 30#include "base/chromeos/chromeos_version.h" 31#endif 32 33namespace ash { 34namespace { 35 36// Primary display stored in global object as it can be 37// accessed after Shell is deleted. 38int64 primary_display_id = gfx::Display::kInvalidDisplayID; 39 40// The maximum value for 'offset' in DisplayLayout in case of outliers. Need 41// to change this value in case to support even larger displays. 42const int kMaxValidOffset = 10000; 43 44// The number of pixels to overlap between the primary and secondary displays, 45// in case that the offset value is too large. 46const int kMinimumOverlapForInvalidOffset = 100; 47 48bool GetPositionFromString(const base::StringPiece& position, 49 DisplayLayout::Position* field) { 50 if (position == "top") { 51 *field = DisplayLayout::TOP; 52 return true; 53 } else if (position == "bottom") { 54 *field = DisplayLayout::BOTTOM; 55 return true; 56 } else if (position == "right") { 57 *field = DisplayLayout::RIGHT; 58 return true; 59 } else if (position == "left") { 60 *field = DisplayLayout::LEFT; 61 return true; 62 } 63 LOG(ERROR) << "Invalid position value: " << position; 64 return false; 65} 66 67std::string GetStringFromPosition(DisplayLayout::Position position) { 68 switch (position) { 69 case DisplayLayout::TOP: 70 return std::string("top"); 71 case DisplayLayout::BOTTOM: 72 return std::string("bottom"); 73 case DisplayLayout::RIGHT: 74 return std::string("right"); 75 case DisplayLayout::LEFT: 76 return std::string("left"); 77 } 78 return std::string("unknown"); 79} 80 81internal::MultiDisplayManager* GetDisplayManager() { 82 return static_cast<internal::MultiDisplayManager*>( 83 aura::Env::GetInstance()->display_manager()); 84} 85 86} // namespace 87 88DisplayLayout::DisplayLayout() 89 : position(RIGHT), 90 offset(0) {} 91 92DisplayLayout::DisplayLayout(DisplayLayout::Position position, int offset) 93 : position(position), 94 offset(offset) { 95 DCHECK_LE(TOP, position); 96 DCHECK_GE(LEFT, position); 97 98 // Set the default value to |position| in case position is invalid. DCHECKs 99 // above doesn't stop in Release builds. 100 if (TOP > position || LEFT < position) 101 this->position = RIGHT; 102 103 DCHECK_GE(kMaxValidOffset, abs(offset)); 104} 105 106DisplayLayout DisplayLayout::Invert() const { 107 Position inverted_position = RIGHT; 108 switch (position) { 109 case TOP: 110 inverted_position = BOTTOM; 111 break; 112 case BOTTOM: 113 inverted_position = TOP; 114 break; 115 case RIGHT: 116 inverted_position = LEFT; 117 break; 118 case LEFT: 119 inverted_position = RIGHT; 120 break; 121 } 122 return DisplayLayout(inverted_position, -offset); 123} 124 125// static 126bool DisplayLayout::ConvertFromValue(const base::Value& value, 127 DisplayLayout* layout) { 128 base::JSONValueConverter<DisplayLayout> converter; 129 return converter.Convert(value, layout); 130} 131 132// static 133bool DisplayLayout::ConvertToValue(const DisplayLayout& layout, 134 base::Value* value) { 135 base::DictionaryValue* dict_value = NULL; 136 if (!value->GetAsDictionary(&dict_value) || dict_value == NULL) 137 return false; 138 139 const std::string position_str = GetStringFromPosition(layout.position); 140 dict_value->SetString("position", position_str); 141 dict_value->SetInteger("offset", layout.offset); 142 return true; 143} 144 145std::string DisplayLayout::ToString() const { 146 const std::string position_str = GetStringFromPosition(position); 147 return StringPrintf("%s, %d", position_str.c_str(), offset); 148} 149 150// static 151void DisplayLayout::RegisterJSONConverter( 152 base::JSONValueConverter<DisplayLayout>* converter) { 153 converter->RegisterCustomField<Position>( 154 "position", &DisplayLayout::position, &GetPositionFromString); 155 converter->RegisterIntField("offset", &DisplayLayout::offset); 156} 157 158DisplayController::DisplayController() 159 : desired_primary_display_id_(gfx::Display::kInvalidDisplayID) { 160 // Reset primary display to make sure that tests don't use 161 // stale display info from previous tests. 162 primary_display_id = gfx::Display::kInvalidDisplayID; 163 164 GetDisplayManager()->AddObserver(this); 165} 166 167DisplayController::~DisplayController() { 168 GetDisplayManager()->RemoveObserver(this); 169 // Delete all root window controllers, which deletes root window 170 // from the last so that the primary root window gets deleted last. 171 for (std::map<int64, aura::RootWindow*>::const_reverse_iterator it = 172 root_windows_.rbegin(); it != root_windows_.rend(); ++it) { 173 internal::RootWindowController* controller = 174 GetRootWindowController(it->second); 175 DCHECK(controller); 176 delete controller; 177 } 178} 179// static 180const gfx::Display& DisplayController::GetPrimaryDisplay() { 181 DCHECK_NE(primary_display_id, gfx::Display::kInvalidDisplayID); 182 return GetDisplayManager()->GetDisplayForId(primary_display_id); 183} 184 185void DisplayController::InitPrimaryDisplay() { 186 const gfx::Display* primary_candidate = GetDisplayManager()->GetDisplayAt(0); 187#if defined(OS_CHROMEOS) 188 if (base::chromeos::IsRunningOnChromeOS()) { 189 internal::MultiDisplayManager* display_manager = GetDisplayManager(); 190 // On ChromeOS device, root windows are stacked vertically, and 191 // default primary is the one on top. 192 int count = display_manager->GetNumDisplays(); 193 int y = primary_candidate->bounds_in_pixel().y(); 194 for (int i = 1; i < count; ++i) { 195 const gfx::Display* display = display_manager->GetDisplayAt(i); 196 if (display_manager->IsInternalDisplayId(display->id())) { 197 primary_candidate = display; 198 break; 199 } else if (display->bounds_in_pixel().y() < y) { 200 primary_candidate = display; 201 y = display->bounds_in_pixel().y(); 202 } 203 } 204 } 205#endif 206 primary_display_id = primary_candidate->id(); 207 aura::RootWindow* root = AddRootWindowForDisplay(*primary_candidate); 208 root->SetHostBounds(primary_candidate->bounds_in_pixel()); 209 UpdateDisplayBoundsForLayout(); 210} 211 212void DisplayController::InitSecondaryDisplays() { 213 internal::MultiDisplayManager* display_manager = GetDisplayManager(); 214 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { 215 const gfx::Display* display = display_manager->GetDisplayAt(i); 216 if (primary_display_id != display->id()) { 217 aura::RootWindow* root = AddRootWindowForDisplay(*display); 218 Shell::GetInstance()->InitRootWindowForSecondaryDisplay(root); 219 } 220 } 221 CommandLine* command_line = CommandLine::ForCurrentProcess(); 222 if (command_line->HasSwitch(switches::kAshSecondaryDisplayLayout)) { 223 std::string value = command_line->GetSwitchValueASCII( 224 switches::kAshSecondaryDisplayLayout); 225 char layout; 226 int offset; 227 if (sscanf(value.c_str(), "%c,%d", &layout, &offset) == 2) { 228 if (layout == 't') 229 default_display_layout_.position = DisplayLayout::TOP; 230 else if (layout == 'b') 231 default_display_layout_.position = DisplayLayout::BOTTOM; 232 else if (layout == 'r') 233 default_display_layout_.position = DisplayLayout::RIGHT; 234 else if (layout == 'l') 235 default_display_layout_.position = DisplayLayout::LEFT; 236 default_display_layout_.offset = offset; 237 } 238 } 239 UpdateDisplayBoundsForLayout(); 240} 241 242void DisplayController::AddObserver(Observer* observer) { 243 observers_.AddObserver(observer); 244} 245 246void DisplayController::RemoveObserver(Observer* observer) { 247 observers_.RemoveObserver(observer); 248} 249 250aura::RootWindow* DisplayController::GetPrimaryRootWindow() { 251 DCHECK(!root_windows_.empty()); 252 return root_windows_[primary_display_id]; 253} 254 255aura::RootWindow* DisplayController::GetRootWindowForDisplayId(int64 id) { 256 return root_windows_[id]; 257} 258 259void DisplayController::CloseChildWindows() { 260 for (std::map<int64, aura::RootWindow*>::const_iterator it = 261 root_windows_.begin(); it != root_windows_.end(); ++it) { 262 aura::RootWindow* root_window = it->second; 263 internal::RootWindowController* controller = 264 GetRootWindowController(root_window); 265 if (controller) { 266 controller->CloseChildWindows(); 267 } else { 268 while (!root_window->children().empty()) { 269 aura::Window* child = root_window->children()[0]; 270 delete child; 271 } 272 } 273 } 274} 275 276std::vector<aura::RootWindow*> DisplayController::GetAllRootWindows() { 277 std::vector<aura::RootWindow*> windows; 278 for (std::map<int64, aura::RootWindow*>::const_iterator it = 279 root_windows_.begin(); it != root_windows_.end(); ++it) { 280 DCHECK(it->second); 281 if (GetRootWindowController(it->second)) 282 windows.push_back(it->second); 283 } 284 return windows; 285} 286 287gfx::Insets DisplayController::GetOverscanInsets(int64 display_id) const { 288 return GetDisplayManager()->GetOverscanInsets(display_id); 289} 290 291std::vector<internal::RootWindowController*> 292DisplayController::GetAllRootWindowControllers() { 293 std::vector<internal::RootWindowController*> controllers; 294 for (std::map<int64, aura::RootWindow*>::const_iterator it = 295 root_windows_.begin(); it != root_windows_.end(); ++it) { 296 internal::RootWindowController* controller = 297 GetRootWindowController(it->second); 298 if (controller) 299 controllers.push_back(controller); 300 } 301 return controllers; 302} 303 304void DisplayController::SetDefaultDisplayLayout(const DisplayLayout& layout) { 305 CommandLine* command_line = CommandLine::ForCurrentProcess(); 306 if (!command_line->HasSwitch(switches::kAshSecondaryDisplayLayout) && 307 (default_display_layout_.position != layout.position || 308 default_display_layout_.offset != layout.offset)) { 309 default_display_layout_ = layout; 310 NotifyDisplayConfigurationChanging(); 311 UpdateDisplayBoundsForLayout(); 312 } 313} 314 315void DisplayController::SetLayoutForDisplayName(const std::string& name, 316 const DisplayLayout& layout) { 317 DisplayLayout& display_for_name = secondary_layouts_[name]; 318 if (display_for_name.position != layout.position || 319 display_for_name.offset != layout.offset) { 320 secondary_layouts_[name] = layout; 321 NotifyDisplayConfigurationChanging(); 322 UpdateDisplayBoundsForLayout(); 323 } 324} 325 326const DisplayLayout& DisplayController::GetLayoutForDisplay( 327 const gfx::Display& display) const { 328 const std::string& name = GetDisplayManager()->GetDisplayNameFor(display); 329 std::map<std::string, DisplayLayout>::const_iterator it = 330 secondary_layouts_.find(name); 331 332 if (it != secondary_layouts_.end()) 333 return it->second; 334 return default_display_layout_; 335} 336 337const DisplayLayout& DisplayController::GetCurrentDisplayLayout() const { 338 DCHECK_EQ(2U, GetDisplayManager()->GetNumDisplays()); 339 if (GetDisplayManager()->GetNumDisplays() > 1) { 340 DisplayController* non_const = const_cast<DisplayController*>(this); 341 return GetLayoutForDisplay(*(non_const->GetSecondaryDisplay())); 342 } 343 // On release build, just fallback to default instead of blowing up. 344 return default_display_layout_; 345} 346 347void DisplayController::SetPrimaryDisplayId(int64 id) { 348 desired_primary_display_id_ = id; 349 350 if (desired_primary_display_id_ == primary_display_id) 351 return; 352 353 aura::DisplayManager* display_manager = 354 aura::Env::GetInstance()->display_manager(); 355 for (size_t i = 0; i < display_manager->GetNumDisplays(); ++i) { 356 gfx::Display* display = display_manager->GetDisplayAt(i); 357 if (display->id() == id) { 358 SetPrimaryDisplay(*display); 359 break; 360 } 361 } 362} 363 364void DisplayController::SetPrimaryDisplay( 365 const gfx::Display& new_primary_display) { 366 internal::MultiDisplayManager* display_manager = GetDisplayManager(); 367 DCHECK(new_primary_display.is_valid()); 368 DCHECK(display_manager->IsActiveDisplay(new_primary_display)); 369 370 if (!new_primary_display.is_valid() || 371 !display_manager->IsActiveDisplay(new_primary_display)) { 372 LOG(ERROR) << "Invalid or non-existent display is requested:" 373 << new_primary_display.ToString(); 374 return; 375 } 376 377 if (primary_display_id == new_primary_display.id() || 378 root_windows_.size() < 2) { 379 return; 380 } 381 382 aura::RootWindow* non_primary_root = root_windows_[new_primary_display.id()]; 383 LOG_IF(ERROR, !non_primary_root) 384 << "Unknown display is requested in SetPrimaryDisplay: id=" 385 << new_primary_display.id(); 386 if (!non_primary_root) 387 return; 388 389 gfx::Display old_primary_display = GetPrimaryDisplay(); 390 391 // Swap root windows between current and new primary display. 392 aura::RootWindow* primary_root = root_windows_[primary_display_id]; 393 DCHECK(primary_root); 394 DCHECK_NE(primary_root, non_primary_root); 395 396 root_windows_[new_primary_display.id()] = primary_root; 397 primary_root->SetProperty(internal::kDisplayIdKey, new_primary_display.id()); 398 399 root_windows_[old_primary_display.id()] = non_primary_root; 400 non_primary_root->SetProperty(internal::kDisplayIdKey, 401 old_primary_display.id()); 402 403 primary_display_id = new_primary_display.id(); 404 desired_primary_display_id_ = primary_display_id; 405 406 display_manager->UpdateWorkAreaOfDisplayNearestWindow( 407 primary_root, old_primary_display.GetWorkAreaInsets()); 408 display_manager->UpdateWorkAreaOfDisplayNearestWindow( 409 non_primary_root, new_primary_display.GetWorkAreaInsets()); 410 411 // Update the layout. 412 SetLayoutForDisplayName( 413 display_manager->GetDisplayNameFor(old_primary_display), 414 GetLayoutForDisplay(new_primary_display).Invert()); 415 416 // Update the dispay manager with new display info. 417 std::vector<gfx::Display> displays; 418 displays.push_back(display_manager->GetDisplayForId(primary_display_id)); 419 displays.push_back(*GetSecondaryDisplay()); 420 GetDisplayManager()->set_force_bounds_changed(true); 421 GetDisplayManager()->OnNativeDisplaysChanged(displays); 422 GetDisplayManager()->set_force_bounds_changed(false); 423} 424 425gfx::Display* DisplayController::GetSecondaryDisplay() { 426 internal::MultiDisplayManager* display_manager = GetDisplayManager(); 427 CHECK_EQ(2U, display_manager->GetNumDisplays()); 428 return display_manager->GetDisplayAt(0)->id() == primary_display_id ? 429 display_manager->GetDisplayAt(1) : display_manager->GetDisplayAt(0); 430} 431 432void DisplayController::OnDisplayBoundsChanged(const gfx::Display& display) { 433 NotifyDisplayConfigurationChanging(); 434 UpdateDisplayBoundsForLayout(); 435 root_windows_[display.id()]->SetHostBounds(display.bounds_in_pixel()); 436} 437 438void DisplayController::OnDisplayAdded(const gfx::Display& display) { 439 DCHECK(!root_windows_.empty()); 440 NotifyDisplayConfigurationChanging(); 441 aura::RootWindow* root = AddRootWindowForDisplay(display); 442 Shell::GetInstance()->InitRootWindowForSecondaryDisplay(root); 443 UpdateDisplayBoundsForLayout(); 444 445 if (desired_primary_display_id_ == display.id()) 446 SetPrimaryDisplay(display); 447} 448 449void DisplayController::OnDisplayRemoved(const gfx::Display& display) { 450 aura::RootWindow* root_to_delete = root_windows_[display.id()]; 451 DCHECK(root_to_delete) << display.ToString(); 452 NotifyDisplayConfigurationChanging(); 453 454 // Display for root window will be deleted when the Primary RootWindow 455 // is deleted by the Shell. 456 root_windows_.erase(display.id()); 457 458 // When the primary root window's display is removed, move the primary 459 // root to the other display. 460 if (primary_display_id == display.id()) { 461 DCHECK_EQ(1U, root_windows_.size()); 462 primary_display_id = GetSecondaryDisplay()->id(); 463 aura::RootWindow* primary_root = root_to_delete; 464 465 // Delete the other root instead. 466 root_to_delete = root_windows_[primary_display_id]; 467 root_to_delete->SetProperty(internal::kDisplayIdKey, display.id()); 468 469 // Setup primary root. 470 root_windows_[primary_display_id] = primary_root; 471 primary_root->SetProperty(internal::kDisplayIdKey, primary_display_id); 472 473 OnDisplayBoundsChanged( 474 GetDisplayManager()->GetDisplayForId(primary_display_id)); 475 } 476 internal::RootWindowController* controller = 477 GetRootWindowController(root_to_delete); 478 DCHECK(controller); 479 controller->MoveWindowsTo(GetPrimaryRootWindow()); 480 // Delete most of root window related objects, but don't delete 481 // root window itself yet because the stack may be using it. 482 controller->Shutdown(); 483 MessageLoop::current()->DeleteSoon(FROM_HERE, controller); 484} 485 486aura::RootWindow* DisplayController::AddRootWindowForDisplay( 487 const gfx::Display& display) { 488 aura::RootWindow* root = 489 GetDisplayManager()->CreateRootWindowForDisplay(display); 490 root_windows_[display.id()] = root; 491 492#if defined(OS_CHROMEOS) 493 static bool force_constrain_pointer_to_root = 494 CommandLine::ForCurrentProcess()->HasSwitch( 495 switches::kAshConstrainPointerToRoot); 496 if (base::chromeos::IsRunningOnChromeOS() || force_constrain_pointer_to_root) 497 root->ConfineCursorToWindow(); 498#endif 499 return root; 500} 501 502void DisplayController::UpdateDisplayBoundsForLayout() { 503 if (Shell::GetScreen()->GetNumDisplays() <= 1) 504 return; 505 506 DCHECK_EQ(2, Shell::GetScreen()->GetNumDisplays()); 507 const gfx::Rect& primary_bounds = GetPrimaryDisplay().bounds(); 508 509 gfx::Display* secondary_display = GetSecondaryDisplay(); 510 const gfx::Rect& secondary_bounds = secondary_display->bounds(); 511 gfx::Point new_secondary_origin = primary_bounds.origin(); 512 513 const DisplayLayout& layout = GetLayoutForDisplay(*secondary_display); 514 DisplayLayout::Position position = layout.position; 515 516 // Ignore the offset in case the secondary display doesn't share edges with 517 // the primary display. 518 int offset = layout.offset; 519 if (position == DisplayLayout::TOP || position == DisplayLayout::BOTTOM) { 520 offset = std::min( 521 offset, primary_bounds.width() - kMinimumOverlapForInvalidOffset); 522 offset = std::max( 523 offset, -secondary_bounds.width() + kMinimumOverlapForInvalidOffset); 524 } else { 525 offset = std::min( 526 offset, primary_bounds.height() - kMinimumOverlapForInvalidOffset); 527 offset = std::max( 528 offset, -secondary_bounds.height() + kMinimumOverlapForInvalidOffset); 529 } 530 switch (position) { 531 case DisplayLayout::TOP: 532 new_secondary_origin.Offset(offset, -secondary_bounds.height()); 533 break; 534 case DisplayLayout::RIGHT: 535 new_secondary_origin.Offset(primary_bounds.width(), offset); 536 break; 537 case DisplayLayout::BOTTOM: 538 new_secondary_origin.Offset(offset, primary_bounds.height()); 539 break; 540 case DisplayLayout::LEFT: 541 new_secondary_origin.Offset(-secondary_bounds.width(), offset); 542 break; 543 } 544 gfx::Insets insets = secondary_display->GetWorkAreaInsets(); 545 secondary_display->set_bounds( 546 gfx::Rect(new_secondary_origin, secondary_bounds.size())); 547 secondary_display->UpdateWorkAreaFromInsets(insets); 548} 549 550void DisplayController::NotifyDisplayConfigurationChanging() { 551 FOR_EACH_OBSERVER(Observer, observers_, OnDisplayConfigurationChanging()); 552} 553 554} // namespace ash 555