1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chromeos/display/output_configurator.h" 6 7 #include <cstdarg> 8 #include <map> 9 #include <string> 10 #include <vector> 11 12 #include "base/basictypes.h" 13 #include "base/compiler_specific.h" 14 #include "base/message_loop/message_loop.h" 15 #include "base/strings/stringprintf.h" 16 #include "testing/gtest/include/gtest/gtest.h" 17 18 namespace chromeos { 19 20 namespace { 21 22 // Strings returned by TestDelegate::GetActionsAndClear() to describe various 23 // actions that were performed. 24 const char kInitXRandR[] = "init"; 25 const char kUpdateXRandR[] = "update"; 26 const char kGrab[] = "grab"; 27 const char kUngrab[] = "ungrab"; 28 const char kSync[] = "sync"; 29 const char kForceDPMS[] = "dpms"; 30 const char kProjectingOn[] = "projecting"; 31 const char kProjectingOff[] = "not_projecting"; 32 33 // String returned by TestDelegate::GetActionsAndClear() if no actions were 34 // requested. 35 const char kNoActions[] = ""; 36 37 // Returns a string describing a TestDelegate::SetBackgroundColor() call. 38 std::string GetBackgroundAction(uint32 color_argb) { 39 return base::StringPrintf("background(0x%x)", color_argb); 40 } 41 42 // Returns a string describing a TestDelegate::ConfigureCrtc() call. 43 std::string GetCrtcAction(RRCrtc crtc, 44 int x, 45 int y, 46 RRMode mode, 47 RROutput output) { 48 return base::StringPrintf("crtc(crtc=%lu,x=%d,y=%d,mode=%lu,output=%lu)", 49 crtc, x, y, mode, output); 50 } 51 52 // Returns a string describing a TestDelegate::CreateFramebuffer() call. 53 std::string GetFramebufferAction(int width, 54 int height, 55 RRCrtc crtc1, 56 RRCrtc crtc2) { 57 return base::StringPrintf( 58 "framebuffer(width=%d,height=%d,crtc1=%lu,crtc2=%lu)", 59 width, height, crtc1, crtc2); 60 } 61 62 // Returns a string describing a TestDelegate::ConfigureCTM() call. 63 std::string GetCTMAction( 64 int device_id, 65 const OutputConfigurator::CoordinateTransformation& ctm) { 66 return base::StringPrintf("ctm(id=%d,transform=(%f,%f,%f,%f))", device_id, 67 ctm.x_scale, ctm.x_offset, ctm.y_scale, ctm.y_offset); 68 } 69 70 // Joins a sequence of strings describing actions (e.g. kScreenDim) such 71 // that they can be compared against a string returned by 72 // TestDelegate::GetActionsAndClear(). The list of actions must be 73 // terminated by a NULL pointer. 74 std::string JoinActions(const char* action, ...) { 75 std::string actions; 76 77 va_list arg_list; 78 va_start(arg_list, action); 79 while (action) { 80 if (!actions.empty()) 81 actions += ","; 82 actions += action; 83 action = va_arg(arg_list, const char*); 84 } 85 va_end(arg_list); 86 return actions; 87 } 88 89 class TestDelegate : public OutputConfigurator::Delegate { 90 public: 91 static const int kXRandREventBase = 10; 92 93 TestDelegate() {} 94 virtual ~TestDelegate() {} 95 96 void set_outputs( 97 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) { 98 outputs_ = outputs; 99 } 100 101 // Returns a comma-separated string describing the actions that were 102 // requested since the previous call to GetActionsAndClear() (i.e. 103 // results are non-repeatable). 104 std::string GetActionsAndClear() { 105 std::string actions = actions_; 106 actions_.clear(); 107 return actions; 108 } 109 110 // Adds a mode to be returned by GetModeDetails(). 111 void AddMode(RRMode mode, int width, int height, bool interlaced) { 112 modes_[mode] = ModeDetails(width, height, interlaced); 113 } 114 115 // OutputConfigurator::Delegate overrides: 116 virtual void SetPanelFittingEnabled(bool enabled) OVERRIDE {} 117 virtual void InitXRandRExtension(int* event_base) OVERRIDE { 118 AppendAction(kInitXRandR); 119 *event_base = kXRandREventBase; 120 } 121 virtual void UpdateXRandRConfiguration( 122 const base::NativeEvent& event) OVERRIDE { AppendAction(kUpdateXRandR); } 123 virtual void GrabServer() OVERRIDE { AppendAction(kGrab); } 124 virtual void UngrabServer() OVERRIDE { AppendAction(kUngrab); } 125 virtual void SyncWithServer() OVERRIDE { AppendAction(kSync); } 126 virtual void SetBackgroundColor(uint32 color_argb) OVERRIDE { 127 AppendAction(GetBackgroundAction(color_argb)); 128 } 129 virtual void ForceDPMSOn() OVERRIDE { AppendAction(kForceDPMS); } 130 virtual std::vector<OutputConfigurator::OutputSnapshot> GetOutputs( 131 const OutputConfigurator::StateController* controller) OVERRIDE { 132 return outputs_; 133 } 134 virtual bool GetModeDetails( 135 RRMode mode, 136 int* width, 137 int* height, 138 bool* interlaced) OVERRIDE { 139 std::map<RRMode, ModeDetails>::const_iterator it = modes_.find(mode); 140 if (it == modes_.end()) 141 return false; 142 143 if (width) 144 *width = it->second.width; 145 if (height) 146 *height = it->second.height; 147 if (interlaced) 148 *interlaced = it->second.interlaced; 149 return true; 150 } 151 virtual bool ConfigureCrtc(RRCrtc crtc, 152 RRMode mode, 153 RROutput output, 154 int x, 155 int y) OVERRIDE { 156 AppendAction(GetCrtcAction(crtc, x, y, mode, output)); 157 return true; 158 } 159 virtual void CreateFrameBuffer( 160 int width, 161 int height, 162 const std::vector<OutputConfigurator::OutputSnapshot>& outputs) OVERRIDE { 163 AppendAction( 164 GetFramebufferAction(width, 165 height, 166 outputs.size() >= 1 ? outputs[0].crtc : 0, 167 outputs.size() >= 2 ? outputs[1].crtc : 0)); 168 } 169 virtual void ConfigureCTM( 170 int touch_device_id, 171 const OutputConfigurator::CoordinateTransformation& ctm) OVERRIDE { 172 AppendAction(GetCTMAction(touch_device_id, ctm)); 173 } 174 virtual void SendProjectingStateToPowerManager(bool projecting) OVERRIDE { 175 AppendAction(projecting ? kProjectingOn : kProjectingOff); 176 } 177 178 private: 179 struct ModeDetails { 180 ModeDetails() : width(0), height(0), interlaced(false) {} 181 ModeDetails(int width, int height, bool interlaced) 182 : width(width), 183 height(height), 184 interlaced(interlaced) {} 185 186 int width; 187 int height; 188 bool interlaced; 189 }; 190 191 void AppendAction(const std::string& action) { 192 if (!actions_.empty()) 193 actions_ += ","; 194 actions_ += action; 195 } 196 197 std::map<RRMode, ModeDetails> modes_; 198 199 // Outputs to be returned by GetOutputs(). 200 std::vector<OutputConfigurator::OutputSnapshot> outputs_; 201 202 std::string actions_; 203 204 DISALLOW_COPY_AND_ASSIGN(TestDelegate); 205 }; 206 207 class TestStateController : public OutputConfigurator::StateController { 208 public: 209 TestStateController() : state_(STATE_DUAL_EXTENDED) {} 210 virtual ~TestStateController() {} 211 212 void set_state(OutputState state) { state_ = state; } 213 214 // OutputConfigurator::StateController overrides: 215 virtual OutputState GetStateForDisplayIds( 216 const std::vector<int64>& outputs) const OVERRIDE { return state_; } 217 virtual bool GetResolutionForDisplayId( 218 int64 display_id, 219 int *width, 220 int *height) const OVERRIDE { 221 return false; 222 } 223 224 private: 225 OutputState state_; 226 227 DISALLOW_COPY_AND_ASSIGN(TestStateController); 228 }; 229 230 class TestMirroringController 231 : public OutputConfigurator::SoftwareMirroringController { 232 public: 233 TestMirroringController() : software_mirroring_enabled_(false) {} 234 virtual ~TestMirroringController() {} 235 236 virtual void SetSoftwareMirroring(bool enabled) OVERRIDE { 237 software_mirroring_enabled_ = enabled; 238 } 239 240 bool software_mirroring_enabled() const { 241 return software_mirroring_enabled_; 242 } 243 244 private: 245 bool software_mirroring_enabled_; 246 247 DISALLOW_COPY_AND_ASSIGN(TestMirroringController); 248 }; 249 250 class OutputConfiguratorTest : public testing::Test { 251 public: 252 OutputConfiguratorTest() 253 : test_api_(&configurator_, TestDelegate::kXRandREventBase) {} 254 virtual ~OutputConfiguratorTest() {} 255 256 virtual void SetUp() OVERRIDE { 257 delegate_ = new TestDelegate(); 258 configurator_.SetDelegateForTesting( 259 scoped_ptr<OutputConfigurator::Delegate>(delegate_)); 260 configurator_.set_state_controller(&state_controller_); 261 configurator_.set_mirroring_controller(&mirroring_controller_); 262 263 OutputConfigurator::OutputSnapshot* o = &outputs_[0]; 264 o->output = 1; 265 o->crtc = 10; 266 o->current_mode = kSmallModeId; 267 o->native_mode = kSmallModeId; 268 o->selected_mode = kSmallModeId; 269 o->mirror_mode = kSmallModeId; 270 o->x = 0; 271 o->y = 0; 272 o->is_internal = true; 273 o->is_aspect_preserving_scaling = true; 274 o->touch_device_id = 0; 275 o->has_display_id = true; 276 277 o = &outputs_[1]; 278 o->output = 2; 279 o->crtc = 11; 280 o->current_mode = kBigModeId; 281 o->native_mode = kBigModeId; 282 o->selected_mode = kBigModeId; 283 o->mirror_mode = kSmallModeId; 284 o->x = 0; 285 o->y = 0; 286 o->is_internal = false; 287 o->is_aspect_preserving_scaling = true; 288 o->touch_device_id = 0; 289 o->has_display_id = true; 290 291 UpdateOutputs(2); 292 delegate_->AddMode(kSmallModeId, kSmallModeWidth, kSmallModeHeight, false); 293 delegate_->AddMode(kBigModeId, kBigModeWidth, kBigModeHeight, false); 294 } 295 296 void DisableNativeMirroring() { 297 outputs_[0].mirror_mode = outputs_[1].mirror_mode = 0L; 298 } 299 300 protected: 301 // Predefined modes that can be used by outputs. 302 static const int kSmallModeId = 20; 303 static const int kSmallModeWidth = 1366; 304 static const int kSmallModeHeight = 768; 305 306 static const int kBigModeId = 21; 307 static const int kBigModeWidth = 2560; 308 static const int kBigModeHeight = 1600; 309 310 // Configures |delegate_| to return the first |num_outputs| entries from 311 // |outputs_|. 312 virtual void UpdateOutputs(size_t num_outputs) { 313 ASSERT_LE(num_outputs, arraysize(outputs_)); 314 std::vector<OutputConfigurator::OutputSnapshot> outputs; 315 for (size_t i = 0; i < num_outputs; ++i) 316 outputs.push_back(outputs_[i]); 317 delegate_->set_outputs(outputs); 318 } 319 320 // Initializes |configurator_| with a single internal display. 321 virtual void InitWithSingleOutput() { 322 UpdateOutputs(1); 323 EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); 324 configurator_.Init(false); 325 EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); 326 configurator_.Start(0); 327 EXPECT_EQ(JoinActions(kGrab, kInitXRandR, 328 GetFramebufferAction(kSmallModeWidth, 329 kSmallModeHeight, outputs_[0].crtc, 0).c_str(), 330 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 331 outputs_[0].output).c_str(), 332 kForceDPMS, kUngrab, kProjectingOff, NULL), 333 delegate_->GetActionsAndClear()); 334 } 335 336 base::MessageLoop message_loop_; 337 TestStateController state_controller_; 338 TestMirroringController mirroring_controller_; 339 OutputConfigurator configurator_; 340 TestDelegate* delegate_; // not owned 341 OutputConfigurator::TestApi test_api_; 342 343 OutputConfigurator::OutputSnapshot outputs_[2]; 344 345 private: 346 DISALLOW_COPY_AND_ASSIGN(OutputConfiguratorTest); 347 }; 348 349 } // namespace 350 351 TEST_F(OutputConfiguratorTest, ConnectSecondOutput) { 352 InitWithSingleOutput(); 353 354 // Connect a second output and check that the configurator enters 355 // extended mode. 356 UpdateOutputs(2); 357 const int kDualHeight = 358 kSmallModeHeight + OutputConfigurator::kVerticalGap + kBigModeHeight; 359 state_controller_.set_state(STATE_DUAL_EXTENDED); 360 EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); 361 EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, 362 GetFramebufferAction(kBigModeWidth, kDualHeight, 363 outputs_[0].crtc, outputs_[1].crtc).c_str(), 364 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 365 outputs_[0].output).c_str(), 366 GetCrtcAction(outputs_[1].crtc, 0, 367 kSmallModeHeight + OutputConfigurator::kVerticalGap, 368 kBigModeId, outputs_[1].output).c_str(), 369 kUngrab, kProjectingOn, NULL), 370 delegate_->GetActionsAndClear()); 371 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 372 373 EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR)); 374 EXPECT_EQ(JoinActions(kGrab, 375 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 376 outputs_[0].crtc, outputs_[1].crtc).c_str(), 377 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 378 outputs_[0].output).c_str(), 379 GetCrtcAction(outputs_[1].crtc, 0, 0, kSmallModeId, 380 outputs_[1].output).c_str(), 381 kUngrab, NULL), 382 delegate_->GetActionsAndClear()); 383 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 384 385 // Disconnect the second output. 386 UpdateOutputs(1); 387 EXPECT_TRUE(test_api_.SendOutputChangeEvents(false)); 388 EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, 389 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 390 outputs_[0].crtc, 0).c_str(), 391 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 392 outputs_[0].output).c_str(), 393 kUngrab, kProjectingOff, NULL), 394 delegate_->GetActionsAndClear()); 395 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 396 397 // Software Mirroring 398 DisableNativeMirroring(); 399 UpdateOutputs(2); 400 state_controller_.set_state(STATE_DUAL_EXTENDED); 401 EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); 402 EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, 403 GetFramebufferAction(kBigModeWidth, kDualHeight, 404 outputs_[0].crtc, outputs_[1].crtc).c_str(), 405 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 406 outputs_[0].output).c_str(), 407 GetCrtcAction(outputs_[1].crtc, 0, 408 kSmallModeHeight + OutputConfigurator::kVerticalGap, 409 kBigModeId, outputs_[1].output).c_str(), 410 kUngrab, kProjectingOn, NULL), 411 delegate_->GetActionsAndClear()); 412 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 413 414 EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR)); 415 EXPECT_EQ(JoinActions(kGrab, kUngrab, NULL), 416 delegate_->GetActionsAndClear()); 417 EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); 418 EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); 419 420 // Setting STATE_DUAL_MIRROR should try to reconfigure 421 EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_EXTENDED)); 422 EXPECT_EQ(JoinActions(NULL), delegate_->GetActionsAndClear()); 423 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 424 425 // Set back to software mirror mode. 426 EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR)); 427 EXPECT_EQ(JoinActions(kGrab, kUngrab, NULL), 428 delegate_->GetActionsAndClear()); 429 EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); 430 EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); 431 432 // Disconnect the second output. 433 UpdateOutputs(1); 434 EXPECT_TRUE(test_api_.SendOutputChangeEvents(false)); 435 EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, 436 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 437 outputs_[0].crtc, 0).c_str(), 438 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 439 outputs_[0].output).c_str(), 440 kUngrab, kProjectingOff, NULL), 441 delegate_->GetActionsAndClear()); 442 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 443 } 444 445 TEST_F(OutputConfiguratorTest, SetDisplayPower) { 446 InitWithSingleOutput(); 447 448 UpdateOutputs(2); 449 state_controller_.set_state(STATE_DUAL_MIRROR); 450 EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); 451 EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, 452 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 453 outputs_[0].crtc, outputs_[1].crtc).c_str(), 454 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 455 outputs_[0].output).c_str(), 456 GetCrtcAction(outputs_[1].crtc, 0, 0, kSmallModeId, 457 outputs_[1].output).c_str(), 458 kUngrab, kProjectingOn, NULL), 459 delegate_->GetActionsAndClear()); 460 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 461 462 // Turning off the internal display should switch the external display to 463 // its native mode. 464 configurator_.SetDisplayPower(DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, 465 OutputConfigurator::kSetDisplayPowerNoFlags); 466 EXPECT_EQ(JoinActions(kGrab, 467 GetFramebufferAction(kBigModeWidth, kBigModeHeight, 468 outputs_[0].crtc, outputs_[1].crtc).c_str(), 469 GetCrtcAction(outputs_[0].crtc, 0, 0, 0, 470 outputs_[0].output).c_str(), 471 GetCrtcAction(outputs_[1].crtc, 0, 0, kBigModeId, 472 outputs_[1].output).c_str(), 473 kForceDPMS, kUngrab, NULL), 474 delegate_->GetActionsAndClear()); 475 EXPECT_EQ(STATE_SINGLE, configurator_.output_state()); 476 477 // When all displays are turned off, the framebuffer should switch back 478 // to the mirrored size. 479 configurator_.SetDisplayPower(DISPLAY_POWER_ALL_OFF, 480 OutputConfigurator::kSetDisplayPowerNoFlags); 481 EXPECT_EQ(JoinActions(kGrab, 482 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 483 outputs_[0].crtc, outputs_[1].crtc).c_str(), 484 GetCrtcAction(outputs_[0].crtc, 0, 0, 0, 485 outputs_[0].output).c_str(), 486 GetCrtcAction(outputs_[1].crtc, 0, 0, 0, 487 outputs_[1].output).c_str(), 488 kUngrab, NULL), 489 delegate_->GetActionsAndClear()); 490 EXPECT_EQ(STATE_DUAL_MIRROR, configurator_.output_state()); 491 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 492 493 // Turn all displays on and check that mirroring is still used. 494 configurator_.SetDisplayPower(DISPLAY_POWER_ALL_ON, 495 OutputConfigurator::kSetDisplayPowerNoFlags); 496 EXPECT_EQ(JoinActions(kGrab, 497 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 498 outputs_[0].crtc, outputs_[1].crtc).c_str(), 499 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 500 outputs_[0].output).c_str(), 501 GetCrtcAction(outputs_[1].crtc, 0, 0, kSmallModeId, 502 outputs_[1].output).c_str(), 503 kForceDPMS, kUngrab, NULL), 504 delegate_->GetActionsAndClear()); 505 EXPECT_EQ(STATE_DUAL_MIRROR, configurator_.output_state()); 506 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 507 508 // Software Mirroring 509 DisableNativeMirroring(); 510 UpdateOutputs(2); 511 512 const int kDualHeight = 513 kSmallModeHeight + OutputConfigurator::kVerticalGap + kBigModeHeight; 514 515 state_controller_.set_state(STATE_DUAL_MIRROR); 516 EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); 517 // Move to extended 518 EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, 519 GetFramebufferAction(kBigModeWidth, kDualHeight, 520 outputs_[0].crtc, outputs_[1].crtc).c_str(), 521 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 522 outputs_[0].output).c_str(), 523 GetCrtcAction(outputs_[1].crtc, 0, 524 kSmallModeHeight + OutputConfigurator::kVerticalGap, 525 kBigModeId, outputs_[1].output).c_str(), 526 kUngrab, kProjectingOn, NULL), 527 delegate_->GetActionsAndClear()); 528 EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); 529 EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); 530 531 // Turning off the internal display should switch the external display to 532 // its native mode. 533 configurator_.SetDisplayPower(DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, 534 OutputConfigurator::kSetDisplayPowerNoFlags); 535 EXPECT_EQ(JoinActions(kGrab, 536 GetFramebufferAction(kBigModeWidth, kBigModeHeight, 537 outputs_[0].crtc, outputs_[1].crtc).c_str(), 538 GetCrtcAction(outputs_[0].crtc, 0, 0, 0, 539 outputs_[0].output).c_str(), 540 GetCrtcAction(outputs_[1].crtc, 0, 0, kBigModeId, 541 outputs_[1].output).c_str(), 542 kForceDPMS, kUngrab, NULL), 543 delegate_->GetActionsAndClear()); 544 EXPECT_EQ(STATE_SINGLE, configurator_.output_state()); 545 EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); 546 547 // When all displays are turned off, the framebuffer should switch back 548 // to the extended + software mirroring. 549 configurator_.SetDisplayPower(DISPLAY_POWER_ALL_OFF, 550 OutputConfigurator::kSetDisplayPowerNoFlags); 551 EXPECT_EQ(JoinActions(kGrab, 552 GetFramebufferAction(kBigModeWidth, kDualHeight, 553 outputs_[0].crtc, outputs_[1].crtc).c_str(), 554 GetCrtcAction(outputs_[0].crtc, 0, 0, 0, 555 outputs_[0].output).c_str(), 556 GetCrtcAction(outputs_[1].crtc, 0, 557 kSmallModeHeight + OutputConfigurator::kVerticalGap, 558 0, outputs_[1].output).c_str(), 559 kUngrab, NULL), 560 delegate_->GetActionsAndClear()); 561 EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); 562 EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); 563 564 // Turn all displays on and check that mirroring is still used. 565 configurator_.SetDisplayPower(DISPLAY_POWER_ALL_ON, 566 OutputConfigurator::kSetDisplayPowerNoFlags); 567 EXPECT_EQ(JoinActions(kGrab, 568 GetFramebufferAction(kBigModeWidth, kDualHeight, 569 outputs_[0].crtc, outputs_[1].crtc).c_str(), 570 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 571 outputs_[0].output).c_str(), 572 GetCrtcAction(outputs_[1].crtc, 0, 573 kSmallModeHeight + OutputConfigurator::kVerticalGap, 574 kBigModeId, outputs_[1].output).c_str(), 575 kForceDPMS, kUngrab, NULL), 576 delegate_->GetActionsAndClear()); 577 EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); 578 EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); 579 580 } 581 582 TEST_F(OutputConfiguratorTest, SuspendAndResume) { 583 InitWithSingleOutput(); 584 585 // No preparation is needed before suspending when the display is already 586 // on. The configurator should still reprobe on resume in case a display 587 // was connected while suspended. 588 configurator_.SuspendDisplays(); 589 EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); 590 configurator_.ResumeDisplays(); 591 EXPECT_EQ(JoinActions(kGrab, 592 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 593 outputs_[0].crtc, 0).c_str(), 594 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 595 outputs_[0].output).c_str(), 596 kForceDPMS, kUngrab, NULL), 597 delegate_->GetActionsAndClear()); 598 599 // Now turn the display off before suspending and check that the 600 // configurator turns it back on and syncs with the server. 601 configurator_.SetDisplayPower(DISPLAY_POWER_ALL_OFF, 602 OutputConfigurator::kSetDisplayPowerNoFlags); 603 EXPECT_EQ(JoinActions(kGrab, 604 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 605 outputs_[0].crtc, 0).c_str(), 606 GetCrtcAction(outputs_[0].crtc, 0, 0, 0, 607 outputs_[0].output).c_str(), 608 kUngrab, NULL), 609 delegate_->GetActionsAndClear()); 610 611 configurator_.SuspendDisplays(); 612 EXPECT_EQ(JoinActions(kGrab, 613 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 614 outputs_[0].crtc, 0).c_str(), 615 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 616 outputs_[0].output).c_str(), 617 kForceDPMS, kUngrab, kSync, NULL), 618 delegate_->GetActionsAndClear()); 619 620 configurator_.ResumeDisplays(); 621 EXPECT_EQ(JoinActions(kGrab, 622 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 623 outputs_[0].crtc, 0).c_str(), 624 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 625 outputs_[0].output).c_str(), 626 kForceDPMS, kUngrab, NULL), 627 delegate_->GetActionsAndClear()); 628 629 // If a second, external display is connected, the displays shouldn't be 630 // powered back on before suspending. 631 UpdateOutputs(2); 632 state_controller_.set_state(STATE_DUAL_MIRROR); 633 EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); 634 EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, 635 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 636 outputs_[0].crtc, outputs_[1].crtc).c_str(), 637 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 638 outputs_[0].output).c_str(), 639 GetCrtcAction(outputs_[1].crtc, 0, 0, kSmallModeId, 640 outputs_[1].output).c_str(), 641 kUngrab, kProjectingOn, NULL), 642 delegate_->GetActionsAndClear()); 643 644 configurator_.SetDisplayPower(DISPLAY_POWER_ALL_OFF, 645 OutputConfigurator::kSetDisplayPowerNoFlags); 646 EXPECT_EQ(JoinActions(kGrab, 647 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 648 outputs_[0].crtc, outputs_[1].crtc).c_str(), 649 GetCrtcAction(outputs_[0].crtc, 0, 0, 0, 650 outputs_[0].output).c_str(), 651 GetCrtcAction(outputs_[1].crtc, 0, 0, 0, 652 outputs_[1].output).c_str(), 653 kUngrab, NULL), 654 delegate_->GetActionsAndClear()); 655 656 configurator_.SuspendDisplays(); 657 EXPECT_EQ(JoinActions(kGrab, kUngrab, kSync, NULL), 658 delegate_->GetActionsAndClear()); 659 660 // If a display is disconnected while resuming, the configurator should 661 // pick up the change. 662 UpdateOutputs(1); 663 configurator_.ResumeDisplays(); 664 EXPECT_EQ(JoinActions(kGrab, 665 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 666 outputs_[0].crtc, 0).c_str(), 667 GetCrtcAction(outputs_[0].crtc, 0, 0, 0, 668 outputs_[0].output).c_str(), 669 kUngrab, NULL), 670 delegate_->GetActionsAndClear()); 671 } 672 673 TEST_F(OutputConfiguratorTest, Headless) { 674 UpdateOutputs(0); 675 EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); 676 configurator_.Init(false); 677 EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); 678 configurator_.Start(0); 679 EXPECT_EQ(JoinActions(kGrab, kInitXRandR, kForceDPMS, kUngrab, 680 kProjectingOff, NULL), 681 delegate_->GetActionsAndClear()); 682 683 // Not much should happen when the display power state is changed while 684 // no displays are connected. 685 configurator_.SetDisplayPower(DISPLAY_POWER_ALL_OFF, 686 OutputConfigurator::kSetDisplayPowerNoFlags); 687 EXPECT_EQ(JoinActions(kGrab, kUngrab, NULL), delegate_->GetActionsAndClear()); 688 configurator_.SetDisplayPower(DISPLAY_POWER_ALL_ON, 689 OutputConfigurator::kSetDisplayPowerNoFlags); 690 EXPECT_EQ(JoinActions(kGrab, kForceDPMS, kUngrab, NULL), 691 delegate_->GetActionsAndClear()); 692 693 // Connect an external display and check that it's configured correctly. 694 outputs_[0].is_internal = false; 695 outputs_[0].native_mode = kBigModeId; 696 outputs_[0].selected_mode = kBigModeId; 697 UpdateOutputs(1); 698 EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); 699 EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, 700 GetFramebufferAction(kBigModeWidth, kBigModeHeight, 701 outputs_[0].crtc, 0).c_str(), 702 GetCrtcAction(outputs_[0].crtc, 0, 0, kBigModeId, 703 outputs_[0].output).c_str(), 704 kUngrab, kProjectingOff, NULL), 705 delegate_->GetActionsAndClear()); 706 } 707 708 TEST_F(OutputConfiguratorTest, StartWithTwoOutputs) { 709 UpdateOutputs(2); 710 EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); 711 configurator_.Init(false); 712 EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); 713 714 state_controller_.set_state(STATE_DUAL_MIRROR); 715 configurator_.Start(0); 716 EXPECT_EQ(JoinActions(kGrab, kInitXRandR, 717 GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, 718 outputs_[0].crtc, outputs_[1].crtc).c_str(), 719 GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, 720 outputs_[0].output).c_str(), 721 GetCrtcAction(outputs_[1].crtc, 0, 0, kSmallModeId, 722 outputs_[1].output).c_str(), 723 kForceDPMS, kUngrab, kProjectingOn, NULL), 724 delegate_->GetActionsAndClear()); 725 } 726 727 TEST_F(OutputConfiguratorTest, InvalidOutputStates) { 728 UpdateOutputs(0); 729 EXPECT_EQ(kNoActions, delegate_->GetActionsAndClear()); 730 configurator_.Init(false); 731 configurator_.Start(0); 732 EXPECT_TRUE(configurator_.SetDisplayMode(STATE_HEADLESS)); 733 EXPECT_FALSE(configurator_.SetDisplayMode(STATE_SINGLE)); 734 EXPECT_FALSE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR)); 735 EXPECT_FALSE(configurator_.SetDisplayMode(STATE_DUAL_EXTENDED)); 736 737 UpdateOutputs(1); 738 EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); 739 EXPECT_FALSE(configurator_.SetDisplayMode(STATE_HEADLESS)); 740 EXPECT_TRUE(configurator_.SetDisplayMode(STATE_SINGLE)); 741 EXPECT_FALSE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR)); 742 EXPECT_FALSE(configurator_.SetDisplayMode(STATE_DUAL_EXTENDED)); 743 744 UpdateOutputs(2); 745 state_controller_.set_state(STATE_DUAL_EXTENDED); 746 EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); 747 EXPECT_FALSE(configurator_.SetDisplayMode(STATE_HEADLESS)); 748 EXPECT_FALSE(configurator_.SetDisplayMode(STATE_SINGLE)); 749 EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR)); 750 EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_EXTENDED)); 751 } 752 753 TEST_F(OutputConfiguratorTest, GetOutputStateForDisplays) { 754 outputs_[0].has_display_id = false; 755 UpdateOutputs(2); 756 757 configurator_.Init(false); 758 configurator_.Start(0); 759 760 state_controller_.set_state(STATE_DUAL_MIRROR); 761 test_api_.SendOutputChangeEvents(true); 762 EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); 763 764 outputs_[0].has_display_id = true; 765 UpdateOutputs(2); 766 test_api_.SendOutputChangeEvents(true); 767 EXPECT_EQ(STATE_DUAL_MIRROR, configurator_.output_state()); 768 } 769 770 } // namespace chromeos 771