| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 #include "ui/display/chromeos/display_configurator.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 #include <utility> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/command_line.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/macros.h" | |
| 14 #include "base/sys_info.h" | |
| 15 #include "base/time/time.h" | |
| 16 #include "ui/display/chromeos/apply_content_protection_task.h" | |
| 17 #include "ui/display/chromeos/display_layout_manager.h" | |
| 18 #include "ui/display/chromeos/display_snapshot_virtual.h" | |
| 19 #include "ui/display/chromeos/display_util.h" | |
| 20 #include "ui/display/chromeos/update_display_configuration_task.h" | |
| 21 #include "ui/display/display.h" | |
| 22 #include "ui/display/display_switches.h" | |
| 23 #include "ui/display/types/display_mode.h" | |
| 24 #include "ui/display/types/display_snapshot.h" | |
| 25 #include "ui/display/types/native_display_delegate.h" | |
| 26 #include "ui/display/util/display_util.h" | |
| 27 | |
| 28 namespace ui { | |
| 29 | |
| 30 namespace { | |
| 31 | |
| 32 typedef std::vector<const DisplayMode*> DisplayModeList; | |
| 33 | |
| 34 // The EDID specification marks the top bit of the manufacturer id as reserved. | |
| 35 const int16_t kReservedManufacturerID = static_cast<int16_t>(1 << 15); | |
| 36 | |
| 37 struct DisplayState { | |
| 38 DisplaySnapshot* display = nullptr; // Not owned. | |
| 39 | |
| 40 // User-selected mode for the display. | |
| 41 const DisplayMode* selected_mode = nullptr; | |
| 42 | |
| 43 // Mode used when displaying the same desktop on multiple displays. | |
| 44 const DisplayMode* mirror_mode = nullptr; | |
| 45 }; | |
| 46 | |
| 47 void DoNothing(bool status) { | |
| 48 } | |
| 49 | |
| 50 } // namespace | |
| 51 | |
| 52 const int DisplayConfigurator::kSetDisplayPowerNoFlags = 0; | |
| 53 const int DisplayConfigurator::kSetDisplayPowerForceProbe = 1 << 0; | |
| 54 const int | |
| 55 DisplayConfigurator::kSetDisplayPowerOnlyIfSingleInternalDisplay = 1 << 1; | |
| 56 | |
| 57 bool DisplayConfigurator::TestApi::TriggerConfigureTimeout() { | |
| 58 if (configurator_->configure_timer_.IsRunning()) { | |
| 59 configurator_->configure_timer_.user_task().Run(); | |
| 60 configurator_->configure_timer_.Stop(); | |
| 61 return true; | |
| 62 } else { | |
| 63 return false; | |
| 64 } | |
| 65 } | |
| 66 | |
| 67 base::TimeDelta DisplayConfigurator::TestApi::GetConfigureDelay() const { | |
| 68 return configurator_->configure_timer_.IsRunning() | |
| 69 ? configurator_->configure_timer_.GetCurrentDelay() | |
| 70 : base::TimeDelta(); | |
| 71 } | |
| 72 | |
| 73 //////////////////////////////////////////////////////////////////////////////// | |
| 74 // DisplayConfigurator::DisplayLayoutManagerImpl implementation | |
| 75 | |
| 76 class DisplayConfigurator::DisplayLayoutManagerImpl | |
| 77 : public DisplayLayoutManager { | |
| 78 public: | |
| 79 DisplayLayoutManagerImpl(DisplayConfigurator* configurator); | |
| 80 ~DisplayLayoutManagerImpl() override; | |
| 81 | |
| 82 // DisplayConfigurator::DisplayLayoutManager: | |
| 83 SoftwareMirroringController* GetSoftwareMirroringController() const override; | |
| 84 StateController* GetStateController() const override; | |
| 85 MultipleDisplayState GetDisplayState() const override; | |
| 86 chromeos::DisplayPowerState GetPowerState() const override; | |
| 87 bool GetDisplayLayout(const std::vector<DisplaySnapshot*>& displays, | |
| 88 MultipleDisplayState new_display_state, | |
| 89 chromeos::DisplayPowerState new_power_state, | |
| 90 std::vector<DisplayConfigureRequest>* requests, | |
| 91 gfx::Size* framebuffer_size) const override; | |
| 92 DisplayStateList GetDisplayStates() const override; | |
| 93 bool IsMirroring() const override; | |
| 94 | |
| 95 private: | |
| 96 // Parses the |displays| into a list of DisplayStates. This effectively adds | |
| 97 // |mirror_mode| and |selected_mode| to the returned results. | |
| 98 // TODO(dnicoara): Break this into GetSelectedMode() and GetMirrorMode() and | |
| 99 // remove DisplayState. | |
| 100 std::vector<DisplayState> ParseDisplays( | |
| 101 const std::vector<DisplaySnapshot*>& displays) const; | |
| 102 | |
| 103 const DisplayMode* GetUserSelectedMode(const DisplaySnapshot& display) const; | |
| 104 | |
| 105 // Helper method for ParseDisplays() that initializes the passed-in | |
| 106 // displays' |mirror_mode| fields by looking for a mode in |internal_display| | |
| 107 // and |external_display| having the same resolution. Returns false if a | |
| 108 // shared mode wasn't found or created. | |
| 109 // | |
| 110 // |try_panel_fitting| allows creating a panel-fitting mode for | |
| 111 // |internal_display| instead of only searching for a matching mode (note that | |
| 112 // it may lead to a crash if |internal_display| is not capable of panel | |
| 113 // fitting). | |
| 114 // | |
| 115 // |preserve_aspect| limits the search/creation only to the modes having the | |
| 116 // native aspect ratio of |external_display|. | |
| 117 bool FindMirrorMode(DisplayState* internal_display, | |
| 118 DisplayState* external_display, | |
| 119 bool try_panel_fitting, | |
| 120 bool preserve_aspect) const; | |
| 121 | |
| 122 DisplayConfigurator* configurator_; // Not owned. | |
| 123 | |
| 124 DISALLOW_COPY_AND_ASSIGN(DisplayLayoutManagerImpl); | |
| 125 }; | |
| 126 | |
| 127 DisplayConfigurator::DisplayLayoutManagerImpl::DisplayLayoutManagerImpl( | |
| 128 DisplayConfigurator* configurator) | |
| 129 : configurator_(configurator) { | |
| 130 } | |
| 131 | |
| 132 DisplayConfigurator::DisplayLayoutManagerImpl::~DisplayLayoutManagerImpl() { | |
| 133 } | |
| 134 | |
| 135 DisplayConfigurator::SoftwareMirroringController* | |
| 136 DisplayConfigurator::DisplayLayoutManagerImpl::GetSoftwareMirroringController() | |
| 137 const { | |
| 138 return configurator_->mirroring_controller_; | |
| 139 } | |
| 140 | |
| 141 DisplayConfigurator::StateController* | |
| 142 DisplayConfigurator::DisplayLayoutManagerImpl::GetStateController() const { | |
| 143 return configurator_->state_controller_; | |
| 144 } | |
| 145 | |
| 146 MultipleDisplayState | |
| 147 DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayState() const { | |
| 148 return configurator_->current_display_state_; | |
| 149 } | |
| 150 | |
| 151 chromeos::DisplayPowerState | |
| 152 DisplayConfigurator::DisplayLayoutManagerImpl::GetPowerState() const { | |
| 153 return configurator_->current_power_state_; | |
| 154 } | |
| 155 | |
| 156 std::vector<DisplayState> | |
| 157 DisplayConfigurator::DisplayLayoutManagerImpl::ParseDisplays( | |
| 158 const std::vector<DisplaySnapshot*>& snapshots) const { | |
| 159 std::vector<DisplayState> cached_displays; | |
| 160 for (auto* snapshot : snapshots) { | |
| 161 DisplayState display_state; | |
| 162 display_state.display = snapshot; | |
| 163 display_state.selected_mode = GetUserSelectedMode(*snapshot); | |
| 164 cached_displays.push_back(display_state); | |
| 165 } | |
| 166 | |
| 167 // Set |mirror_mode| fields. | |
| 168 if (cached_displays.size() == 2) { | |
| 169 bool one_is_internal = | |
| 170 cached_displays[0].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; | |
| 171 bool two_is_internal = | |
| 172 cached_displays[1].display->type() == DISPLAY_CONNECTION_TYPE_INTERNAL; | |
| 173 int internal_displays = | |
| 174 (one_is_internal ? 1 : 0) + (two_is_internal ? 1 : 0); | |
| 175 DCHECK_LT(internal_displays, 2); | |
| 176 LOG_IF(WARNING, internal_displays >= 2) | |
| 177 << "At least two internal displays detected."; | |
| 178 | |
| 179 bool can_mirror = false; | |
| 180 for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) { | |
| 181 // Try preserving external display's aspect ratio on the first attempt. | |
| 182 // If that fails, fall back to the highest matching resolution. | |
| 183 bool preserve_aspect = attempt == 0; | |
| 184 | |
| 185 if (internal_displays == 1) { | |
| 186 can_mirror = FindMirrorMode(&cached_displays[one_is_internal ? 0 : 1], | |
| 187 &cached_displays[one_is_internal ? 1 : 0], | |
| 188 configurator_->is_panel_fitting_enabled_, | |
| 189 preserve_aspect); | |
| 190 } else { // if (internal_displays == 0) | |
| 191 // No panel fitting for external displays, so fall back to exact match. | |
| 192 can_mirror = FindMirrorMode(&cached_displays[0], &cached_displays[1], | |
| 193 false, preserve_aspect); | |
| 194 if (!can_mirror && preserve_aspect) { | |
| 195 // FindMirrorMode() will try to preserve aspect ratio of what it | |
| 196 // thinks is external display, so if it didn't succeed with one, maybe | |
| 197 // it will succeed with the other. This way we will have the correct | |
| 198 // aspect ratio on at least one of them. | |
| 199 can_mirror = FindMirrorMode(&cached_displays[1], &cached_displays[0], | |
| 200 false, preserve_aspect); | |
| 201 } | |
| 202 } | |
| 203 } | |
| 204 } | |
| 205 | |
| 206 return cached_displays; | |
| 207 } | |
| 208 | |
| 209 bool DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayLayout( | |
| 210 const std::vector<DisplaySnapshot*>& displays, | |
| 211 MultipleDisplayState new_display_state, | |
| 212 chromeos::DisplayPowerState new_power_state, | |
| 213 std::vector<DisplayConfigureRequest>* requests, | |
| 214 gfx::Size* framebuffer_size) const { | |
| 215 std::vector<DisplayState> states = ParseDisplays(displays); | |
| 216 std::vector<bool> display_power; | |
| 217 int num_on_displays = | |
| 218 GetDisplayPower(displays, new_power_state, &display_power); | |
| 219 VLOG(1) << "EnterState: display=" | |
| 220 << MultipleDisplayStateToString(new_display_state) | |
| 221 << " power=" << DisplayPowerStateToString(new_power_state); | |
| 222 | |
| 223 // Framebuffer dimensions. | |
| 224 gfx::Size size; | |
| 225 | |
| 226 for (size_t i = 0; i < displays.size(); ++i) { | |
| 227 requests->push_back(DisplayConfigureRequest( | |
| 228 displays[i], displays[i]->current_mode(), gfx::Point())); | |
| 229 } | |
| 230 | |
| 231 switch (new_display_state) { | |
| 232 case MULTIPLE_DISPLAY_STATE_INVALID: | |
| 233 NOTREACHED() << "Ignoring request to enter invalid state with " | |
| 234 << displays.size() << " connected display(s)"; | |
| 235 return false; | |
| 236 case MULTIPLE_DISPLAY_STATE_HEADLESS: | |
| 237 if (displays.size() != 0) { | |
| 238 LOG(WARNING) << "Ignoring request to enter headless mode with " | |
| 239 << displays.size() << " connected display(s)"; | |
| 240 return false; | |
| 241 } | |
| 242 break; | |
| 243 case MULTIPLE_DISPLAY_STATE_SINGLE: { | |
| 244 // If there are multiple displays connected, only one should be turned on. | |
| 245 if (displays.size() != 1 && num_on_displays != 1) { | |
| 246 LOG(WARNING) << "Ignoring request to enter single mode with " | |
| 247 << displays.size() << " connected displays and " | |
| 248 << num_on_displays << " turned on"; | |
| 249 return false; | |
| 250 } | |
| 251 | |
| 252 for (size_t i = 0; i < states.size(); ++i) { | |
| 253 const DisplayState* state = &states[i]; | |
| 254 (*requests)[i].mode = display_power[i] ? state->selected_mode : NULL; | |
| 255 | |
| 256 if (display_power[i] || states.size() == 1) { | |
| 257 const DisplayMode* mode_info = state->selected_mode; | |
| 258 if (!mode_info) { | |
| 259 LOG(WARNING) << "No selected mode when configuring display: " | |
| 260 << state->display->ToString(); | |
| 261 return false; | |
| 262 } | |
| 263 if (mode_info->size() == gfx::Size(1024, 768)) { | |
| 264 VLOG(1) << "Potentially misdetecting display(1024x768):" | |
| 265 << " displays size=" << states.size() | |
| 266 << ", num_on_displays=" << num_on_displays | |
| 267 << ", current size:" << size.width() << "x" << size.height() | |
| 268 << ", i=" << i << ", display=" << state->display->ToString() | |
| 269 << ", display_mode=" << mode_info->ToString(); | |
| 270 } | |
| 271 size = mode_info->size(); | |
| 272 } | |
| 273 } | |
| 274 break; | |
| 275 } | |
| 276 case MULTIPLE_DISPLAY_STATE_DUAL_MIRROR: { | |
| 277 if (states.size() != 2 || | |
| 278 (num_on_displays != 0 && num_on_displays != 2)) { | |
| 279 LOG(WARNING) << "Ignoring request to enter mirrored mode with " | |
| 280 << states.size() << " connected display(s) and " | |
| 281 << num_on_displays << " turned on"; | |
| 282 return false; | |
| 283 } | |
| 284 | |
| 285 const DisplayMode* mode_info = states[0].mirror_mode; | |
| 286 if (!mode_info) { | |
| 287 LOG(WARNING) << "No mirror mode when configuring display: " | |
| 288 << states[0].display->ToString(); | |
| 289 return false; | |
| 290 } | |
| 291 size = mode_info->size(); | |
| 292 | |
| 293 for (size_t i = 0; i < states.size(); ++i) { | |
| 294 const DisplayState* state = &states[i]; | |
| 295 (*requests)[i].mode = display_power[i] ? state->mirror_mode : NULL; | |
| 296 } | |
| 297 break; | |
| 298 } | |
| 299 case MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED: | |
| 300 case MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED: { | |
| 301 // In docked mode (with internal display + 2 external displays) the state | |
| 302 // will be DUAL_EXTENDED with internal display turned off and the 2 | |
| 303 // external displays turned on. | |
| 304 if (new_display_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED && | |
| 305 states.size() != 2 && num_on_displays != 2) { | |
| 306 LOG(WARNING) << "Ignoring request to enter extended mode with " | |
| 307 << states.size() << " connected display(s) and " | |
| 308 << num_on_displays << " turned on"; | |
| 309 return false; | |
| 310 } | |
| 311 | |
| 312 for (size_t i = 0; i < states.size(); ++i) { | |
| 313 const DisplayState* state = &states[i]; | |
| 314 (*requests)[i].origin.set_y(size.height() ? size.height() + kVerticalGap | |
| 315 : 0); | |
| 316 (*requests)[i].mode = display_power[i] ? state->selected_mode : NULL; | |
| 317 | |
| 318 // Retain the full screen size even if all displays are off so the | |
| 319 // same desktop configuration can be restored when the displays are | |
| 320 // turned back on. | |
| 321 const DisplayMode* mode_info = states[i].selected_mode; | |
| 322 if (!mode_info) { | |
| 323 LOG(WARNING) << "No selected mode when configuring display: " | |
| 324 << state->display->ToString(); | |
| 325 return false; | |
| 326 } | |
| 327 | |
| 328 size.set_width(std::max<int>(size.width(), mode_info->size().width())); | |
| 329 size.set_height(size.height() + (size.height() ? kVerticalGap : 0) + | |
| 330 mode_info->size().height()); | |
| 331 } | |
| 332 break; | |
| 333 } | |
| 334 } | |
| 335 DCHECK(new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS || | |
| 336 !size.IsEmpty()); | |
| 337 *framebuffer_size = size; | |
| 338 return true; | |
| 339 } | |
| 340 | |
| 341 DisplayConfigurator::DisplayStateList | |
| 342 DisplayConfigurator::DisplayLayoutManagerImpl::GetDisplayStates() const { | |
| 343 return configurator_->cached_displays(); | |
| 344 } | |
| 345 | |
| 346 bool DisplayConfigurator::DisplayLayoutManagerImpl::IsMirroring() const { | |
| 347 if (GetDisplayState() == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR) | |
| 348 return true; | |
| 349 | |
| 350 return GetSoftwareMirroringController() && | |
| 351 GetSoftwareMirroringController()->SoftwareMirroringEnabled(); | |
| 352 } | |
| 353 | |
| 354 const DisplayMode* | |
| 355 DisplayConfigurator::DisplayLayoutManagerImpl::GetUserSelectedMode( | |
| 356 const DisplaySnapshot& display) const { | |
| 357 gfx::Size size; | |
| 358 const DisplayMode* selected_mode = nullptr; | |
| 359 if (GetStateController() && | |
| 360 GetStateController()->GetResolutionForDisplayId(display.display_id(), | |
| 361 &size)) { | |
| 362 selected_mode = FindDisplayModeMatchingSize(display, size); | |
| 363 } | |
| 364 | |
| 365 // Fall back to native mode. | |
| 366 return selected_mode ? selected_mode : display.native_mode(); | |
| 367 } | |
| 368 | |
| 369 bool DisplayConfigurator::DisplayLayoutManagerImpl::FindMirrorMode( | |
| 370 DisplayState* internal_display, | |
| 371 DisplayState* external_display, | |
| 372 bool try_panel_fitting, | |
| 373 bool preserve_aspect) const { | |
| 374 const DisplayMode* internal_native_info = | |
| 375 internal_display->display->native_mode(); | |
| 376 const DisplayMode* external_native_info = | |
| 377 external_display->display->native_mode(); | |
| 378 if (!internal_native_info || !external_native_info) | |
| 379 return false; | |
| 380 | |
| 381 // Check if some external display resolution can be mirrored on internal. | |
| 382 // Prefer the modes in the order they're present in DisplaySnapshot, assuming | |
| 383 // this is the order in which they look better on the monitor. | |
| 384 for (const auto& external_mode : external_display->display->modes()) { | |
| 385 bool is_native_aspect_ratio = | |
| 386 external_native_info->size().width() * external_mode->size().height() == | |
| 387 external_native_info->size().height() * external_mode->size().width(); | |
| 388 if (preserve_aspect && !is_native_aspect_ratio) | |
| 389 continue; // Allow only aspect ratio preserving modes for mirroring. | |
| 390 | |
| 391 // Try finding an exact match. | |
| 392 for (const auto& internal_mode : internal_display->display->modes()) { | |
| 393 if (internal_mode->size() == external_mode->size() && | |
| 394 internal_mode->is_interlaced() == external_mode->is_interlaced()) { | |
| 395 internal_display->mirror_mode = internal_mode.get(); | |
| 396 external_display->mirror_mode = external_mode.get(); | |
| 397 return true; // Mirror mode found. | |
| 398 } | |
| 399 } | |
| 400 | |
| 401 // Try to create a matching internal display mode by panel fitting. | |
| 402 if (try_panel_fitting) { | |
| 403 // We can downscale by 1.125, and upscale indefinitely. Downscaling looks | |
| 404 // ugly, so, can fit == can upscale. Also, internal panels don't support | |
| 405 // fitting interlaced modes. | |
| 406 bool can_fit = internal_native_info->size().width() >= | |
| 407 external_mode->size().width() && | |
| 408 internal_native_info->size().height() >= | |
| 409 external_mode->size().height() && | |
| 410 !external_mode->is_interlaced(); | |
| 411 if (can_fit) { | |
| 412 configurator_->native_display_delegate_->AddMode( | |
| 413 *internal_display->display, external_mode.get()); | |
| 414 internal_display->display->add_mode(external_mode.get()); | |
| 415 internal_display->mirror_mode = | |
| 416 internal_display->display->modes().back().get(); | |
| 417 external_display->mirror_mode = external_mode.get(); | |
| 418 return true; // Mirror mode created. | |
| 419 } | |
| 420 } | |
| 421 } | |
| 422 | |
| 423 return false; | |
| 424 } | |
| 425 | |
| 426 //////////////////////////////////////////////////////////////////////////////// | |
| 427 // DisplayConfigurator implementation | |
| 428 | |
| 429 // static | |
| 430 const DisplayMode* DisplayConfigurator::FindDisplayModeMatchingSize( | |
| 431 const DisplaySnapshot& display, | |
| 432 const gfx::Size& size) { | |
| 433 const DisplayMode* best_mode = NULL; | |
| 434 for (const std::unique_ptr<const DisplayMode>& mode : display.modes()) { | |
| 435 if (mode->size() != size) | |
| 436 continue; | |
| 437 | |
| 438 if (mode.get() == display.native_mode()) { | |
| 439 best_mode = mode.get(); | |
| 440 break; | |
| 441 } | |
| 442 | |
| 443 if (!best_mode) { | |
| 444 best_mode = mode.get(); | |
| 445 continue; | |
| 446 } | |
| 447 | |
| 448 if (mode->is_interlaced()) { | |
| 449 if (!best_mode->is_interlaced()) | |
| 450 continue; | |
| 451 } else { | |
| 452 // Reset the best rate if the non interlaced is | |
| 453 // found the first time. | |
| 454 if (best_mode->is_interlaced()) { | |
| 455 best_mode = mode.get(); | |
| 456 continue; | |
| 457 } | |
| 458 } | |
| 459 if (mode->refresh_rate() < best_mode->refresh_rate()) | |
| 460 continue; | |
| 461 | |
| 462 best_mode = mode.get(); | |
| 463 } | |
| 464 | |
| 465 return best_mode; | |
| 466 } | |
| 467 | |
| 468 DisplayConfigurator::DisplayConfigurator() | |
| 469 : state_controller_(NULL), | |
| 470 mirroring_controller_(NULL), | |
| 471 is_panel_fitting_enabled_(false), | |
| 472 configure_display_(base::SysInfo::IsRunningOnChromeOS()), | |
| 473 current_display_state_(MULTIPLE_DISPLAY_STATE_INVALID), | |
| 474 current_power_state_(chromeos::DISPLAY_POWER_ALL_ON), | |
| 475 requested_display_state_(MULTIPLE_DISPLAY_STATE_INVALID), | |
| 476 requested_power_state_(chromeos::DISPLAY_POWER_ALL_ON), | |
| 477 pending_power_state_(chromeos::DISPLAY_POWER_ALL_ON), | |
| 478 has_pending_power_state_(false), | |
| 479 pending_power_flags_(kSetDisplayPowerNoFlags), | |
| 480 force_configure_(false), | |
| 481 next_display_protection_client_id_(1), | |
| 482 display_externally_controlled_(false), | |
| 483 display_control_changing_(false), | |
| 484 displays_suspended_(false), | |
| 485 layout_manager_(new DisplayLayoutManagerImpl(this)), | |
| 486 weak_ptr_factory_(this) { | |
| 487 } | |
| 488 | |
| 489 DisplayConfigurator::~DisplayConfigurator() { | |
| 490 if (native_display_delegate_) | |
| 491 native_display_delegate_->RemoveObserver(this); | |
| 492 | |
| 493 CallAndClearInProgressCallbacks(false); | |
| 494 CallAndClearQueuedCallbacks(false); | |
| 495 | |
| 496 while (!query_protection_callbacks_.empty()) { | |
| 497 query_protection_callbacks_.front().Run(QueryProtectionResponse()); | |
| 498 query_protection_callbacks_.pop(); | |
| 499 } | |
| 500 | |
| 501 while (!enable_protection_callbacks_.empty()) { | |
| 502 enable_protection_callbacks_.front().Run(false); | |
| 503 enable_protection_callbacks_.pop(); | |
| 504 } | |
| 505 } | |
| 506 | |
| 507 void DisplayConfigurator::SetDelegateForTesting( | |
| 508 std::unique_ptr<NativeDisplayDelegate> display_delegate) { | |
| 509 DCHECK(!native_display_delegate_); | |
| 510 | |
| 511 native_display_delegate_ = std::move(display_delegate); | |
| 512 configure_display_ = true; | |
| 513 } | |
| 514 | |
| 515 void DisplayConfigurator::SetInitialDisplayPower( | |
| 516 chromeos::DisplayPowerState power_state) { | |
| 517 DCHECK_EQ(current_display_state_, MULTIPLE_DISPLAY_STATE_INVALID); | |
| 518 requested_power_state_ = current_power_state_ = power_state; | |
| 519 NotifyPowerStateObservers(); | |
| 520 } | |
| 521 | |
| 522 void DisplayConfigurator::Init( | |
| 523 std::unique_ptr<NativeDisplayDelegate> display_delegate, | |
| 524 bool is_panel_fitting_enabled) { | |
| 525 is_panel_fitting_enabled_ = is_panel_fitting_enabled; | |
| 526 if (!configure_display_ || display_externally_controlled_) | |
| 527 return; | |
| 528 | |
| 529 // If the delegate is already initialized don't update it (For example, tests | |
| 530 // set their own delegates). | |
| 531 if (!native_display_delegate_) { | |
| 532 native_display_delegate_ = std::move(display_delegate); | |
| 533 native_display_delegate_->AddObserver(this); | |
| 534 } | |
| 535 } | |
| 536 | |
| 537 void DisplayConfigurator::TakeControl(const DisplayControlCallback& callback) { | |
| 538 if (display_control_changing_) { | |
| 539 callback.Run(false); | |
| 540 return; | |
| 541 } | |
| 542 | |
| 543 if (!display_externally_controlled_) { | |
| 544 callback.Run(true); | |
| 545 return; | |
| 546 } | |
| 547 | |
| 548 display_control_changing_ = true; | |
| 549 native_display_delegate_->TakeDisplayControl( | |
| 550 base::Bind(&DisplayConfigurator::OnDisplayControlTaken, | |
| 551 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 552 } | |
| 553 | |
| 554 void DisplayConfigurator::OnDisplayControlTaken( | |
| 555 const DisplayControlCallback& callback, | |
| 556 bool success) { | |
| 557 display_control_changing_ = false; | |
| 558 display_externally_controlled_ = !success; | |
| 559 if (success) { | |
| 560 // Force a configuration since the display configuration may have changed. | |
| 561 force_configure_ = true; | |
| 562 // Restore the last power state used before releasing control. | |
| 563 SetDisplayPower(requested_power_state_, kSetDisplayPowerNoFlags, | |
| 564 base::Bind(&DoNothing)); | |
| 565 } | |
| 566 | |
| 567 callback.Run(success); | |
| 568 } | |
| 569 | |
| 570 void DisplayConfigurator::RelinquishControl( | |
| 571 const DisplayControlCallback& callback) { | |
| 572 if (display_control_changing_) { | |
| 573 callback.Run(false); | |
| 574 return; | |
| 575 } | |
| 576 | |
| 577 if (display_externally_controlled_) { | |
| 578 callback.Run(true); | |
| 579 return; | |
| 580 } | |
| 581 | |
| 582 // For simplicity, just fail if in the middle of a display configuration. | |
| 583 if (configuration_task_) { | |
| 584 callback.Run(false); | |
| 585 return; | |
| 586 } | |
| 587 | |
| 588 display_control_changing_ = true; | |
| 589 | |
| 590 // Turn off the displays before releasing control since we're no longer using | |
| 591 // them for output. | |
| 592 SetDisplayPowerInternal( | |
| 593 chromeos::DISPLAY_POWER_ALL_OFF, kSetDisplayPowerNoFlags, | |
| 594 base::Bind(&DisplayConfigurator::SendRelinquishDisplayControl, | |
| 595 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 596 } | |
| 597 | |
| 598 void DisplayConfigurator::SendRelinquishDisplayControl( | |
| 599 const DisplayControlCallback& callback, bool success) { | |
| 600 if (success) { | |
| 601 // Set the flag early such that an incoming configuration event won't start | |
| 602 // while we're releasing control of the displays. | |
| 603 display_externally_controlled_ = true; | |
| 604 native_display_delegate_->RelinquishDisplayControl( | |
| 605 base::Bind(&DisplayConfigurator::OnDisplayControlRelinquished, | |
| 606 weak_ptr_factory_.GetWeakPtr(), callback)); | |
| 607 } else { | |
| 608 display_control_changing_ = false; | |
| 609 callback.Run(false); | |
| 610 } | |
| 611 } | |
| 612 | |
| 613 void DisplayConfigurator::OnDisplayControlRelinquished( | |
| 614 const DisplayControlCallback& callback, | |
| 615 bool success) { | |
| 616 display_control_changing_ = false; | |
| 617 display_externally_controlled_ = success; | |
| 618 if (!success) { | |
| 619 force_configure_ = true; | |
| 620 RunPendingConfiguration(); | |
| 621 } | |
| 622 | |
| 623 callback.Run(success); | |
| 624 } | |
| 625 | |
| 626 void DisplayConfigurator::ForceInitialConfigure( | |
| 627 uint32_t background_color_argb) { | |
| 628 if (!configure_display_ || display_externally_controlled_) | |
| 629 return; | |
| 630 | |
| 631 DCHECK(native_display_delegate_); | |
| 632 native_display_delegate_->Initialize(); | |
| 633 | |
| 634 // ForceInitialConfigure should be the first configuration so there shouldn't | |
| 635 // be anything scheduled. | |
| 636 DCHECK(!configuration_task_); | |
| 637 | |
| 638 configuration_task_.reset(new UpdateDisplayConfigurationTask( | |
| 639 native_display_delegate_.get(), layout_manager_.get(), | |
| 640 requested_display_state_, requested_power_state_, | |
| 641 kSetDisplayPowerForceProbe, background_color_argb, true, | |
| 642 base::Bind(&DisplayConfigurator::OnConfigured, | |
| 643 weak_ptr_factory_.GetWeakPtr()))); | |
| 644 configuration_task_->Run(); | |
| 645 } | |
| 646 | |
| 647 DisplayConfigurator::ContentProtectionClientId | |
| 648 DisplayConfigurator::RegisterContentProtectionClient() { | |
| 649 if (!configure_display_ || display_externally_controlled_) | |
| 650 return kInvalidClientId; | |
| 651 | |
| 652 return next_display_protection_client_id_++; | |
| 653 } | |
| 654 | |
| 655 void DisplayConfigurator::UnregisterContentProtectionClient( | |
| 656 ContentProtectionClientId client_id) { | |
| 657 client_protection_requests_.erase(client_id); | |
| 658 | |
| 659 ContentProtections protections; | |
| 660 for (const auto& requests_pair : client_protection_requests_) { | |
| 661 for (const auto& protections_pair : requests_pair.second) { | |
| 662 protections[protections_pair.first] |= protections_pair.second; | |
| 663 } | |
| 664 } | |
| 665 | |
| 666 enable_protection_callbacks_.push(base::Bind(&DoNothing)); | |
| 667 ApplyContentProtectionTask* task = new ApplyContentProtectionTask( | |
| 668 layout_manager_.get(), native_display_delegate_.get(), protections, | |
| 669 base::Bind(&DisplayConfigurator::OnContentProtectionClientUnregistered, | |
| 670 weak_ptr_factory_.GetWeakPtr())); | |
| 671 content_protection_tasks_.push( | |
| 672 base::Bind(&ApplyContentProtectionTask::Run, base::Owned(task))); | |
| 673 | |
| 674 if (content_protection_tasks_.size() == 1) | |
| 675 content_protection_tasks_.front().Run(); | |
| 676 } | |
| 677 | |
| 678 void DisplayConfigurator::OnContentProtectionClientUnregistered(bool success) { | |
| 679 DCHECK(!content_protection_tasks_.empty()); | |
| 680 content_protection_tasks_.pop(); | |
| 681 | |
| 682 DCHECK(!enable_protection_callbacks_.empty()); | |
| 683 EnableProtectionCallback callback = enable_protection_callbacks_.front(); | |
| 684 enable_protection_callbacks_.pop(); | |
| 685 | |
| 686 if (!content_protection_tasks_.empty()) | |
| 687 content_protection_tasks_.front().Run(); | |
| 688 } | |
| 689 | |
| 690 void DisplayConfigurator::QueryContentProtectionStatus( | |
| 691 ContentProtectionClientId client_id, | |
| 692 int64_t display_id, | |
| 693 const QueryProtectionCallback& callback) { | |
| 694 // Exclude virtual displays so that protected content will not be recaptured | |
| 695 // through the cast stream. | |
| 696 for (const DisplaySnapshot* display : cached_displays_) { | |
| 697 if (display->display_id() == display_id && | |
| 698 !IsPhysicalDisplayType(display->type())) { | |
| 699 callback.Run(QueryProtectionResponse()); | |
| 700 return; | |
| 701 } | |
| 702 } | |
| 703 | |
| 704 if (!configure_display_ || display_externally_controlled_) { | |
| 705 callback.Run(QueryProtectionResponse()); | |
| 706 return; | |
| 707 } | |
| 708 | |
| 709 query_protection_callbacks_.push(callback); | |
| 710 QueryContentProtectionTask* task = new QueryContentProtectionTask( | |
| 711 layout_manager_.get(), native_display_delegate_.get(), display_id, | |
| 712 base::Bind(&DisplayConfigurator::OnContentProtectionQueried, | |
| 713 weak_ptr_factory_.GetWeakPtr(), client_id, display_id)); | |
| 714 content_protection_tasks_.push( | |
| 715 base::Bind(&QueryContentProtectionTask::Run, base::Owned(task))); | |
| 716 if (content_protection_tasks_.size() == 1) | |
| 717 content_protection_tasks_.front().Run(); | |
| 718 } | |
| 719 | |
| 720 void DisplayConfigurator::OnContentProtectionQueried( | |
| 721 ContentProtectionClientId client_id, | |
| 722 int64_t display_id, | |
| 723 QueryContentProtectionTask::Response task_response) { | |
| 724 QueryProtectionResponse response; | |
| 725 response.success = task_response.success; | |
| 726 response.link_mask = task_response.link_mask; | |
| 727 | |
| 728 // Don't reveal protections requested by other clients. | |
| 729 ProtectionRequests::iterator it = client_protection_requests_.find(client_id); | |
| 730 if (response.success && it != client_protection_requests_.end()) { | |
| 731 uint32_t requested_mask = 0; | |
| 732 if (it->second.find(display_id) != it->second.end()) | |
| 733 requested_mask = it->second[display_id]; | |
| 734 response.protection_mask = | |
| 735 task_response.enabled & ~task_response.unfulfilled & requested_mask; | |
| 736 } | |
| 737 | |
| 738 DCHECK(!content_protection_tasks_.empty()); | |
| 739 content_protection_tasks_.pop(); | |
| 740 | |
| 741 DCHECK(!query_protection_callbacks_.empty()); | |
| 742 QueryProtectionCallback callback = query_protection_callbacks_.front(); | |
| 743 query_protection_callbacks_.pop(); | |
| 744 callback.Run(response); | |
| 745 | |
| 746 if (!content_protection_tasks_.empty()) | |
| 747 content_protection_tasks_.front().Run(); | |
| 748 } | |
| 749 | |
| 750 void DisplayConfigurator::EnableContentProtection( | |
| 751 ContentProtectionClientId client_id, | |
| 752 int64_t display_id, | |
| 753 uint32_t desired_method_mask, | |
| 754 const EnableProtectionCallback& callback) { | |
| 755 if (!configure_display_ || display_externally_controlled_) { | |
| 756 callback.Run(false); | |
| 757 return; | |
| 758 } | |
| 759 | |
| 760 ContentProtections protections; | |
| 761 for (const auto& requests_pair : client_protection_requests_) { | |
| 762 for (const auto& protections_pair : requests_pair.second) { | |
| 763 if (requests_pair.first == client_id && | |
| 764 protections_pair.first == display_id) | |
| 765 continue; | |
| 766 | |
| 767 protections[protections_pair.first] |= protections_pair.second; | |
| 768 } | |
| 769 } | |
| 770 protections[display_id] |= desired_method_mask; | |
| 771 | |
| 772 enable_protection_callbacks_.push(callback); | |
| 773 ApplyContentProtectionTask* task = new ApplyContentProtectionTask( | |
| 774 layout_manager_.get(), native_display_delegate_.get(), protections, | |
| 775 base::Bind(&DisplayConfigurator::OnContentProtectionEnabled, | |
| 776 weak_ptr_factory_.GetWeakPtr(), client_id, display_id, | |
| 777 desired_method_mask)); | |
| 778 content_protection_tasks_.push( | |
| 779 base::Bind(&ApplyContentProtectionTask::Run, base::Owned(task))); | |
| 780 if (content_protection_tasks_.size() == 1) | |
| 781 content_protection_tasks_.front().Run(); | |
| 782 } | |
| 783 | |
| 784 void DisplayConfigurator::OnContentProtectionEnabled( | |
| 785 ContentProtectionClientId client_id, | |
| 786 int64_t display_id, | |
| 787 uint32_t desired_method_mask, | |
| 788 bool success) { | |
| 789 DCHECK(!content_protection_tasks_.empty()); | |
| 790 content_protection_tasks_.pop(); | |
| 791 | |
| 792 DCHECK(!enable_protection_callbacks_.empty()); | |
| 793 EnableProtectionCallback callback = enable_protection_callbacks_.front(); | |
| 794 enable_protection_callbacks_.pop(); | |
| 795 | |
| 796 if (!success) { | |
| 797 callback.Run(false); | |
| 798 return; | |
| 799 } | |
| 800 | |
| 801 if (desired_method_mask == CONTENT_PROTECTION_METHOD_NONE) { | |
| 802 if (client_protection_requests_.find(client_id) != | |
| 803 client_protection_requests_.end()) { | |
| 804 client_protection_requests_[client_id].erase(display_id); | |
| 805 if (client_protection_requests_[client_id].size() == 0) | |
| 806 client_protection_requests_.erase(client_id); | |
| 807 } | |
| 808 } else { | |
| 809 client_protection_requests_[client_id][display_id] = desired_method_mask; | |
| 810 } | |
| 811 | |
| 812 callback.Run(true); | |
| 813 if (!content_protection_tasks_.empty()) | |
| 814 content_protection_tasks_.front().Run(); | |
| 815 } | |
| 816 | |
| 817 std::vector<ui::ColorCalibrationProfile> | |
| 818 DisplayConfigurator::GetAvailableColorCalibrationProfiles(int64_t display_id) { | |
| 819 if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | |
| 820 switches::kDisableDisplayColorCalibration)) { | |
| 821 for (const DisplaySnapshot* display : cached_displays_) { | |
| 822 if (display->display_id() == display_id && | |
| 823 IsPhysicalDisplayType(display->type())) { | |
| 824 return native_display_delegate_->GetAvailableColorCalibrationProfiles( | |
| 825 *display); | |
| 826 } | |
| 827 } | |
| 828 } | |
| 829 | |
| 830 return std::vector<ui::ColorCalibrationProfile>(); | |
| 831 } | |
| 832 | |
| 833 bool DisplayConfigurator::SetColorCalibrationProfile( | |
| 834 int64_t display_id, | |
| 835 ui::ColorCalibrationProfile new_profile) { | |
| 836 for (const DisplaySnapshot* display : cached_displays_) { | |
| 837 if (display->display_id() == display_id && | |
| 838 IsPhysicalDisplayType(display->type())) { | |
| 839 return native_display_delegate_->SetColorCalibrationProfile(*display, | |
| 840 new_profile); | |
| 841 } | |
| 842 } | |
| 843 | |
| 844 return false; | |
| 845 } | |
| 846 | |
| 847 bool DisplayConfigurator::SetColorCorrection( | |
| 848 int64_t display_id, | |
| 849 const std::vector<GammaRampRGBEntry>& degamma_lut, | |
| 850 const std::vector<GammaRampRGBEntry>& gamma_lut, | |
| 851 const std::vector<float>& correction_matrix) { | |
| 852 for (const DisplaySnapshot* display : cached_displays_) { | |
| 853 if (display->display_id() == display_id) | |
| 854 return native_display_delegate_->SetColorCorrection( | |
| 855 *display, degamma_lut, gamma_lut, correction_matrix); | |
| 856 } | |
| 857 | |
| 858 return false; | |
| 859 } | |
| 860 | |
| 861 void DisplayConfigurator::PrepareForExit() { | |
| 862 configure_display_ = false; | |
| 863 } | |
| 864 | |
| 865 void DisplayConfigurator::SetDisplayPowerInternal( | |
| 866 chromeos::DisplayPowerState power_state, | |
| 867 int flags, | |
| 868 const ConfigurationCallback& callback) { | |
| 869 // Only skip if the current power state is the same and the latest requested | |
| 870 // power state is the same. If |pending_power_state_ != current_power_state_| | |
| 871 // then there is a current task pending or the last configuration failed. In | |
| 872 // either case request a new configuration to make sure the state is | |
| 873 // consistent with the expectations. | |
| 874 if (power_state == current_power_state_ && | |
| 875 power_state == pending_power_state_ && | |
| 876 !(flags & kSetDisplayPowerForceProbe)) { | |
| 877 callback.Run(true); | |
| 878 return; | |
| 879 } | |
| 880 | |
| 881 pending_power_state_ = power_state; | |
| 882 has_pending_power_state_ = true; | |
| 883 pending_power_flags_ = flags; | |
| 884 queued_configuration_callbacks_.push_back(callback); | |
| 885 | |
| 886 if (configure_timer_.IsRunning()) { | |
| 887 // If there is a configuration task scheduled, avoid performing | |
| 888 // configuration immediately. Instead reset the timer to wait for things to | |
| 889 // settle. | |
| 890 configure_timer_.Reset(); | |
| 891 return; | |
| 892 } | |
| 893 | |
| 894 RunPendingConfiguration(); | |
| 895 } | |
| 896 | |
| 897 void DisplayConfigurator::SetDisplayPower( | |
| 898 chromeos::DisplayPowerState power_state, | |
| 899 int flags, | |
| 900 const ConfigurationCallback& callback) { | |
| 901 if (!configure_display_ || display_externally_controlled_) { | |
| 902 callback.Run(false); | |
| 903 return; | |
| 904 } | |
| 905 | |
| 906 VLOG(1) << "SetDisplayPower: power_state=" | |
| 907 << DisplayPowerStateToString(power_state) << " flags=" << flags | |
| 908 << ", configure timer=" | |
| 909 << (configure_timer_.IsRunning() ? "Running" : "Stopped"); | |
| 910 | |
| 911 requested_power_state_ = power_state; | |
| 912 SetDisplayPowerInternal(requested_power_state_, flags, callback); | |
| 913 } | |
| 914 | |
| 915 void DisplayConfigurator::SetDisplayMode(MultipleDisplayState new_state) { | |
| 916 if (!configure_display_ || display_externally_controlled_) | |
| 917 return; | |
| 918 | |
| 919 VLOG(1) << "SetDisplayMode: state=" | |
| 920 << MultipleDisplayStateToString(new_state); | |
| 921 if (current_display_state_ == new_state) { | |
| 922 // Cancel software mirroring if the state is moving from | |
| 923 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED to | |
| 924 // MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED. | |
| 925 if (mirroring_controller_ && | |
| 926 new_state == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED) | |
| 927 mirroring_controller_->SetSoftwareMirroring(false); | |
| 928 NotifyDisplayStateObservers(true, new_state); | |
| 929 return; | |
| 930 } | |
| 931 | |
| 932 requested_display_state_ = new_state; | |
| 933 | |
| 934 RunPendingConfiguration(); | |
| 935 } | |
| 936 | |
| 937 void DisplayConfigurator::OnConfigurationChanged() { | |
| 938 // Don't do anything if the displays are currently suspended. Instead we will | |
| 939 // probe and reconfigure the displays if necessary in ResumeDisplays(). | |
| 940 if (displays_suspended_) { | |
| 941 VLOG(1) << "Displays are currently suspended. Not attempting to " | |
| 942 << "reconfigure them."; | |
| 943 return; | |
| 944 } | |
| 945 | |
| 946 // Configure displays with |kConfigureDelayMs| delay, | |
| 947 // so that time-consuming ConfigureDisplays() won't be called multiple times. | |
| 948 configure_timer_.Start(FROM_HERE, | |
| 949 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), | |
| 950 this, &DisplayConfigurator::ConfigureDisplays); | |
| 951 } | |
| 952 | |
| 953 void DisplayConfigurator::AddObserver(Observer* observer) { | |
| 954 observers_.AddObserver(observer); | |
| 955 } | |
| 956 | |
| 957 void DisplayConfigurator::RemoveObserver(Observer* observer) { | |
| 958 observers_.RemoveObserver(observer); | |
| 959 } | |
| 960 | |
| 961 void DisplayConfigurator::SuspendDisplays( | |
| 962 const ConfigurationCallback& callback) { | |
| 963 if (!configure_display_ || display_externally_controlled_) { | |
| 964 callback.Run(false); | |
| 965 return; | |
| 966 } | |
| 967 | |
| 968 displays_suspended_ = true; | |
| 969 | |
| 970 // Stop |configure_timer_| because we will force probe and configure all the | |
| 971 // displays at resume time anyway. | |
| 972 configure_timer_.Stop(); | |
| 973 | |
| 974 // Turn off the displays for suspend. This way, if we wake up for lucid sleep, | |
| 975 // the displays will not turn on (all displays should be off for lucid sleep | |
| 976 // unless explicitly requested by lucid sleep code). Use | |
| 977 // SetDisplayPowerInternal so requested_power_state_ is maintained. | |
| 978 SetDisplayPowerInternal(chromeos::DISPLAY_POWER_ALL_OFF, | |
| 979 kSetDisplayPowerNoFlags, callback); | |
| 980 | |
| 981 // We need to make sure that the monitor configuration we just did actually | |
| 982 // completes before we return. | |
| 983 native_display_delegate_->SyncWithServer(); | |
| 984 } | |
| 985 | |
| 986 void DisplayConfigurator::ResumeDisplays() { | |
| 987 if (!configure_display_ || display_externally_controlled_) | |
| 988 return; | |
| 989 | |
| 990 displays_suspended_ = false; | |
| 991 | |
| 992 if (current_display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_MIRROR || | |
| 993 current_display_state_ == MULTIPLE_DISPLAY_STATE_DUAL_EXTENDED || | |
| 994 current_display_state_ == MULTIPLE_DISPLAY_STATE_MULTI_EXTENDED) { | |
| 995 // When waking up from suspend while being in a multi display mode, we | |
| 996 // schedule a delayed forced configuration, which will make | |
| 997 // SetDisplayPowerInternal() avoid performing the configuration immediately. | |
| 998 // This gives a chance to wait for all displays to be added and detected | |
| 999 // before configuration is performed, so we won't immediately resize the | |
| 1000 // desktops and the windows on it to fit on a single display. | |
| 1001 configure_timer_.Start(FROM_HERE, base::TimeDelta::FromMilliseconds( | |
| 1002 kResumeConfigureMultiDisplayDelayMs), | |
| 1003 this, &DisplayConfigurator::ConfigureDisplays); | |
| 1004 } | |
| 1005 | |
| 1006 // If requested_power_state_ is ALL_OFF due to idle suspend, powerd will turn | |
| 1007 // the display power on when it enables the backlight. | |
| 1008 SetDisplayPower(requested_power_state_, kSetDisplayPowerNoFlags, | |
| 1009 base::Bind(&DoNothing)); | |
| 1010 } | |
| 1011 | |
| 1012 void DisplayConfigurator::ConfigureDisplays() { | |
| 1013 if (!configure_display_ || display_externally_controlled_) | |
| 1014 return; | |
| 1015 | |
| 1016 force_configure_ = true; | |
| 1017 RunPendingConfiguration(); | |
| 1018 } | |
| 1019 | |
| 1020 void DisplayConfigurator::RunPendingConfiguration() { | |
| 1021 // Configuration task is currently running. Do not start a second | |
| 1022 // configuration. | |
| 1023 if (configuration_task_) | |
| 1024 return; | |
| 1025 | |
| 1026 if (!ShouldRunConfigurationTask()) { | |
| 1027 LOG(ERROR) << "Called RunPendingConfiguration without any changes" | |
| 1028 " requested"; | |
| 1029 CallAndClearQueuedCallbacks(true); | |
| 1030 return; | |
| 1031 } | |
| 1032 | |
| 1033 configuration_task_.reset(new UpdateDisplayConfigurationTask( | |
| 1034 native_display_delegate_.get(), layout_manager_.get(), | |
| 1035 requested_display_state_, pending_power_state_, pending_power_flags_, | |
| 1036 0, force_configure_, base::Bind(&DisplayConfigurator::OnConfigured, | |
| 1037 weak_ptr_factory_.GetWeakPtr()))); | |
| 1038 configuration_task_->set_virtual_display_snapshots( | |
| 1039 virtual_display_snapshots_.get()); | |
| 1040 | |
| 1041 // Reset the flags before running the task; otherwise it may end up scheduling | |
| 1042 // another configuration. | |
| 1043 force_configure_ = false; | |
| 1044 pending_power_flags_ = kSetDisplayPowerNoFlags; | |
| 1045 has_pending_power_state_ = false; | |
| 1046 requested_display_state_ = MULTIPLE_DISPLAY_STATE_INVALID; | |
| 1047 | |
| 1048 DCHECK(in_progress_configuration_callbacks_.empty()); | |
| 1049 in_progress_configuration_callbacks_.swap(queued_configuration_callbacks_); | |
| 1050 | |
| 1051 configuration_task_->Run(); | |
| 1052 } | |
| 1053 | |
| 1054 void DisplayConfigurator::OnConfigured( | |
| 1055 bool success, | |
| 1056 const std::vector<DisplaySnapshot*>& displays, | |
| 1057 const gfx::Size& framebuffer_size, | |
| 1058 MultipleDisplayState new_display_state, | |
| 1059 chromeos::DisplayPowerState new_power_state) { | |
| 1060 VLOG(1) << "OnConfigured: success=" << success << " new_display_state=" | |
| 1061 << MultipleDisplayStateToString(new_display_state) | |
| 1062 << " new_power_state=" << DisplayPowerStateToString(new_power_state); | |
| 1063 | |
| 1064 cached_displays_ = displays; | |
| 1065 if (success) { | |
| 1066 chromeos::DisplayPowerState old_power_state = current_power_state_; | |
| 1067 current_display_state_ = new_display_state; | |
| 1068 current_power_state_ = new_power_state; | |
| 1069 | |
| 1070 // |framebuffer_size| is empty in software mirroring mode, headless mode, | |
| 1071 // or all displays are off. | |
| 1072 DCHECK(!framebuffer_size.IsEmpty() || | |
| 1073 (mirroring_controller_ && | |
| 1074 mirroring_controller_->SoftwareMirroringEnabled()) || | |
| 1075 new_display_state == MULTIPLE_DISPLAY_STATE_HEADLESS || | |
| 1076 new_power_state == chromeos::DISPLAY_POWER_ALL_OFF); | |
| 1077 | |
| 1078 if (!framebuffer_size.IsEmpty()) | |
| 1079 framebuffer_size_ = framebuffer_size; | |
| 1080 | |
| 1081 // If the pending power state hasn't changed then make sure that value | |
| 1082 // gets updated as well since the last requested value may have been | |
| 1083 // dependent on certain conditions (ie: if only the internal monitor was | |
| 1084 // present). | |
| 1085 if (!has_pending_power_state_) | |
| 1086 pending_power_state_ = new_power_state; | |
| 1087 | |
| 1088 if (old_power_state != current_power_state_) | |
| 1089 NotifyPowerStateObservers(); | |
| 1090 } | |
| 1091 | |
| 1092 configuration_task_.reset(); | |
| 1093 NotifyDisplayStateObservers(success, new_display_state); | |
| 1094 CallAndClearInProgressCallbacks(success); | |
| 1095 | |
| 1096 if (success && !configure_timer_.IsRunning() && | |
| 1097 ShouldRunConfigurationTask()) { | |
| 1098 configure_timer_.Start(FROM_HERE, | |
| 1099 base::TimeDelta::FromMilliseconds(kConfigureDelayMs), | |
| 1100 this, &DisplayConfigurator::RunPendingConfiguration); | |
| 1101 } else { | |
| 1102 // If a new configuration task isn't scheduled respond to all queued | |
| 1103 // callbacks (for example if requested state is current state). | |
| 1104 if (!configure_timer_.IsRunning()) | |
| 1105 CallAndClearQueuedCallbacks(success); | |
| 1106 } | |
| 1107 } | |
| 1108 | |
| 1109 bool DisplayConfigurator::ShouldRunConfigurationTask() const { | |
| 1110 if (force_configure_) | |
| 1111 return true; | |
| 1112 | |
| 1113 // Schedule if there is a request to change the display state. | |
| 1114 if (requested_display_state_ != current_display_state_ && | |
| 1115 requested_display_state_ != MULTIPLE_DISPLAY_STATE_INVALID) | |
| 1116 return true; | |
| 1117 | |
| 1118 // Schedule if there is a request to change the power state. | |
| 1119 if (has_pending_power_state_) | |
| 1120 return true; | |
| 1121 | |
| 1122 return false; | |
| 1123 } | |
| 1124 | |
| 1125 void DisplayConfigurator::CallAndClearInProgressCallbacks(bool success) { | |
| 1126 for (const auto& callback : in_progress_configuration_callbacks_) | |
| 1127 callback.Run(success); | |
| 1128 | |
| 1129 in_progress_configuration_callbacks_.clear(); | |
| 1130 } | |
| 1131 | |
| 1132 void DisplayConfigurator::CallAndClearQueuedCallbacks(bool success) { | |
| 1133 for (const auto& callback : queued_configuration_callbacks_) | |
| 1134 callback.Run(success); | |
| 1135 | |
| 1136 queued_configuration_callbacks_.clear(); | |
| 1137 } | |
| 1138 | |
| 1139 void DisplayConfigurator::NotifyDisplayStateObservers( | |
| 1140 bool success, | |
| 1141 MultipleDisplayState attempted_state) { | |
| 1142 if (success) { | |
| 1143 for (Observer& observer : observers_) | |
| 1144 observer.OnDisplayModeChanged(cached_displays_); | |
| 1145 } else { | |
| 1146 for (Observer& observer : observers_) | |
| 1147 observer.OnDisplayModeChangeFailed(cached_displays_, attempted_state); | |
| 1148 } | |
| 1149 } | |
| 1150 | |
| 1151 void DisplayConfigurator::NotifyPowerStateObservers() { | |
| 1152 for (Observer& observer : observers_) | |
| 1153 observer.OnPowerStateChanged(current_power_state_); | |
| 1154 } | |
| 1155 | |
| 1156 int64_t DisplayConfigurator::AddVirtualDisplay(const gfx::Size& display_size) { | |
| 1157 if (last_virtual_display_id_ == 0xff) { | |
| 1158 LOG(WARNING) << "Exceeded virtual display id limit"; | |
| 1159 return display::kInvalidDisplayId; | |
| 1160 } | |
| 1161 | |
| 1162 DisplaySnapshotVirtual* virtual_snapshot = new DisplaySnapshotVirtual( | |
| 1163 display::GenerateDisplayID(kReservedManufacturerID, 0x0, | |
| 1164 ++last_virtual_display_id_), | |
| 1165 display_size); | |
| 1166 virtual_display_snapshots_.push_back(virtual_snapshot); | |
| 1167 ConfigureDisplays(); | |
| 1168 | |
| 1169 return virtual_snapshot->display_id(); | |
| 1170 } | |
| 1171 | |
| 1172 bool DisplayConfigurator::RemoveVirtualDisplay(int64_t display_id) { | |
| 1173 bool display_found = false; | |
| 1174 for (auto it = virtual_display_snapshots_.begin(); | |
| 1175 it != virtual_display_snapshots_.end(); ++it) { | |
| 1176 if ((*it)->display_id() == display_id) { | |
| 1177 virtual_display_snapshots_.erase(it); | |
| 1178 ConfigureDisplays(); | |
| 1179 display_found = true; | |
| 1180 break; | |
| 1181 } | |
| 1182 } | |
| 1183 | |
| 1184 if (!display_found) | |
| 1185 return false; | |
| 1186 | |
| 1187 int64_t max_display_id = 0; | |
| 1188 for (auto* display : virtual_display_snapshots_) | |
| 1189 max_display_id = std::max(max_display_id, display->display_id()); | |
| 1190 last_virtual_display_id_ = max_display_id & 0xff; | |
| 1191 | |
| 1192 return true; | |
| 1193 } | |
| 1194 | |
| 1195 bool DisplayConfigurator::IsDisplayOn() const { | |
| 1196 return current_power_state_ != chromeos::DISPLAY_POWER_ALL_OFF; | |
| 1197 } | |
| 1198 | |
| 1199 } // namespace ui | |
| OLD | NEW |