diff options
author | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-22 17:27:45 +0000 |
---|---|---|
committer | oshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-05-22 17:27:45 +0000 |
commit | c1f30dca390541354298d9db9f2571a50f569155 (patch) | |
tree | 58a875a3b128356962ddf8ab47f473da238846e4 /chromeos | |
parent | 7a44f9872eb3acf2acd6e25a3889bc42548e1d1f (diff) | |
download | chromium_src-c1f30dca390541354298d9db9f2571a50f569155.zip chromium_src-c1f30dca390541354298d9db9f2571a50f569155.tar.gz chromium_src-c1f30dca390541354298d9db9f2571a50f569155.tar.bz2 |
This is a subset CL of the crrev.com/14731027, and
counter part is crrev.com/15367003.
I'll send another CL that hooks them once both are landed.
* Add SoftwareMirroringController that implements software based mirroring.
* EnterStateOrFallbackToSoftwareMirroring will try to
fallback software mirroring if the hardware based
mirroring failed.
BUG=239776
TEST=covered by test
Review URL: https://chromiumcodereview.appspot.com/15448004
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@201554 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos')
-rw-r--r-- | chromeos/display/output_configurator.cc | 31 | ||||
-rw-r--r-- | chromeos/display/output_configurator.h | 23 | ||||
-rw-r--r-- | chromeos/display/output_configurator_unittest.cc | 143 |
3 files changed, 193 insertions, 4 deletions
diff --git a/chromeos/display/output_configurator.cc b/chromeos/display/output_configurator.cc index 44c4053..8214d0c 100644 --- a/chromeos/display/output_configurator.cc +++ b/chromeos/display/output_configurator.cc @@ -160,6 +160,7 @@ bool OutputConfigurator::TestApi::SendOutputChangeEvents(bool connected) { OutputConfigurator::OutputConfigurator() : state_controller_(NULL), + mirroring_controller_(NULL), configure_display_(base::chromeos::IsRunningOnChromeOS()), xrandr_event_base_(0), output_state_(STATE_INVALID), @@ -204,7 +205,8 @@ void OutputConfigurator::Start() { delegate_->InitXRandRExtension(&xrandr_event_base_); std::vector<OutputSnapshot> outputs = delegate_->GetOutputs(); - EnterState(GetOutputState(outputs, power_state_), power_state_, outputs); + EnterStateOrFallBackToSoftwareMirroring( + GetOutputState(outputs, power_state_), power_state_, outputs); // Force the DPMS on chrome startup as the driver doesn't always detect // that all displays are on when signing out. @@ -234,7 +236,8 @@ bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state, flags & kSetDisplayPowerOnlyIfSingleInternalDisplay; bool single_internal_display = outputs.size() == 1 && outputs[0].is_internal; if ((single_internal_display || !only_if_single_internal_display) && - EnterState(GetOutputState(outputs, power_state), power_state, outputs)) { + EnterStateOrFallBackToSoftwareMirroring( + GetOutputState(outputs, power_state), power_state, outputs)) { if (power_state != DISPLAY_POWER_ALL_OFF) { // Force the DPMS on since the driver doesn't always detect that it // should turn on. This is needed when coming back from idle suspend. @@ -256,7 +259,8 @@ bool OutputConfigurator::SetDisplayMode(OutputState new_state) { delegate_->GrabServer(); std::vector<OutputSnapshot> outputs = delegate_->GetOutputs(); - bool success = EnterState(new_state, power_state_, outputs); + bool success = EnterStateOrFallBackToSoftwareMirroring( + new_state, power_state_, outputs); delegate_->UngrabServer(); if (success) { @@ -344,7 +348,8 @@ void OutputConfigurator::ConfigureOutputs() { delegate_->GrabServer(); std::vector<OutputSnapshot> outputs = delegate_->GetOutputs(); OutputState new_state = GetOutputState(outputs, power_state_); - bool success = EnterState(new_state, power_state_, outputs); + bool success = EnterStateOrFallBackToSoftwareMirroring( + new_state, power_state_, outputs); delegate_->UngrabServer(); if (success) { @@ -360,6 +365,24 @@ void OutputConfigurator::NotifyOnDisplayChanged() { FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged()); } +bool OutputConfigurator::EnterStateOrFallBackToSoftwareMirroring( + OutputState output_state, + DisplayPowerState power_state, + const std::vector<OutputSnapshot>& outputs) { + bool success = EnterState(output_state, power_state, outputs); + if (mirroring_controller_) { + bool enable_software_mirroring = false; + if (!success && output_state == STATE_DUAL_MIRROR) { + if (output_state_ != STATE_DUAL_EXTENDED || power_state_ != power_state) + EnterState(STATE_DUAL_EXTENDED, power_state, outputs); + enable_software_mirroring = success = + output_state_ == STATE_DUAL_EXTENDED; + } + mirroring_controller_->SetSoftwareMirroring(enable_software_mirroring); + } + return success; +} + bool OutputConfigurator::EnterState( OutputState output_state, DisplayPowerState power_state, diff --git a/chromeos/display/output_configurator.h b/chromeos/display/output_configurator.h index 84de2d0..96c38b3 100644 --- a/chromeos/display/output_configurator.h +++ b/chromeos/display/output_configurator.h @@ -114,6 +114,15 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher { const std::vector<int64>& display_ids) const = 0; }; + // Interface for classes that implement software based mirroring. + class SoftwareMirroringController { + public: + virtual ~SoftwareMirroringController() {} + + // Called when the hardware mirroring failed. + virtual void SetSoftwareMirroring(bool enabled) = 0; + }; + // Interface for classes that perform actions on behalf of OutputController. class Delegate { public: @@ -227,6 +236,9 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher { void set_state_controller(StateController* controller) { state_controller_ = controller; } + void set_mirroring_controller(SoftwareMirroringController* controller) { + mirroring_controller_ = controller; + } // Replaces |delegate_| with |delegate| and sets |configure_display_| to // true. Should be called before Init(). @@ -290,6 +302,16 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher { void NotifyOnDisplayChanged(); // Switches to the state specified in |output_state| and |power_state|. + // If the hardware mirroring failed and |mirroring_controller_| is set, + // it switches to |STATE_DUAL_EXTENDED| and calls |SetSoftwareMirroring()| + // to enable software based mirroing. + // On success, updates |output_state_| and |power_state_| and returns true. + bool EnterStateOrFallBackToSoftwareMirroring( + OutputState output_state, + DisplayPowerState power_state, + const std::vector<OutputSnapshot>& outputs); + + // Switches to the state specified in |output_state| and |power_state|. // On success, updates |output_state_| and |power_state_| and returns true. bool EnterState(OutputState output_state, DisplayPowerState power_state, @@ -312,6 +334,7 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher { const OutputConfigurator::OutputSnapshot* output); StateController* state_controller_; + SoftwareMirroringController* mirroring_controller_; scoped_ptr<Delegate> delegate_; // Key of the map is the touch display's id, and the value of the map is the diff --git a/chromeos/display/output_configurator_unittest.cc b/chromeos/display/output_configurator_unittest.cc index 8f69ce7..606180c 100644 --- a/chromeos/display/output_configurator_unittest.cc +++ b/chromeos/display/output_configurator_unittest.cc @@ -217,6 +217,26 @@ class TestStateController : public OutputConfigurator::StateController { DISALLOW_COPY_AND_ASSIGN(TestStateController); }; +class TestMirroringController + : public OutputConfigurator::SoftwareMirroringController { + public: + TestMirroringController() : software_mirroring_enabled_(false) {} + virtual ~TestMirroringController() {} + + virtual void SetSoftwareMirroring(bool enabled) OVERRIDE { + software_mirroring_enabled_ = enabled; + } + + bool software_mirroring_enabled() const { + return software_mirroring_enabled_; + } + + private: + bool software_mirroring_enabled_; + + DISALLOW_COPY_AND_ASSIGN(TestMirroringController); +}; + class OutputConfiguratorTest : public testing::Test { public: OutputConfiguratorTest() @@ -228,6 +248,7 @@ class OutputConfiguratorTest : public testing::Test { configurator_.SetDelegateForTesting( scoped_ptr<OutputConfigurator::Delegate>(delegate_)); configurator_.set_state_controller(&state_controller_); + configurator_.set_mirroring_controller(&mirroring_controller_); OutputConfigurator::OutputSnapshot* o = &outputs_[0]; o->output = 1; @@ -260,6 +281,10 @@ class OutputConfiguratorTest : public testing::Test { delegate_->AddMode(kBigModeId, kBigModeWidth, kBigModeHeight, false); } + void DisableNativeMirroring() { + outputs_[0].mirror_mode = outputs_[1].mirror_mode = 0L; + } + protected: // Predefined modes that can be used by outputs. static const int kSmallModeId = 20; @@ -299,6 +324,7 @@ class OutputConfiguratorTest : public testing::Test { base::MessageLoop message_loop_; TestStateController state_controller_; + TestMirroringController mirroring_controller_; OutputConfigurator configurator_; TestDelegate* delegate_; // not owned OutputConfigurator::TestApi test_api_; @@ -331,6 +357,7 @@ TEST_F(OutputConfiguratorTest, ConnectSecondOutput) { kBigModeId, outputs_[1].output).c_str(), kUngrab, kProjectingOn, NULL), delegate_->GetActionsAndClear()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR)); EXPECT_EQ(JoinActions(kGrab, @@ -342,6 +369,7 @@ TEST_F(OutputConfiguratorTest, ConnectSecondOutput) { outputs_[1].output).c_str(), kUngrab, NULL), delegate_->GetActionsAndClear()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); // Disconnect the second output. UpdateOutputs(1); @@ -353,6 +381,42 @@ TEST_F(OutputConfiguratorTest, ConnectSecondOutput) { outputs_[0].output).c_str(), kUngrab, kProjectingOff, NULL), delegate_->GetActionsAndClear()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); + + // Software Mirroring + DisableNativeMirroring(); + UpdateOutputs(2); + state_controller_.set_state(STATE_DUAL_EXTENDED); + EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); + EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, + GetFramebufferAction(kBigModeWidth, kDualHeight, + outputs_[0].crtc, outputs_[1].crtc).c_str(), + GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, + outputs_[0].output).c_str(), + GetCrtcAction(outputs_[1].crtc, 0, + kSmallModeHeight + OutputConfigurator::kVerticalGap, + kBigModeId, outputs_[1].output).c_str(), + kUngrab, kProjectingOn, NULL), + delegate_->GetActionsAndClear()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); + + EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_MIRROR)); + EXPECT_EQ(JoinActions(kGrab, kUngrab, NULL), + delegate_->GetActionsAndClear()); + EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); + EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); + + // Disconnect the second output. + UpdateOutputs(1); + EXPECT_TRUE(test_api_.SendOutputChangeEvents(false)); + EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, + GetFramebufferAction(kSmallModeWidth, kSmallModeHeight, + outputs_[0].crtc, 0).c_str(), + GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, + outputs_[0].output).c_str(), + kUngrab, kProjectingOff, NULL), + delegate_->GetActionsAndClear()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); } TEST_F(OutputConfiguratorTest, SetDisplayPower) { @@ -370,6 +434,7 @@ TEST_F(OutputConfiguratorTest, SetDisplayPower) { outputs_[1].output).c_str(), kUngrab, kProjectingOn, NULL), delegate_->GetActionsAndClear()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); // Turning off the internal display should switch the external display to // its native mode. @@ -384,6 +449,7 @@ TEST_F(OutputConfiguratorTest, SetDisplayPower) { outputs_[1].output).c_str(), kForceDPMS, kUngrab, NULL), delegate_->GetActionsAndClear()); + EXPECT_EQ(STATE_SINGLE, configurator_.output_state()); // When all displays are turned off, the framebuffer should switch back // to the mirrored size. @@ -398,6 +464,8 @@ TEST_F(OutputConfiguratorTest, SetDisplayPower) { outputs_[1].output).c_str(), kUngrab, NULL), delegate_->GetActionsAndClear()); + EXPECT_EQ(STATE_DUAL_MIRROR, configurator_.output_state()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); // Turn all displays on and check that mirroring is still used. configurator_.SetDisplayPower(DISPLAY_POWER_ALL_ON, @@ -411,6 +479,81 @@ TEST_F(OutputConfiguratorTest, SetDisplayPower) { outputs_[1].output).c_str(), kForceDPMS, kUngrab, NULL), delegate_->GetActionsAndClear()); + EXPECT_EQ(STATE_DUAL_MIRROR, configurator_.output_state()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); + + // Software Mirroring + DisableNativeMirroring(); + UpdateOutputs(2); + + const int kDualHeight = + kSmallModeHeight + OutputConfigurator::kVerticalGap + kBigModeHeight; + + state_controller_.set_state(STATE_DUAL_MIRROR); + EXPECT_TRUE(test_api_.SendOutputChangeEvents(true)); + // Move to extended + EXPECT_EQ(JoinActions(kUpdateXRandR, kGrab, + GetFramebufferAction(kBigModeWidth, kDualHeight, + outputs_[0].crtc, outputs_[1].crtc).c_str(), + GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, + outputs_[0].output).c_str(), + GetCrtcAction(outputs_[1].crtc, 0, + kSmallModeHeight + OutputConfigurator::kVerticalGap, + kBigModeId, outputs_[1].output).c_str(), + kUngrab, kProjectingOn, NULL), + delegate_->GetActionsAndClear()); + EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); + EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); + + // Turning off the internal display should switch the external display to + // its native mode. + configurator_.SetDisplayPower(DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON, + OutputConfigurator::kSetDisplayPowerNoFlags); + EXPECT_EQ(JoinActions(kGrab, + GetFramebufferAction(kBigModeWidth, kBigModeHeight, + outputs_[0].crtc, outputs_[1].crtc).c_str(), + GetCrtcAction(outputs_[0].crtc, 0, 0, 0, + outputs_[0].output).c_str(), + GetCrtcAction(outputs_[1].crtc, 0, 0, kBigModeId, + outputs_[1].output).c_str(), + kForceDPMS, kUngrab, NULL), + delegate_->GetActionsAndClear()); + EXPECT_EQ(STATE_SINGLE, configurator_.output_state()); + EXPECT_FALSE(mirroring_controller_.software_mirroring_enabled()); + + // When all displays are turned off, the framebuffer should switch back + // to the extended + software mirroring. + configurator_.SetDisplayPower(DISPLAY_POWER_ALL_OFF, + OutputConfigurator::kSetDisplayPowerNoFlags); + EXPECT_EQ(JoinActions(kGrab, + GetFramebufferAction(kBigModeWidth, kDualHeight, + outputs_[0].crtc, outputs_[1].crtc).c_str(), + GetCrtcAction(outputs_[0].crtc, 0, 0, 0, + outputs_[0].output).c_str(), + GetCrtcAction(outputs_[1].crtc, 0, + kSmallModeHeight + OutputConfigurator::kVerticalGap, + 0, outputs_[1].output).c_str(), + kUngrab, NULL), + delegate_->GetActionsAndClear()); + EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); + EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); + + // Turn all displays on and check that mirroring is still used. + configurator_.SetDisplayPower(DISPLAY_POWER_ALL_ON, + OutputConfigurator::kSetDisplayPowerNoFlags); + EXPECT_EQ(JoinActions(kGrab, + GetFramebufferAction(kBigModeWidth, kDualHeight, + outputs_[0].crtc, outputs_[1].crtc).c_str(), + GetCrtcAction(outputs_[0].crtc, 0, 0, kSmallModeId, + outputs_[0].output).c_str(), + GetCrtcAction(outputs_[1].crtc, 0, + kSmallModeHeight + OutputConfigurator::kVerticalGap, + kBigModeId, outputs_[1].output).c_str(), + kForceDPMS, kUngrab, NULL), + delegate_->GetActionsAndClear()); + EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state()); + EXPECT_TRUE(mirroring_controller_.software_mirroring_enabled()); + } TEST_F(OutputConfiguratorTest, SuspendAndResume) { |