Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "ui/display/chromeos/display_configurator.h" | 5 #include "ui/display/chromeos/display_configurator.h" |
| 6 | 6 |
| 7 #include "base/bind.h" | 7 #include "base/bind.h" |
| 8 #include "base/command_line.h" | 8 #include "base/command_line.h" |
| 9 #include "base/logging.h" | 9 #include "base/logging.h" |
| 10 #include "base/sys_info.h" | 10 #include "base/sys_info.h" |
| 11 #include "base/time/time.h" | 11 #include "base/time/time.h" |
| 12 #include "ui/display/chromeos/display_util.h" | 12 #include "ui/display/chromeos/display_util.h" |
| 13 #include "ui/display/chromeos/update_display_configuration_task.h" | |
| 13 #include "ui/display/display_switches.h" | 14 #include "ui/display/display_switches.h" |
| 14 #include "ui/display/types/display_mode.h" | 15 #include "ui/display/types/display_mode.h" |
| 15 #include "ui/display/types/display_snapshot.h" | 16 #include "ui/display/types/display_snapshot.h" |
| 16 #include "ui/display/types/native_display_delegate.h" | 17 #include "ui/display/types/native_display_delegate.h" |
| 17 | 18 |
| 18 namespace ui { | 19 namespace ui { |
| 19 | 20 |
| 20 namespace { | 21 namespace { |
| 21 | 22 |
| 22 typedef std::vector<const DisplayMode*> DisplayModeList; | 23 typedef std::vector<const DisplayMode*> DisplayModeList; |
| (...skipping 24 matching lines...) Expand all Loading... | |
| 47 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() { | 48 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() { |
| 48 if (configurator_->configure_timer_.IsRunning()) { | 49 if (configurator_->configure_timer_.IsRunning()) { |
| 49 configurator_->configure_timer_.user_task().Run(); | 50 configurator_->configure_timer_.user_task().Run(); |
| 50 configurator_->configure_timer_.Stop(); | 51 configurator_->configure_timer_.Stop(); |
| 51 return true; | 52 return true; |
| 52 } else { | 53 } else { |
| 53 return false; | 54 return false; |
| 54 } | 55 } |
| 55 } | 56 } |
| 56 | 57 |
| 58 //////////////////////////////////////////////////////////////////////////////// | |
| 59 // DisplayConfigurator::DisplayLayoutManagerImpl implementation | |
| 60 | |
| 61 class DisplayConfigurator::DisplayLayoutManagerImpl | |
| 62 : public DisplayLayoutManager { | |
| 63 public: | |
| 64 DisplayLayoutManagerImpl(DisplayConfigurator* configurator); | |
| 65 ~DisplayLayoutManagerImpl() override; | |
| 66 | |
| 67 // DisplayConfigurator::DisplayLayoutManager: | |
| 68 SoftwareMirroringController* GetSoftwareMirroringController() const override; | |
| 69 StateController* GetStateController() const override; | |
| 70 MultipleDisplayState GetDisplayState() const override; | |
| 71 chromeos::DisplayPowerState GetPowerState() const override; | |
| 72 std::vector<DisplayState> ParseDisplays( | |
| 73 const std::vector<DisplaySnapshot*>& displays) const override; | |
| 74 bool GetDisplayLayout(const std::vector<DisplayState>& displays, | |
| 75 MultipleDisplayState new_display_state, | |
| 76 chromeos::DisplayPowerState new_power_state, | |
| 77 std::vector<DisplayConfigureRequest>* requests, | |
| 78 gfx::Size* framebuffer_size) const override; | |
| 79 | |
| 80 private: | |
| 81 // Helper method for ParseDisplays() that initializes the passed-in | |
| 82 // displays' |mirror_mode| fields by looking for a mode in |internal_display| | |
| 83 // and |external_display| having the same resolution. Returns false if a | |
| 84 // shared | |
| 85 // mode wasn't found or created. | |
|
Daniel Erat
2014/12/12 16:25:28
nit: fix wrapping
dnicoara
2014/12/12 17:45:09
Done.
| |
| 86 // | |
| 87 // |try_panel_fitting| allows creating a panel-fitting mode for | |
| 88 // |internal_display| instead of only searching for a matching mode (note that | |
| 89 // it may lead to a crash if |internal_info| is not capable of panel fitting). | |
|
Daniel Erat
2014/12/12 16:25:28
s/internal_info/internal_display/ ?
dnicoara
2014/12/12 17:45:09
Done.
| |
| 90 // | |
| 91 // |preserve_aspect| limits the search/creation only to the modes having the | |
| 92 // native aspect ratio of |external_display|. | |
| 93 bool FindMirrorMode(DisplayState* internal_display, | |
| 94 DisplayState* external_display, | |
| 95 bool try_panel_fitting, | |
| 96 bool preserve_aspect) const; | |
| 97 | |
| 98 DisplayConfigurator* configurator_; // Not owned. | |
| 99 | |
| 100 DISALLOW_COPY_AND_ASSIGN(DisplayLayoutManagerImpl); | |
| 101 }; | |
| 102 | |
| 103 DisplayConfigurator::DisplayLayoutManagerImpl::DisplayLayoutManagerImpl( | |
| 104 DisplayConfigurator* configurator) | |
| 105 : configurator_(configurator) { | |
| 106 } | |
| 107 | |
| 108 DisplayConfigurator::DisplayLayoutManagerImpl::~DisplayLayoutManagerImpl() { | |
| 109 } | |
| 110 | |
| 111 DisplayConfigurator::SoftwareMirroringController* | |
| 112 DisplayConfigurator::DisplayLayoutManagerImpl::GetSoftwareMirroringController() | |
| 113 const { | |
| 114 return configurator_->mirroring_controller_; | |
| 115 } | |
| 116 | |
| 117 DisplayConfigurator::StateController* | |
| 118 DisplayConfigurator::DisplayLayoutManagerImpl::GetStateController() const { | |
| 119 return configurator_->state_controller_; | |
| 120 } | |
| 121 | |
| 122 MultipleDisplayState | |
| 123 DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayState() const { | |
| 124 return configurator_->current_display_state_; | |
| 125 } | |
| 126 | |
| 127 chromeos::DisplayPowerState | |
| 128 DisplayConfigurator::DisplayLayoutManagerImpl::GetPowerState() const { | |
| 129 return configurator_->current_power_state_; | |
| 130 } | |
| 131 | |
| 132 std::vector<DisplayConfigurator::DisplayState> | |
| 133 DisplayConfigurator::DisplayLayoutManagerImpl::ParseDisplays( | |
| 134 const std::vector<DisplaySnapshot*>& snapshots) const { | |
| 135 std::vector<DisplayState> cached_displays; | |
| 136 for (size_t i = 0; i < snapshots.size(); ++i) { | |
|
Daniel Erat
2014/12/12 16:25:29
nit: for (auto snapshot : snapshots) ?
dnicoara
2014/12/12 17:45:09
Done.
| |
| 137 DisplayState display_state; | |
| 138 display_state.display = snapshots[i]; | |
| 139 cached_displays.push_back(display_state); | |
| 140 } | |
| 141 | |
| 142 // Set |selected_mode| fields. | |
| 143 for (size_t i = 0; i < cached_displays.size(); ++i) { | |
| 144 DisplayState* display_state = &cached_displays[i]; | |
| 145 gfx::Size size; | |
| 146 if (GetStateController() && | |
| 147 GetStateController()->GetResolutionForDisplayId( | |
| 148 display_state->display->display_id(), &size)) { | |
| 149 display_state->selected_mode = | |
| 150 FindDisplayModeMatchingSize(*display_state->display, size); | |
| 151 } | |
| 152 | |
| 153 // Fall back to native mode. | |
| 154 if (!display_state->selected_mode) | |
| 155 display_state->selected_mode = display_state->display->native_mode(); | |
| 156 } | |
| 157 | |
| 158 // Set |mirror_mode| fields. | |
| 159 if (cached_displays.size() == 2) { | |
| 160 bool one_is_internal = | |
| 161 cached_displays[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; | |
| 162 bool two_is_internal = | |
| 163 cached_displays[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; | |
| 164 int internal_displays = | |
| 165 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0); | |
| 166 DCHECK_LT(internal_displays, 2); | |
| 167 LOG_IF(WARNING, internal_displays == 2) | |
|
Daniel Erat
2014/12/12 16:25:28
nit: >= 2 ?
dnicoara
2014/12/12 17:45:09
Done.
| |
| 168 << "Two internal displays detected."; | |
| 169 | |
| 170 bool can_mirror = false; | |
| 171 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { | |
| 172 // Try preserving external display's aspect ratio on the first attempt. | |
| 173 // If that fails, fall back to the highest matching resolution. | |
| 174 bool preserve_aspect = attempt == 0; | |
| 175 | |
| 176 if (internal_displays == 1) { | |
| 177 if (one_is_internal) { | |
|
Daniel Erat
2014/12/12 16:25:29
nit: to remove duplication, maybe rewrite as:
c
dnicoara
2014/12/12 17:45:09
Done.
| |
| 178 can_mirror = FindMirrorMode(&cached_displays[0], &cached_displays[1], | |
| 179 configurator_->is_panel_fitting_enabled_, | |
| 180 preserve_aspect); | |
| 181 } else { | |
| 182 DCHECK(two_is_internal); | |
| 183 can_mirror = FindMirrorMode(&cached_displays[1], &cached_displays[0], | |
| 184 configurator_->is_panel_fitting_enabled_, | |
| 185 preserve_aspect); | |
| 186 } | |
| 187 } else { // if (internal_displays == 0) | |
| 188 // No panel fitting for external displays, so fall back to exact match. | |
| 189 can_mirror = FindMirrorMode(&cached_displays[0], &cached_displays[1], | |
| 190 false, preserve_aspect); | |
| 191 if (!can_mirror && preserve_aspect) { | |
| 192 // FindMirrorMode() will try to preserve aspect ratio of what it | |
| 193 // thinks is external display, so if it didn't succeed with one, maybe | |
| 194 // it will succeed with the other. This way we will have the correct | |
| 195 // aspect ratio on at least one of them. | |
| 196 can_mirror = FindMirrorMode(&cached_displays[1], &cached_displays[0], | |
| 197 false, preserve_aspect); | |
| 198 } | |
| 199 } | |
| 200 } | |
| 201 } | |
| 202 | |
| 203 return cached_displays; | |
| 204 } | |
| 205 | |
| 206 bool DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayLayout( | |
| 207 const std::vector<DisplayState>& displays, | |
| 208 MultipleDisplayState new_display_state, | |
| 209 chromeos::DisplayPowerState new_power_state, | |
| 210 std::vector<DisplayConfigureRequest>* requests, | |
| 211 gfx::Size* framebuffer_size) const { | |
| 212 std::vector<bool> display_power; | |
| 213 int num_on_displays = | |
| 214 GetDisplayPower(displays, new_power_state, &display_power); | |
| 215 VLOG(1) << "EnterState: display=" | |
| 216 << MultipleDisplayStateToString(new_display_state) | |
| 217 << " power=" << DisplayPowerStateToString(new_power_state); | |
| 218 | |
| 219 // Framebuffer dimensions. | |
| 220 gfx::Size size; | |
| 221 | |
| 222 for (size_t i = 0; i < displays.size(); ++i) { | |
| 223 requests->push_back(DisplayConfigureRequest( | |
| 224 displays[i].display, displays[i].display->current_mode(), | |
| 225 gfx::Point())); | |
| 226 } | |
| 227 | |
| 228 switch (new_display_state) { | |
| 229 case MULTIPLE_DISPLAY_STATE_INVALID: | |
| 230 NOTREACHED() << "Ignoring request to enter invalid state with " | |
| 231 << displays.size() << " connected display(s)"; | |
| 232 return false; | |
| 233 case MULTIPLE_DISPLAY_STATE_HEADLESS: | |
| 234 if (displays.size() != 0) { | |
| 235 LOG(WARNING) << "Ignoring request to enter headless mode with " | |
| 236 << displays.size() << " connected display(s)"; | |
| 237 return false; | |
| 238 } | |
| 239 break; | |
| 240 case MULTIPLE_DISPLAY_STATE_SINGLE: { | |
| 241 // If there are multiple displays connected, only one should be turned on. | |
| 242 if (displays.size() != 1 && num_on_displays != 1) { | |
| 243 LOG(WARNING) << "Ignoring request to enter single mode with " | |
| 244 << displays.size() << " connected displays and " | |
| 245 << num_on_displays << " turned on"; | |
| 246 return false; | |
| 247 } | |
| 248 | |
| 249 for (size_t i = 0; i < displays.size(); ++i) { | |
| 250 const DisplayConfigurator::DisplayState* state = &displays[i]; | |
| 251 (*requests)[i].mode = display_power[i] ? state->selected_mode : NULL; | |
| 252 | |
| 253 if (display_power[i] || displays.size() == 1) { | |
| 254 const DisplayMode* mode_info = state->selected_mode; | |
| 255 if (!mode_info) { | |
| 256 LOG(WARNING) << "No selected mode when configuring display: " | |
| 257 << state->display->ToString(); | |
| 258 return false; | |
| 259 } | |
| 260 if (mode_info->size() == gfx::Size(1024, 768)) { | |
| 261 VLOG(1) << "Potentially misdetecting display(1024x768):" | |
| 262 << " displays size=" << displays.size() | |
| 263 << ", num_on_displays=" << num_on_displays | |
| 264 << ", current size:" << size.width() << "x" << size.height() | |
| 265 << ", i=" << i << ", display=" << state->display->ToString() | |
| 266 << ", display_mode=" << mode_info->ToString(); | |
| 267 } | |
| 268 size = mode_info->size(); | |
| 269 } | |
| 270 } | |
| 271 break; | |
| 272 } | |
| 273 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: { | |
| 274 if (displays.size() != 2 || | |
| 275 (num_on_displays != 0 && num_on_displays != 2)) { | |
| 276 LOG(WARNING) << "Ignoring request to enter mirrored mode with " | |
| 277 << displays.size() << " connected display(s) and " | |
| 278 << num_on_displays << " turned on"; | |
| 279 return false; | |
| 280 } | |
| 281 | |
| 282 const DisplayMode* mode_info = displays[0].mirror_mode; | |
| 283 if (!mode_info) { | |
| 284 LOG(WARNING) << "No mirror mode when configuring display: " | |
| 285 << displays[0].display->ToString(); | |
| 286 return false; | |
| 287 } | |
| 288 size = mode_info->size(); | |
| 289 | |
| 290 for (size_t i = 0; i < displays.size(); ++i) { | |
| 291 const DisplayConfigurator::DisplayState* state = &displays[i]; | |
| 292 (*requests)[i].mode = display_power[i] ? state->mirror_mode : NULL; | |
| 293 } | |
| 294 break; | |
| 295 } | |
| 296 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: | |
| 297 case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED: { | |
| 298 if ((new_display_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED && | |
| 299 displays.size() != 2) || | |
| 300 (new_display_state == MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED && | |
| 301 displays.size() <= 2) || | |
| 302 (num_on_displays != 0 && | |
| 303 num_on_displays != static_cast<int>(displays.size()))) { | |
| 304 LOG(WARNING) << "Ignoring request to enter extended mode with " | |
| 305 << displays.size() << " connected display(s) and " | |
| 306 << num_on_displays << " turned on"; | |
| 307 return false; | |
| 308 } | |
| 309 | |
| 310 for (size_t i = 0; i < displays.size(); ++i) { | |
| 311 const DisplayConfigurator::DisplayState* state = &displays[i]; | |
| 312 (*requests)[i].origin.set_y(size.height() ? size.height() + kVerticalGap | |
| 313 : 0); | |
| 314 (*requests)[i].mode = display_power[i] ? state->selected_mode : NULL; | |
| 315 | |
| 316 // Retain the full screen size even if all displays are off so the | |
| 317 // same desktop configuration can be restored when the displays are | |
| 318 // turned back on. | |
| 319 const DisplayMode* mode_info = displays[i].selected_mode; | |
| 320 if (!mode_info) { | |
| 321 LOG(WARNING) << "No selected mode when configuring display: " | |
| 322 << state->display->ToString(); | |
| 323 return false; | |
| 324 } | |
| 325 | |
| 326 size.set_width(std::max<int>(size.width(), mode_info->size().width())); | |
| 327 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) + | |
| 328 mode_info->size().height()); | |
| 329 } | |
| 330 break; | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 *framebuffer_size = size; | |
| 335 return true; | |
| 336 } | |
| 337 | |
| 338 bool DisplayConfigurator::DisplayLayoutManagerImpl::FindMirrorMode( | |
| 339 DisplayState* internal_display, | |
| 340 DisplayState* external_display, | |
| 341 bool try_panel_fitting, | |
| 342 bool preserve_aspect) const { | |
| 343 const DisplayMode* internal_native_info = | |
| 344 internal_display->display->native_mode(); | |
| 345 const DisplayMode* external_native_info = | |
| 346 external_display->display->native_mode(); | |
| 347 if (!internal_native_info || !external_native_info) | |
| 348 return false; | |
| 349 | |
| 350 // Check if some external display resolution can be mirrored on internal. | |
| 351 // Prefer the modes in the order they're present in DisplaySnapshot, assuming | |
| 352 // this is the order in which they look better on the monitor. | |
| 353 for (DisplayModeList::const_iterator external_it = | |
| 354 external_display->display->modes().begin(); | |
| 355 external_it != external_display->display->modes().end(); ++external_it) { | |
| 356 const DisplayMode& external_info = **external_it; | |
| 357 bool is_native_aspect_ratio = | |
| 358 external_native_info->size().width() * external_info.size().height() == | |
| 359 external_native_info->size().height() * external_info.size().width(); | |
| 360 if (preserve_aspect && !is_native_aspect_ratio) | |
| 361 continue; // Allow only aspect ratio preserving modes for mirroring. | |
| 362 | |
| 363 // Try finding an exact match. | |
| 364 for (DisplayModeList::const_iterator internal_it = | |
| 365 internal_display->display->modes().begin(); | |
| 366 internal_it != internal_display->display->modes().end(); | |
| 367 ++internal_it) { | |
| 368 const DisplayMode& internal_info = **internal_it; | |
| 369 if (internal_info.size().width() == external_info.size().width() && | |
| 370 internal_info.size().height() == external_info.size().height() && | |
| 371 internal_info.is_interlaced() == external_info.is_interlaced()) { | |
| 372 internal_display->mirror_mode = *internal_it; | |
| 373 external_display->mirror_mode = *external_it; | |
| 374 return true; // Mirror mode found. | |
| 375 } | |
| 376 } | |
| 377 | |
| 378 // Try to create a matching internal display mode by panel fitting. | |
| 379 if (try_panel_fitting) { | |
| 380 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks | |
| 381 // ugly, so, can fit == can upscale. Also, internal panels don't support | |
| 382 // fitting interlaced modes. | |
| 383 bool can_fit = internal_native_info->size().width() >= | |
| 384 external_info.size().width() && | |
| 385 internal_native_info->size().height() >= | |
| 386 external_info.size().height() && | |
| 387 !external_info.is_interlaced(); | |
| 388 if (can_fit) { | |
| 389 configurator_->native_display_delegate_->AddMode( | |
| 390 *internal_display->display, *external_it); | |
| 391 internal_display->display->add_mode(*external_it); | |
| 392 internal_display->mirror_mode = *external_it; | |
| 393 external_display->mirror_mode = *external_it; | |
| 394 return true; // Mirror mode created. | |
| 395 } | |
| 396 } | |
| 397 } | |
| 398 | |
| 399 return false; | |
| 400 } | |
| 401 | |
| 402 //////////////////////////////////////////////////////////////////////////////// | |
| 403 // DisplayConfigurator implementation | |
| 404 | |
| 57 // static | 405 // static |
| 58 const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize( | 406 const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize( |
| 59 const DisplaySnapshot& display, | 407 const DisplaySnapshot& display, |
| 60 const gfx::Size& size) { | 408 const gfx::Size& size) { |
| 61 const DisplayMode* best_mode = NULL; | 409 const DisplayMode* best_mode = NULL; |
| 62 for (DisplayModeList::const_iterator it = display.modes().begin(); | 410 for (DisplayModeList::const_iterator it = display.modes().begin(); |
| 63 it != display.modes().end(); | 411 it != display.modes().end(); |
| 64 ++it) { | 412 ++it) { |
| 65 const DisplayMode* mode = *it; | 413 const DisplayMode* mode = *it; |
| 66 | 414 |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 95 } | 443 } |
| 96 | 444 |
| 97 return best_mode; | 445 return best_mode; |
| 98 } | 446 } |
| 99 | 447 |
| 100 DisplayConfigurator::DisplayConfigurator() | 448 DisplayConfigurator::DisplayConfigurator() |
| 101 : state_controller_(NULL), | 449 : state_controller_(NULL), |
| 102 mirroring_controller_(NULL), | 450 mirroring_controller_(NULL), |
| 103 is_panel_fitting_enabled_(false), | 451 is_panel_fitting_enabled_(false), |
| 104 configure_display_(base::SysInfo::IsRunningOnChromeOS()), | 452 configure_display_(base::SysInfo::IsRunningOnChromeOS()), |
| 105 display_state_(MULTIPLE_DISPLAY_STATE_INVALID), | 453 current_display_state_(MULTIPLE_DISPLAY_STATE_INVALID), |
| 454 current_power_state_(chromeos::DISPLAY_POWER_ALL_ON), | |
| 455 requested_display_state_(MULTIPLE_DISPLAY_STATE_INVALID), | |
| 106 requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON), | 456 requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON), |
| 107 current_power_state_(chromeos::DISPLAY_POWER_ALL_ON), | 457 requested_power_state_change_(false), |
| 458 requested_power_flags_(kSetDisplayPowerNoFlags), | |
| 459 force_configure_(false), | |
| 108 next_display_protection_client_id_(1), | 460 next_display_protection_client_id_(1), |
| 109 display_externally_controlled_(false) { | 461 display_externally_controlled_(false), |
| 462 layout_manager_(new DisplayLayoutManagerImpl(this)), | |
| 463 weak_ptr_factory_(this) { | |
| 110 } | 464 } |
| 111 | 465 |
| 112 DisplayConfigurator::~DisplayConfigurator() { | 466 DisplayConfigurator::~DisplayConfigurator() { |
| 113 if (native_display_delegate_) | 467 if (native_display_delegate_) |
| 114 native_display_delegate_->RemoveObserver(this); | 468 native_display_delegate_->RemoveObserver(this); |
| 115 } | 469 } |
| 116 | 470 |
| 117 void DisplayConfigurator::SetDelegateForTesting( | 471 void DisplayConfigurator::SetDelegateForTesting( |
| 118 scoped_ptr<NativeDisplayDelegate> display_delegate) { | 472 scoped_ptr<NativeDisplayDelegate> display_delegate) { |
| 119 DCHECK(!native_display_delegate_); | 473 DCHECK(!native_display_delegate_); |
| 120 | 474 |
| 121 native_display_delegate_ = display_delegate.Pass(); | 475 native_display_delegate_ = display_delegate.Pass(); |
| 122 configure_display_ = true; | 476 configure_display_ = true; |
| 123 } | 477 } |
| 124 | 478 |
| 125 void DisplayConfigurator::SetInitialDisplayPower( | 479 void DisplayConfigurator::SetInitialDisplayPower( |
| 126 chromeos::DisplayPowerState power_state) { | 480 chromeos::DisplayPowerState power_state) { |
| 127 DCHECK_EQ(display_state_, MULTIPLE_DISPLAY_STATE_INVALID); | 481 DCHECK_EQ(current_display_state_, MULTIPLE_DISPLAY_STATE_INVALID); |
| 128 requested_power_state_ = current_power_state_ = power_state; | 482 requested_power_state_ = current_power_state_ = power_state; |
| 129 } | 483 } |
| 130 | 484 |
| 131 void DisplayConfigurator::Init(bool is_panel_fitting_enabled) { | 485 void DisplayConfigurator::Init(bool is_panel_fitting_enabled) { |
| 132 is_panel_fitting_enabled_ = is_panel_fitting_enabled; | 486 is_panel_fitting_enabled_ = is_panel_fitting_enabled; |
| 133 if (!configure_display_ || display_externally_controlled_) | 487 if (!configure_display_ || display_externally_controlled_) |
| 134 return; | 488 return; |
| 135 | 489 |
| 136 // If the delegate is already initialized don't update it (For example, tests | 490 // If the delegate is already initialized don't update it (For example, tests |
| 137 // set their own delegates). | 491 // set their own delegates). |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 166 | 520 |
| 167 display_externally_controlled_ = true; | 521 display_externally_controlled_ = true; |
| 168 native_display_delegate_->RelinquishDisplayControl(); | 522 native_display_delegate_->RelinquishDisplayControl(); |
| 169 } | 523 } |
| 170 | 524 |
| 171 void DisplayConfigurator::ForceInitialConfigure( | 525 void DisplayConfigurator::ForceInitialConfigure( |
| 172 uint32_t background_color_argb) { | 526 uint32_t background_color_argb) { |
| 173 if (!configure_display_ || display_externally_controlled_) | 527 if (!configure_display_ || display_externally_controlled_) |
| 174 return; | 528 return; |
| 175 | 529 |
| 176 native_display_delegate_->GrabServer(); | |
| 177 native_display_delegate_->Initialize(); | 530 native_display_delegate_->Initialize(); |
| 178 | 531 |
| 179 UpdateCachedDisplays(); | 532 // ForceInitialConfigure should be the first configuration so there shouldn't |
| 180 if (cached_displays_.size() > 1 && background_color_argb) | 533 // be anything scheduled. |
| 181 native_display_delegate_->SetBackgroundColor(background_color_argb); | 534 DCHECK(!configuration_task_); |
| 182 const MultipleDisplayState new_state = ChooseDisplayState( | |
| 183 requested_power_state_); | |
| 184 const bool success = EnterStateOrFallBackToSoftwareMirroring( | |
| 185 new_state, requested_power_state_); | |
| 186 | 535 |
| 187 // Force the DPMS on chrome startup as the driver doesn't always detect | 536 configuration_task_.reset(new UpdateDisplayConfigurationTask( |
| 188 // that all displays are on when signing out. | 537 native_display_delegate_.get(), layout_manager_.get(), |
| 189 native_display_delegate_->ForceDPMSOn(); | 538 requested_display_state_, requested_power_state_, |
| 190 native_display_delegate_->UngrabServer(); | 539 kSetDisplayPowerForceProbe, background_color_argb, true, |
| 191 NotifyObservers(success, new_state); | 540 base::Bind(&DisplayConfigurator::OnConfigured, |
| 541 weak_ptr_factory_.GetWeakPtr()))); | |
| 542 configuration_task_->Run(); | |
| 192 } | 543 } |
| 193 | 544 |
| 194 bool DisplayConfigurator::IsMirroring() const { | 545 bool DisplayConfigurator::IsMirroring() const { |
| 195 return display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR || | 546 return current_display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR || |
| 196 (mirroring_controller_ && | 547 (mirroring_controller_ && |
| 197 mirroring_controller_->SoftwareMirroringEnabled()); | 548 mirroring_controller_->SoftwareMirroringEnabled()); |
| 198 } | 549 } |
| 199 | 550 |
| 200 bool DisplayConfigurator::ApplyProtections(const ContentProtections& requests) { | 551 bool DisplayConfigurator::ApplyProtections(const ContentProtections& requests) { |
| 201 for (DisplayStateList::const_iterator it = cached_displays_.begin(); | 552 for (DisplayStateList::const_iterator it = cached_displays_.begin(); |
| 202 it != cached_displays_.end(); | 553 it != cached_displays_.end(); |
| 203 ++it) { | 554 ++it) { |
| 204 uint32_t all_desired = 0; | 555 uint32_t all_desired = 0; |
| 205 | 556 |
| 206 // In mirror mode, protection request of all displays need to be fulfilled. | 557 // In mirror mode, protection request of all displays need to be fulfilled. |
| 207 // In non-mirror mode, only request of client's display needs to be | 558 // In non-mirror mode, only request of client's display needs to be |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 409 } | 760 } |
| 410 } | 761 } |
| 411 | 762 |
| 412 return false; | 763 return false; |
| 413 } | 764 } |
| 414 | 765 |
| 415 void DisplayConfigurator::PrepareForExit() { | 766 void DisplayConfigurator::PrepareForExit() { |
| 416 configure_display_ = false; | 767 configure_display_ = false; |
| 417 } | 768 } |
| 418 | 769 |
| 419 bool DisplayConfigurator::SetDisplayPower( | 770 void DisplayConfigurator::SetDisplayPower( |
| 420 chromeos::DisplayPowerState power_state, | 771 chromeos::DisplayPowerState power_state, |
| 421 int flags) { | 772 int flags) { |
| 422 if (!configure_display_ || display_externally_controlled_) | 773 if (!configure_display_ || display_externally_controlled_) |
| 423 return false; | 774 return; |
| 424 | 775 |
| 425 VLOG(1) << "SetDisplayPower: power_state=" | 776 VLOG(1) << "SetDisplayPower: power_state=" |
| 426 << DisplayPowerStateToString(power_state) << " flags=" << flags | 777 << DisplayPowerStateToString(power_state) << " flags=" << flags |
| 427 << ", configure timer=" | 778 << ", configure timer=" |
| 428 << (configure_timer_.IsRunning() ? "Running" : "Stopped"); | 779 << (configure_timer_.IsRunning() ? "Running" : "Stopped"); |
| 429 if (power_state == current_power_state_ && | 780 if (power_state == requested_power_state_ && |
| 430 !(flags & kSetDisplayPowerForceProbe)) | 781 !(flags & kSetDisplayPowerForceProbe)) |
| 431 return true; | 782 return; |
| 432 | 783 |
| 433 native_display_delegate_->GrabServer(); | 784 requested_power_state_ = power_state; |
| 434 UpdateCachedDisplays(); | 785 requested_power_state_change_ = true; |
| 786 requested_power_flags_ = flags; | |
| 435 | 787 |
| 436 const MultipleDisplayState new_state = ChooseDisplayState(power_state); | 788 RunPendingConfiguration(); |
| 437 bool attempted_change = false; | |
| 438 bool success = false; | |
| 439 | |
| 440 bool only_if_single_internal_display = | |
| 441 flags & kSetDisplayPowerOnlyIfSingleInternalDisplay; | |
| 442 bool single_internal_display = | |
| 443 cached_displays_.size() == 1 && | |
| 444 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; | |
| 445 if (single_internal_display || !only_if_single_internal_display) { | |
| 446 success = EnterStateOrFallBackToSoftwareMirroring(new_state, power_state); | |
| 447 attempted_change = true; | |
| 448 | |
| 449 // Force the DPMS on since the driver doesn't always detect that it | |
| 450 // should turn on. This is needed when coming back from idle suspend. | |
| 451 if (success && power_state != chromeos::DISPLAY_POWER_ALL_OFF) | |
| 452 native_display_delegate_->ForceDPMSOn(); | |
| 453 } | |
| 454 | |
| 455 native_display_delegate_->UngrabServer(); | |
| 456 if (attempted_change) | |
| 457 NotifyObservers(success, new_state); | |
| 458 return success; | |
| 459 } | 789 } |
| 460 | 790 |
| 461 bool DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) { | 791 void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) { |
| 462 if (!configure_display_ || display_externally_controlled_) | 792 if (!configure_display_ || display_externally_controlled_) |
| 463 return false; | 793 return; |
| 464 | 794 |
| 465 VLOG(1) << "SetDisplayMode: state=" | 795 VLOG(1) << "SetDisplayMode: state=" |
| 466 << MultipleDisplayStateToString(new_state); | 796 << MultipleDisplayStateToString(new_state); |
| 467 if (display_state_ == new_state) { | 797 if (current_display_state_ == new_state) { |
| 468 // Cancel software mirroring if the state is moving from | 798 // Cancel software mirroring if the state is moving from |
| 469 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to | 799 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to |
| 470 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED. | 800 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED. |
| 471 if (mirroring_controller_ && | 801 if (mirroring_controller_ && |
| 472 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) | 802 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) |
| 473 mirroring_controller_->SetSoftwareMirroring(false); | 803 mirroring_controller_->SetSoftwareMirroring(false); |
| 474 NotifyObservers(true, new_state); | 804 NotifyObservers(true, new_state); |
| 475 return true; | 805 return; |
| 476 } | 806 } |
| 477 | 807 |
| 478 native_display_delegate_->GrabServer(); | 808 requested_display_state_ = new_state; |
| 479 UpdateCachedDisplays(); | |
| 480 const bool success = EnterStateOrFallBackToSoftwareMirroring( | |
| 481 new_state, requested_power_state_); | |
| 482 native_display_delegate_->UngrabServer(); | |
| 483 | 809 |
| 484 NotifyObservers(success, new_state); | 810 RunPendingConfiguration(); |
| 485 return success; | |
| 486 } | 811 } |
| 487 | 812 |
| 488 void DisplayConfigurator::OnConfigurationChanged() { | 813 void DisplayConfigurator::OnConfigurationChanged() { |
| 489 // Configure displays with |kConfigureDelayMs| delay, | 814 // Configure displays with |kConfigureDelayMs| delay, |
| 490 // so that time-consuming ConfigureDisplays() won't be called multiple times. | 815 // so that time-consuming ConfigureDisplays() won't be called multiple times. |
| 491 if (configure_timer_.IsRunning()) { | 816 if (configure_timer_.IsRunning()) { |
| 492 // Note: when the timer is running it is possible that a different task | 817 // Note: when the timer is running it is possible that a different task |
| 493 // (RestoreRequestedPowerStateAfterResume()) is scheduled. In these cases, | 818 // (RestoreRequestedPowerStateAfterResume()) is scheduled. In these cases, |
| 494 // prefer the already scheduled task to ConfigureDisplays() since | 819 // prefer the already scheduled task to ConfigureDisplays() since |
| 495 // ConfigureDisplays() performs only basic configuration while | 820 // ConfigureDisplays() performs only basic configuration while |
| (...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 531 } | 856 } |
| 532 | 857 |
| 533 void DisplayConfigurator::ResumeDisplays() { | 858 void DisplayConfigurator::ResumeDisplays() { |
| 534 configure_timer_.Start( | 859 configure_timer_.Start( |
| 535 FROM_HERE, | 860 FROM_HERE, |
| 536 base::TimeDelta::FromMilliseconds(kResumeDelayMs), | 861 base::TimeDelta::FromMilliseconds(kResumeDelayMs), |
| 537 base::Bind(&DisplayConfigurator::RestoreRequestedPowerStateAfterResume, | 862 base::Bind(&DisplayConfigurator::RestoreRequestedPowerStateAfterResume, |
| 538 base::Unretained(this))); | 863 base::Unretained(this))); |
| 539 } | 864 } |
| 540 | 865 |
| 541 void DisplayConfigurator::UpdateCachedDisplays() { | |
| 542 std::vector<DisplaySnapshot*> snapshots = | |
| 543 native_display_delegate_->GetDisplays(); | |
| 544 | |
| 545 cached_displays_.clear(); | |
| 546 for (size_t i = 0; i < snapshots.size(); ++i) { | |
| 547 DisplayState display_state; | |
| 548 display_state.display = snapshots[i]; | |
| 549 cached_displays_.push_back(display_state); | |
| 550 } | |
| 551 | |
| 552 // Set |selected_mode| fields. | |
| 553 for (size_t i = 0; i < cached_displays_.size(); ++i) { | |
| 554 DisplayState* display_state = &cached_displays_[i]; | |
| 555 gfx::Size size; | |
| 556 if (state_controller_ && | |
| 557 state_controller_->GetResolutionForDisplayId( | |
| 558 display_state->display->display_id(), &size)) { | |
| 559 display_state->selected_mode = | |
| 560 FindDisplayModeMatchingSize(*display_state->display, size); | |
| 561 } | |
| 562 | |
| 563 // Fall back to native mode. | |
| 564 if (!display_state->selected_mode) | |
| 565 display_state->selected_mode = display_state->display->native_mode(); | |
| 566 } | |
| 567 | |
| 568 // Set |mirror_mode| fields. | |
| 569 if (cached_displays_.size() == 2) { | |
| 570 bool one_is_internal = | |
| 571 cached_displays_[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; | |
| 572 bool two_is_internal = | |
| 573 cached_displays_[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; | |
| 574 int internal_displays = | |
| 575 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0); | |
| 576 DCHECK_LT(internal_displays, 2); | |
| 577 LOG_IF(WARNING, internal_displays == 2) | |
| 578 << "Two internal displays detected."; | |
| 579 | |
| 580 bool can_mirror = false; | |
| 581 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { | |
| 582 // Try preserving external display's aspect ratio on the first attempt. | |
| 583 // If that fails, fall back to the highest matching resolution. | |
| 584 bool preserve_aspect = attempt == 0; | |
| 585 | |
| 586 if (internal_displays == 1) { | |
| 587 if (one_is_internal) { | |
| 588 can_mirror = FindMirrorMode(&cached_displays_[0], | |
| 589 &cached_displays_[1], | |
| 590 is_panel_fitting_enabled_, | |
| 591 preserve_aspect); | |
| 592 } else { | |
| 593 DCHECK(two_is_internal); | |
| 594 can_mirror = FindMirrorMode(&cached_displays_[1], | |
| 595 &cached_displays_[0], | |
| 596 is_panel_fitting_enabled_, | |
| 597 preserve_aspect); | |
| 598 } | |
| 599 } else { // if (internal_displays == 0) | |
| 600 // No panel fitting for external displays, so fall back to exact match. | |
| 601 can_mirror = FindMirrorMode( | |
| 602 &cached_displays_[0], &cached_displays_[1], false, preserve_aspect); | |
| 603 if (!can_mirror && preserve_aspect) { | |
| 604 // FindMirrorMode() will try to preserve aspect ratio of what it | |
| 605 // thinks is external display, so if it didn't succeed with one, maybe | |
| 606 // it will succeed with the other. This way we will have the correct | |
| 607 // aspect ratio on at least one of them. | |
| 608 can_mirror = FindMirrorMode(&cached_displays_[1], | |
| 609 &cached_displays_[0], | |
| 610 false, | |
| 611 preserve_aspect); | |
| 612 } | |
| 613 } | |
| 614 } | |
| 615 } | |
| 616 } | |
| 617 | |
| 618 bool DisplayConfigurator::FindMirrorMode(DisplayState* internal_display, | |
| 619 DisplayState* external_display, | |
| 620 bool try_panel_fitting, | |
| 621 bool preserve_aspect) { | |
| 622 const DisplayMode* internal_native_info = | |
| 623 internal_display->display->native_mode(); | |
| 624 const DisplayMode* external_native_info = | |
| 625 external_display->display->native_mode(); | |
| 626 if (!internal_native_info || !external_native_info) | |
| 627 return false; | |
| 628 | |
| 629 // Check if some external display resolution can be mirrored on internal. | |
| 630 // Prefer the modes in the order they're present in DisplaySnapshot, assuming | |
| 631 // this is the order in which they look better on the monitor. | |
| 632 for (DisplayModeList::const_iterator external_it = | |
| 633 external_display->display->modes().begin(); | |
| 634 external_it != external_display->display->modes().end(); | |
| 635 ++external_it) { | |
| 636 const DisplayMode& external_info = **external_it; | |
| 637 bool is_native_aspect_ratio = | |
| 638 external_native_info->size().width() * external_info.size().height() == | |
| 639 external_native_info->size().height() * external_info.size().width(); | |
| 640 if (preserve_aspect && !is_native_aspect_ratio) | |
| 641 continue; // Allow only aspect ratio preserving modes for mirroring. | |
| 642 | |
| 643 // Try finding an exact match. | |
| 644 for (DisplayModeList::const_iterator internal_it = | |
| 645 internal_display->display->modes().begin(); | |
| 646 internal_it != internal_display->display->modes().end(); | |
| 647 ++internal_it) { | |
| 648 const DisplayMode& internal_info = **internal_it; | |
| 649 if (internal_info.size().width() == external_info.size().width() && | |
| 650 internal_info.size().height() == external_info.size().height() && | |
| 651 internal_info.is_interlaced() == external_info.is_interlaced()) { | |
| 652 internal_display->mirror_mode = *internal_it; | |
| 653 external_display->mirror_mode = *external_it; | |
| 654 return true; // Mirror mode found. | |
| 655 } | |
| 656 } | |
| 657 | |
| 658 // Try to create a matching internal display mode by panel fitting. | |
| 659 if (try_panel_fitting) { | |
| 660 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks | |
| 661 // ugly, so, can fit == can upscale. Also, internal panels don't support | |
| 662 // fitting interlaced modes. | |
| 663 bool can_fit = internal_native_info->size().width() >= | |
| 664 external_info.size().width() && | |
| 665 internal_native_info->size().height() >= | |
| 666 external_info.size().height() && | |
| 667 !external_info.is_interlaced(); | |
| 668 if (can_fit) { | |
| 669 native_display_delegate_->AddMode(*internal_display->display, | |
| 670 *external_it); | |
| 671 internal_display->display->add_mode(*external_it); | |
| 672 internal_display->mirror_mode = *external_it; | |
| 673 external_display->mirror_mode = *external_it; | |
| 674 return true; // Mirror mode created. | |
| 675 } | |
| 676 } | |
| 677 } | |
| 678 | |
| 679 return false; | |
| 680 } | |
| 681 | |
| 682 void DisplayConfigurator::ConfigureDisplays() { | 866 void DisplayConfigurator::ConfigureDisplays() { |
| 683 if (!configure_display_ || display_externally_controlled_) | 867 if (!configure_display_ || display_externally_controlled_) |
| 684 return; | 868 return; |
| 685 | 869 |
| 686 native_display_delegate_->GrabServer(); | 870 force_configure_ = true; |
| 687 UpdateCachedDisplays(); | 871 RunPendingConfiguration(); |
| 688 const MultipleDisplayState new_state = ChooseDisplayState( | 872 } |
| 689 requested_power_state_); | |
| 690 const bool success = EnterStateOrFallBackToSoftwareMirroring( | |
| 691 new_state, requested_power_state_); | |
| 692 native_display_delegate_->UngrabServer(); | |
| 693 | 873 |
| 694 NotifyObservers(success, new_state); | 874 void DisplayConfigurator::RunPendingConfiguration() { |
| 875 // Configuration task is currently running. Do not start a second | |
| 876 // configuration. | |
| 877 if (configuration_task_) | |
| 878 return; | |
| 879 | |
|
Daniel Erat
2014/12/12 16:25:29
nit: check that the requested_ fields don't have t
dnicoara
2014/12/12 17:45:09
I've slightly changed ShouldRunConfigurationTask()
| |
| 880 configuration_task_.reset(new UpdateDisplayConfigurationTask( | |
| 881 native_display_delegate_.get(), layout_manager_.get(), | |
| 882 requested_display_state_, requested_power_state_, requested_power_flags_, | |
| 883 0, force_configure_, base::Bind(&DisplayConfigurator::OnConfigured, | |
| 884 weak_ptr_factory_.GetWeakPtr()))); | |
| 885 | |
| 886 // Reset the flags before running the task otherwise it may end up scheduling | |
|
Daniel Erat
2014/12/12 16:25:29
nit: "... before running the task; otherwise ..."
dnicoara
2014/12/12 17:45:09
Done.
| |
| 887 // another configuration. | |
| 888 force_configure_ = false; | |
| 889 requested_power_flags_ = kSetDisplayPowerNoFlags; | |
| 890 requested_power_state_change_ = false; | |
| 891 requested_display_state_ = MULTIPLE_DISPLAY_STATE_INVALID; | |
| 892 | |
| 893 configuration_task_->Run(); | |
| 894 } | |
| 895 | |
| 896 void DisplayConfigurator::OnConfigured( | |
| 897 bool success, | |
| 898 const std::vector<DisplayState>& displays, | |
| 899 const gfx::Size& framebuffer_size, | |
| 900 MultipleDisplayState new_display_state, | |
| 901 chromeos::DisplayPowerState new_power_state) { | |
| 902 VLOG(1) << "OnConfigured: success=" << success << " new_display_state=" | |
| 903 << MultipleDisplayStateToString(new_display_state) | |
| 904 << " new_power_state=" << DisplayPowerStateToString(new_power_state); | |
| 905 | |
| 906 cached_displays_ = displays; | |
| 907 if (success) { | |
| 908 current_display_state_ = new_display_state; | |
| 909 current_power_state_ = new_power_state; | |
| 910 framebuffer_size_ = framebuffer_size; | |
| 911 // If the requested power state hasn't changed then make sure that value | |
| 912 // gets updated as well since the last requested value may have been | |
| 913 // dependent on certain conditions (ie: if only the internal monitor was | |
| 914 // present). | |
| 915 if (!requested_power_state_change_) | |
| 916 requested_power_state_ = new_power_state; | |
| 917 } | |
| 918 | |
| 919 configuration_task_.reset(); | |
| 920 NotifyObservers(success, new_display_state); | |
| 921 | |
| 922 if (success && ShouldScheduleConfigurationTask()) { | |
| 923 configure_timer_.Start(FROM_HERE, | |
| 924 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), | |
| 925 this, &DisplayConfigurator::RunPendingConfiguration); | |
| 926 } | |
| 927 } | |
| 928 | |
| 929 bool DisplayConfigurator::ShouldScheduleConfigurationTask() const { | |
| 930 // A task is already scheduled, no need to schedule again. | |
| 931 if (configure_timer_.IsRunning()) | |
| 932 return false; | |
| 933 | |
| 934 if (force_configure_) | |
| 935 return true; | |
| 936 | |
| 937 // Schedule if there is a request to change the display state. | |
| 938 if (requested_display_state_ != current_display_state_ && | |
| 939 requested_display_state_ != MULTIPLE_DISPLAY_STATE_INVALID) | |
| 940 return true; | |
| 941 | |
| 942 // Schedule if there is a request to change the power state. | |
| 943 if (current_power_state_ != requested_power_state_) | |
| 944 return true; | |
| 945 | |
| 946 return false; | |
| 695 } | 947 } |
| 696 | 948 |
| 697 void DisplayConfigurator::RestoreRequestedPowerStateAfterResume() { | 949 void DisplayConfigurator::RestoreRequestedPowerStateAfterResume() { |
| 698 // Force probing to ensure that we pick up any changes that were made while | 950 // Force probing to ensure that we pick up any changes that were made while |
| 699 // the system was suspended. | 951 // the system was suspended. |
| 700 SetDisplayPower(requested_power_state_, kSetDisplayPowerForceProbe); | 952 SetDisplayPower(requested_power_state_, kSetDisplayPowerForceProbe); |
| 701 } | 953 } |
| 702 | 954 |
| 703 void DisplayConfigurator::NotifyObservers( | 955 void DisplayConfigurator::NotifyObservers( |
| 704 bool success, | 956 bool success, |
| 705 MultipleDisplayState attempted_state) { | 957 MultipleDisplayState attempted_state) { |
| 706 if (success) { | 958 if (success) { |
| 707 FOR_EACH_OBSERVER( | 959 FOR_EACH_OBSERVER( |
| 708 Observer, observers_, OnDisplayModeChanged(cached_displays_)); | 960 Observer, observers_, OnDisplayModeChanged(cached_displays_)); |
| 709 } else { | 961 } else { |
| 710 FOR_EACH_OBSERVER( | 962 FOR_EACH_OBSERVER( |
| 711 Observer, observers_, OnDisplayModeChangeFailed(attempted_state)); | 963 Observer, observers_, OnDisplayModeChangeFailed(attempted_state)); |
| 712 } | 964 } |
| 713 } | 965 } |
| 714 | 966 |
| 715 bool DisplayConfigurator::EnterStateOrFallBackToSoftwareMirroring( | |
| 716 MultipleDisplayState display_state, | |
| 717 chromeos::DisplayPowerState power_state) { | |
| 718 bool success = EnterState(display_state, power_state); | |
| 719 if (mirroring_controller_) { | |
| 720 bool enable_software_mirroring = false; | |
| 721 if (!success && display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) { | |
| 722 if (display_state_ != MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED || | |
| 723 current_power_state_ != power_state) | |
| 724 EnterState(MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED, power_state); | |
| 725 enable_software_mirroring = success = | |
| 726 display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED; | |
| 727 } | |
| 728 mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring); | |
| 729 } | |
| 730 return success; | |
| 731 } | |
| 732 | |
| 733 bool DisplayConfigurator::EnterState(MultipleDisplayState display_state, | |
| 734 chromeos::DisplayPowerState power_state) { | |
| 735 std::vector<bool> display_power; | |
| 736 int num_on_displays = | |
| 737 GetDisplayPower(cached_displays_, power_state, &display_power); | |
| 738 VLOG(1) << "EnterState: display=" | |
| 739 << MultipleDisplayStateToString(display_state) | |
| 740 << " power=" << DisplayPowerStateToString(power_state); | |
| 741 | |
| 742 // Save the requested state so we'll try to use it next time even if we fail. | |
| 743 requested_power_state_ = power_state; | |
| 744 | |
| 745 // Framebuffer dimensions. | |
| 746 gfx::Size size; | |
| 747 | |
| 748 std::vector<gfx::Point> new_origins(cached_displays_.size(), gfx::Point()); | |
| 749 std::vector<const DisplayMode*> new_mode; | |
| 750 for (size_t i = 0; i < cached_displays_.size(); ++i) | |
| 751 new_mode.push_back(cached_displays_[i].display->current_mode()); | |
| 752 | |
| 753 switch (display_state) { | |
| 754 case MULTIPLE_DISPLAY_STATE_INVALID: | |
| 755 NOTREACHED() << "Ignoring request to enter invalid state with " | |
| 756 << cached_displays_.size() << " connected display(s)"; | |
| 757 return false; | |
| 758 case MULTIPLE_DISPLAY_STATE_HEADLESS: | |
| 759 if (cached_displays_.size() != 0) { | |
| 760 LOG(WARNING) << "Ignoring request to enter headless mode with " | |
| 761 << cached_displays_.size() << " connected display(s)"; | |
| 762 return false; | |
| 763 } | |
| 764 break; | |
| 765 case MULTIPLE_DISPLAY_STATE_SINGLE: { | |
| 766 // If there are multiple displays connected, only one should be turned on. | |
| 767 if (cached_displays_.size() != 1 && num_on_displays != 1) { | |
| 768 LOG(WARNING) << "Ignoring request to enter single mode with " | |
| 769 << cached_displays_.size() << " connected displays and " | |
| 770 << num_on_displays << " turned on"; | |
| 771 return false; | |
| 772 } | |
| 773 | |
| 774 for (size_t i = 0; i < cached_displays_.size(); ++i) { | |
| 775 DisplayState* state = &cached_displays_[i]; | |
| 776 new_mode[i] = display_power[i] ? state->selected_mode : NULL; | |
| 777 | |
| 778 if (display_power[i] || cached_displays_.size() == 1) { | |
| 779 const DisplayMode* mode_info = state->selected_mode; | |
| 780 if (!mode_info) { | |
| 781 LOG(WARNING) << "No selected mode when configuring display: " | |
| 782 << state->display->ToString(); | |
| 783 return false; | |
| 784 } | |
| 785 if (mode_info->size() == gfx::Size(1024, 768)) { | |
| 786 VLOG(1) << "Potentially misdetecting display(1024x768):" | |
| 787 << " displays size=" << cached_displays_.size() | |
| 788 << ", num_on_displays=" << num_on_displays | |
| 789 << ", current size:" << size.width() << "x" << size.height() | |
| 790 << ", i=" << i << ", display=" << state->display->ToString() | |
| 791 << ", display_mode=" << mode_info->ToString(); | |
| 792 } | |
| 793 size = mode_info->size(); | |
| 794 } | |
| 795 } | |
| 796 break; | |
| 797 } | |
| 798 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: { | |
| 799 if (cached_displays_.size() != 2 || | |
| 800 (num_on_displays != 0 && num_on_displays != 2)) { | |
| 801 LOG(WARNING) << "Ignoring request to enter mirrored mode with " | |
| 802 << cached_displays_.size() << " connected display(s) and " | |
| 803 << num_on_displays << " turned on"; | |
| 804 return false; | |
| 805 } | |
| 806 | |
| 807 const DisplayMode* mode_info = cached_displays_[0].mirror_mode; | |
| 808 if (!mode_info) { | |
| 809 LOG(WARNING) << "No mirror mode when configuring display: " | |
| 810 << cached_displays_[0].display->ToString(); | |
| 811 return false; | |
| 812 } | |
| 813 size = mode_info->size(); | |
| 814 | |
| 815 for (size_t i = 0; i < cached_displays_.size(); ++i) { | |
| 816 DisplayState* state = &cached_displays_[i]; | |
| 817 new_mode[i] = display_power[i] ? state->mirror_mode : NULL; | |
| 818 } | |
| 819 break; | |
| 820 } | |
| 821 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: | |
| 822 case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED: { | |
| 823 if ((display_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED && | |
| 824 cached_displays_.size() != 2) || | |
| 825 (display_state == MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED && | |
| 826 cached_displays_.size() <= 2) || | |
| 827 (num_on_displays != 0 && | |
| 828 num_on_displays != static_cast<int>(cached_displays_.size()))) { | |
| 829 LOG(WARNING) << "Ignoring request to enter extended mode with " | |
| 830 << cached_displays_.size() << " connected display(s) and " | |
| 831 << num_on_displays << " turned on"; | |
| 832 return false; | |
| 833 } | |
| 834 | |
| 835 for (size_t i = 0; i < cached_displays_.size(); ++i) { | |
| 836 DisplayState* state = &cached_displays_[i]; | |
| 837 new_origins[i].set_y(size.height() ? size.height() + kVerticalGap : 0); | |
| 838 new_mode[i] = display_power[i] ? state->selected_mode : NULL; | |
| 839 | |
| 840 // Retain the full screen size even if all displays are off so the | |
| 841 // same desktop configuration can be restored when the displays are | |
| 842 // turned back on. | |
| 843 const DisplayMode* mode_info = cached_displays_[i].selected_mode; | |
| 844 if (!mode_info) { | |
| 845 LOG(WARNING) << "No selected mode when configuring display: " | |
| 846 << state->display->ToString(); | |
| 847 return false; | |
| 848 } | |
| 849 | |
| 850 size.set_width(std::max<int>(size.width(), mode_info->size().width())); | |
| 851 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) + | |
| 852 mode_info->size().height()); | |
| 853 } | |
| 854 break; | |
| 855 } | |
| 856 } | |
| 857 | |
| 858 // Finally, apply the desired changes. | |
| 859 bool all_succeeded = true; | |
| 860 if (!cached_displays_.empty()) { | |
| 861 native_display_delegate_->CreateFrameBuffer(size); | |
| 862 for (size_t i = 0; i < cached_displays_.size(); ++i) { | |
| 863 const DisplayState& state = cached_displays_[i]; | |
| 864 bool configure_succeeded = false; | |
| 865 | |
| 866 while (true) { | |
| 867 if (native_display_delegate_->Configure( | |
| 868 *state.display, new_mode[i], new_origins[i])) { | |
| 869 state.display->set_current_mode(new_mode[i]); | |
| 870 state.display->set_origin(new_origins[i]); | |
| 871 | |
| 872 configure_succeeded = true; | |
| 873 break; | |
| 874 } | |
| 875 | |
| 876 const DisplayMode* mode_info = new_mode[i]; | |
| 877 if (!mode_info) | |
| 878 break; | |
| 879 | |
| 880 // Find the mode with the next-best resolution and see if that can | |
| 881 // be set. | |
| 882 int best_mode_pixels = 0; | |
| 883 | |
| 884 int current_mode_pixels = mode_info->size().GetArea(); | |
| 885 for (DisplayModeList::const_iterator it = | |
| 886 state.display->modes().begin(); | |
| 887 it != state.display->modes().end(); | |
| 888 it++) { | |
| 889 int pixel_count = (*it)->size().GetArea(); | |
| 890 if ((pixel_count < current_mode_pixels) && | |
| 891 (pixel_count > best_mode_pixels)) { | |
| 892 new_mode[i] = *it; | |
| 893 best_mode_pixels = pixel_count; | |
| 894 } | |
| 895 } | |
| 896 | |
| 897 if (best_mode_pixels == 0) | |
| 898 break; | |
| 899 } | |
| 900 | |
| 901 if (!configure_succeeded) | |
| 902 all_succeeded = false; | |
| 903 | |
| 904 // If we are trying to set mirror mode and one of the modesets fails, | |
| 905 // then the two monitors will be mis-matched. In this case, return | |
| 906 // false to let the observers be aware. | |
| 907 if (display_state == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR && | |
| 908 display_power[i] && | |
| 909 state.display->current_mode() != state.mirror_mode) | |
| 910 all_succeeded = false; | |
| 911 } | |
| 912 } | |
| 913 | |
| 914 if (all_succeeded) { | |
| 915 display_state_ = display_state; | |
| 916 current_power_state_ = power_state; | |
| 917 framebuffer_size_ = size; | |
| 918 } | |
| 919 return all_succeeded; | |
| 920 } | |
| 921 | |
| 922 MultipleDisplayState DisplayConfigurator::ChooseDisplayState( | |
| 923 chromeos::DisplayPowerState power_state) const { | |
| 924 int num_on_displays = GetDisplayPower(cached_displays_, power_state, NULL); | |
| 925 switch (cached_displays_.size()) { | |
| 926 case 0: | |
| 927 return MULTIPLE_DISPLAY_STATE_HEADLESS; | |
| 928 case 1: | |
| 929 return MULTIPLE_DISPLAY_STATE_SINGLE; | |
| 930 default: { | |
| 931 if (num_on_displays == 1) { | |
| 932 // If only one display is currently turned on, return the "single" | |
| 933 // state so that its native mode will be used. | |
| 934 return MULTIPLE_DISPLAY_STATE_SINGLE; | |
| 935 } if (num_on_displays >= 3) { | |
| 936 return MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED; | |
| 937 } else if (cached_displays_.size() == 2) { | |
| 938 if (!state_controller_) | |
| 939 return MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED; | |
| 940 // With either both displays on or both displays off, use one of the | |
| 941 // dual modes. | |
| 942 std::vector<int64_t> display_ids; | |
| 943 for (size_t i = 0; i < cached_displays_.size(); ++i) | |
| 944 display_ids.push_back(cached_displays_[i].display->display_id()); | |
| 945 | |
| 946 return state_controller_->GetStateForDisplayIds(display_ids); | |
| 947 } | |
| 948 NOTREACHED(); | |
| 949 } | |
| 950 } | |
| 951 return MULTIPLE_DISPLAY_STATE_INVALID; | |
| 952 } | |
| 953 | |
| 954 } // namespace ui | 967 } // namespace ui |
| OLD | NEW |