summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authordisher@chromium.org <disher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-02 22:12:25 +0000
committerdisher@chromium.org <disher@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-02 22:12:25 +0000
commit70b3e7d594425427ee8daac76db9eb48edc688ec (patch)
treea1b4971a38d8fed709e142b7b9dcd10a97090704
parent286cbcf3a10620a824408b0841e6e37bfe5eb2a2 (diff)
downloadchromium_src-70b3e7d594425427ee8daac76db9eb48edc688ec.zip
chromium_src-70b3e7d594425427ee8daac76db9eb48edc688ec.tar.gz
chromium_src-70b3e7d594425427ee8daac76db9eb48edc688ec.tar.bz2
Chrome version of monitor configuration code
New MonitorConfigurator class interprets events (ctrl-F4, display add/remove, and brightness-related CRTC state changes). Ctrl-F4 events come in through the AcceleratorController. Display add/remove events are interpretted by MonitorChangeObserverX11. CRTC enable/disable events related to power management come over DBus and are interpretted by the new OutputObserver class. Note the the chromeos/monitor/DEPS change can be removed once the now-unused code is retired from powerd (since that is the only reason for a DBus call-out). Valgrind suppressions.txt was updated because this change re-orders our initial XRR calls such that a different top-level call is blamed for its leak. BUG=chromium:126493 TEST=Manually tested with an additional display and mode switching on Lumpy. Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=140031 Review URL: https://chromiumcodereview.appspot.com/10384103 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@140203 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--ash/accelerators/accelerator_controller.cc7
-rw-r--r--ash/accelerators/accelerator_table.cc5
-rw-r--r--ash/accelerators/accelerator_table.h1
-rw-r--r--ash/shell.cc21
-rw-r--r--ash/shell.h14
-rw-r--r--chrome/browser/chromeos/chrome_browser_main_chromeos.cc3
-rw-r--r--chrome/browser/chromeos/chrome_browser_main_chromeos.h2
-rw-r--r--chrome/browser/chromeos/power/output_observer.cc26
-rw-r--r--chrome/browser/chromeos/power/output_observer.h32
-rw-r--r--chrome/chrome_browser.gypi2
-rw-r--r--chromeos/chromeos.gyp7
-rw-r--r--chromeos/monitor/DEPS3
-rw-r--r--chromeos/monitor/output_configurator.cc607
-rw-r--r--chromeos/monitor/output_configurator.h145
-rw-r--r--tools/valgrind/memcheck/suppressions.txt5
-rw-r--r--ui/aura/monitor_change_observer_x11.cc1
-rw-r--r--ui/aura/root_window_host_linux.cc2
17 files changed, 879 insertions, 4 deletions
diff --git a/ash/accelerators/accelerator_controller.cc b/ash/accelerators/accelerator_controller.cc
index 2de0ac5..72a43ec 100644
--- a/ash/accelerators/accelerator_controller.cc
+++ b/ash/accelerators/accelerator_controller.cc
@@ -37,6 +37,10 @@
#include "ui/compositor/screen_rotation.h"
#include "ui/oak/oak.h"
+#if defined(OS_CHROMEOS)
+#include "chromeos/monitor/output_configurator.h"
+#endif // defined(OS_CHROMEOS)
+
namespace {
bool HandleCycleWindowMRU(ash::WindowCycleController::Direction direction,
@@ -329,6 +333,9 @@ bool AcceleratorController::AcceleratorPressed(
return HandleCrosh();
case TOGGLE_SPOKEN_FEEDBACK:
return HandleToggleSpokenFeedback();
+ case CYCLE_DISPLAY_MODE:
+ ash::Shell::GetInstance()->output_configurator()->CycleDisplayMode();
+ return true;
#endif
case EXIT:
return HandleExit();
diff --git a/ash/accelerators/accelerator_table.cc b/ash/accelerators/accelerator_table.cc
index c225674..7548145 100644
--- a/ash/accelerators/accelerator_table.cc
+++ b/ash/accelerators/accelerator_table.cc
@@ -30,6 +30,7 @@ const AcceleratorData kAcceleratorData[] = {
#if defined(OS_CHROMEOS)
{ true, ui::VKEY_BRIGHTNESS_DOWN, ui::EF_NONE, BRIGHTNESS_DOWN },
{ true, ui::VKEY_BRIGHTNESS_UP, ui::EF_NONE, BRIGHTNESS_UP },
+ { true, ui::VKEY_F4, ui::EF_CONTROL_DOWN, CYCLE_DISPLAY_MODE },
{ true, ui::VKEY_L, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, LOCK_SCREEN },
{ true, ui::VKEY_M, ui::EF_CONTROL_DOWN, OPEN_FILE_MANAGER },
{ true, ui::VKEY_T, ui::EF_CONTROL_DOWN | ui::EF_ALT_DOWN, OPEN_CROSH },
@@ -108,7 +109,6 @@ const AcceleratorData kAcceleratorData[] = {
// For testing on systems where Alt-Tab is already mapped.
{ true, ui::VKEY_W, ui::EF_ALT_DOWN, CYCLE_FORWARD_MRU },
{ true, ui::VKEY_W, ui::EF_SHIFT_DOWN | ui::EF_ALT_DOWN, CYCLE_BACKWARD_MRU },
- { true, ui::VKEY_F4, ui::EF_CONTROL_DOWN, MONITOR_ADD_REMOVE },
{ true, ui::VKEY_F4, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN, MONITOR_CYCLE },
{ true, ui::VKEY_HOME, ui::EF_SHIFT_DOWN | ui::EF_CONTROL_DOWN,
MONITOR_TOGGLE_SCALE },
@@ -123,6 +123,9 @@ const size_t kAcceleratorDataLength = arraysize(kAcceleratorData);
const AcceleratorAction kActionsAllowedAtLoginOrLockScreen[] = {
BRIGHTNESS_DOWN,
BRIGHTNESS_UP,
+#if defined(OS_CHROMEOS)
+ CYCLE_DISPLAY_MODE,
+#endif // defined(OS_CHROMEOS)
NEXT_IME,
PREVIOUS_IME,
SWITCH_IME, // Switch to another IME depending on the accelerator.
diff --git a/ash/accelerators/accelerator_table.h b/ash/accelerators/accelerator_table.h
index 10d6ef7..53461db 100644
--- a/ash/accelerators/accelerator_table.h
+++ b/ash/accelerators/accelerator_table.h
@@ -58,6 +58,7 @@ enum AcceleratorAction {
WINDOW_SNAP_LEFT,
WINDOW_SNAP_RIGHT,
#if defined(OS_CHROMEOS)
+ CYCLE_DISPLAY_MODE,
LOCK_SCREEN,
OPEN_CROSH,
OPEN_FILE_MANAGER,
diff --git a/ash/shell.cc b/ash/shell.cc
index aff6467..cd92511 100644
--- a/ash/shell.cc
+++ b/ash/shell.cc
@@ -95,6 +95,11 @@
#include "ash/accelerators/nested_dispatcher_controller.h"
#endif
+#if defined(OS_CHROMEOS)
+#include "chromeos/monitor/output_configurator.h"
+#include "ui/aura/dispatcher_linux.h"
+#endif // defined(OS_CHROMEOS)
+
namespace ash {
namespace {
@@ -547,6 +552,9 @@ Shell::Shell(ShellDelegate* delegate)
screen_(new ScreenAsh(root_window_.get())),
root_filter_(NULL),
delegate_(delegate),
+#if defined(OS_CHROMEOS)
+ output_configurator_(new chromeos::OutputConfigurator()),
+#endif // defined(OS_CHROMEOS)
shelf_(NULL),
panel_layout_manager_(NULL),
root_window_layout_(NULL),
@@ -554,6 +562,12 @@ Shell::Shell(ShellDelegate* delegate)
browser_context_(NULL) {
gfx::Screen::SetInstance(screen_);
ui_controls::InstallUIControlsAura(CreateUIControlsAura(root_window_.get()));
+#if defined(OS_CHROMEOS)
+ // OutputConfigurator needs to get events regarding added/removed outputs.
+ static_cast<aura::DispatcherLinux*>(
+ aura::Env::GetInstance()->GetDispatcher())->AddDispatcherForRootWindow(
+ output_configurator());
+#endif // defined(OS_CHROMEOS)
}
Shell::~Shell() {
@@ -624,6 +638,13 @@ Shell::~Shell() {
DCHECK(instance_ == this);
instance_ = NULL;
+
+#if defined(OS_CHROMEOS)
+ // Remove OutputConfigurator from Dispatcher.
+ static_cast<aura::DispatcherLinux*>(
+ aura::Env::GetInstance()->GetDispatcher())->RemoveDispatcherForRootWindow(
+ output_configurator());
+#endif // defined(OS_CHROMEOS)
}
// static
diff --git a/ash/shell.h b/ash/shell.h
index 0e64b71..6e874c0 100644
--- a/ash/shell.h
+++ b/ash/shell.h
@@ -36,6 +36,9 @@ class InputMethodEventFilter;
class RootWindowEventFilter;
}
}
+namespace chromeos {
+class OutputConfigurator;
+}
namespace content {
class BrowserContext;
}
@@ -317,6 +320,12 @@ class ASH_EXPORT Shell {
// Initialize the root window to be used for a secondary monitor.
void InitRootWindowForSecondaryMonitor(aura::RootWindow* root);
+#if defined(OS_CHROMEOS)
+ chromeos::OutputConfigurator* output_configurator() {
+ return output_configurator_.get();
+ }
+#endif // defined(OS_CHROMEOS)
+
private:
FRIEND_TEST_ALL_PREFIXES(RootWindowEventFilterTest, MouseEventCursors);
FRIEND_TEST_ALL_PREFIXES(RootWindowEventFilterTest, TransformActivate);
@@ -413,6 +422,11 @@ class ASH_EXPORT Shell {
// of layer animations for visual debugging.
scoped_ptr<internal::SlowAnimationEventFilter> slow_animation_filter_;
+#if defined(OS_CHROMEOS)
+ // Controls video output device state.
+ scoped_ptr<chromeos::OutputConfigurator> output_configurator_;
+#endif // defined(OS_CHROMEOS)
+
// The shelf for managing the launcher and the status widget in non-compact
// mode. Shell does not own the shelf. Instead, it is owned by container of
// the status area.
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
index d2e4f9e..2fd5d2d 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.cc
@@ -41,6 +41,7 @@
#include "chrome/browser/chromeos/net/network_change_notifier_chromeos.h"
#include "chrome/browser/chromeos/oom_priority_manager.h"
#include "chrome/browser/chromeos/power/brightness_observer.h"
+#include "chrome/browser/chromeos/power/output_observer.h"
#include "chrome/browser/chromeos/power/power_button_controller_delegate_chromeos.h"
#include "chrome/browser/chromeos/power/power_button_observer.h"
#include "chrome/browser/chromeos/power/power_state_override.h"
@@ -409,6 +410,7 @@ void ChromeBrowserMainPartsChromeos::PostProfileInit() {
// Initialize the brightness observer so that we'll display an onscreen
// indication of brightness changes during login.
brightness_observer_.reset(new chromeos::BrightnessObserver());
+ output_observer_.reset(new chromeos::OutputObserver());
resume_observer_.reset(new chromeos::ResumeObserver());
screen_lock_observer_.reset(new chromeos::ScreenLockObserver());
if (chromeos::KioskModeSettings::Get()->IsKioskModeEnabled())
@@ -484,6 +486,7 @@ void ChromeBrowserMainPartsChromeos::PostMainMessageLoopRun() {
screen_lock_observer_.reset();
resume_observer_.reset();
brightness_observer_.reset();
+ output_observer_.reset();
// The XInput2 event listener needs to be shut down earlier than when
// Singletons are finally destroyed in AtExitManager.
diff --git a/chrome/browser/chromeos/chrome_browser_main_chromeos.h b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
index a5b076b..8dc29df 100644
--- a/chrome/browser/chromeos/chrome_browser_main_chromeos.h
+++ b/chrome/browser/chromeos/chrome_browser_main_chromeos.h
@@ -10,6 +10,7 @@
namespace chromeos {
class BrightnessObserver;
+class OutputObserver;
class PowerButtonObserver;
class PowerStateOverride;
class ResumeObserver;
@@ -49,6 +50,7 @@ class ChromeBrowserMainPartsChromeos : public ChromeBrowserMainPartsLinux {
private:
scoped_ptr<chromeos::BrightnessObserver> brightness_observer_;
+ scoped_ptr<chromeos::OutputObserver> output_observer_;
scoped_ptr<chromeos::ResumeObserver> resume_observer_;
scoped_ptr<chromeos::ScreenLockObserver> screen_lock_observer_;
scoped_ptr<chromeos::SessionManagerObserver> session_manager_observer_;
diff --git a/chrome/browser/chromeos/power/output_observer.cc b/chrome/browser/chromeos/power/output_observer.cc
new file mode 100644
index 0000000..1896495
--- /dev/null
+++ b/chrome/browser/chromeos/power/output_observer.cc
@@ -0,0 +1,26 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/chromeos/power/output_observer.h"
+
+#include "ash/shell.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "chromeos/monitor/output_configurator.h"
+
+namespace chromeos {
+
+OutputObserver::OutputObserver() {
+ DBusThreadManager::Get()->GetPowerManagerClient()->AddObserver(this);
+}
+
+OutputObserver::~OutputObserver() {
+ DBusThreadManager::Get()->GetPowerManagerClient()->RemoveObserver(this);
+}
+
+void OutputObserver::ScreenPowerSet(bool power_on, bool all_displays) {
+ ash::Shell::GetInstance()->output_configurator()->
+ ScreenPowerSet(power_on, all_displays);
+}
+
+} // namespace chromeos
diff --git a/chrome/browser/chromeos/power/output_observer.h b/chrome/browser/chromeos/power/output_observer.h
new file mode 100644
index 0000000..9c2b1d4
--- /dev/null
+++ b/chrome/browser/chromeos/power/output_observer.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_BROWSER_CHROMEOS_POWER_OUTPUT_OBSERVER_H_
+#define CHROME_BROWSER_CHROMEOS_POWER_OUTPUT_OBSERVER_H_
+#pragma once
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chromeos/dbus/power_manager_client.h"
+
+namespace chromeos {
+
+// This observer listens for when video outputs have been turned off so that the
+// corresponding CRTCs can be disabled to force the connected output off.
+class OutputObserver : public PowerManagerClient::Observer {
+ public:
+ // This class registers/unregisters itself as an observer in ctor/dtor.
+ OutputObserver();
+ virtual ~OutputObserver();
+
+ private:
+ // PowerManagerClient::Observer implementation.
+ virtual void ScreenPowerSet(bool power_on, bool all_displays) OVERRIDE;
+
+ DISALLOW_COPY_AND_ASSIGN(OutputObserver);
+};
+
+} // namespace chromeos
+
+#endif // CHROME_BROWSER_CHROMEOS_POWER_OUTPUT_OBSERVER_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 4502a12..e7718e9 100644
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -786,6 +786,8 @@
'browser/chromeos/power/brightness_observer.h',
'browser/chromeos/power/low_battery_observer.cc',
'browser/chromeos/power/low_battery_observer.h',
+ 'browser/chromeos/power/output_observer.cc',
+ 'browser/chromeos/power/output_observer.h',
'browser/chromeos/power/power_button_controller_delegate_chromeos.cc',
'browser/chromeos/power/power_button_controller_delegate_chromeos.h',
'browser/chromeos/power/power_button_observer.cc',
diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp
index 97c6604..68ea8d7 100644
--- a/chromeos/chromeos.gyp
+++ b/chromeos/chromeos.gyp
@@ -95,9 +95,16 @@
'dbus/speech_synthesizer_client.h',
'dbus/update_engine_client.cc',
'dbus/update_engine_client.h',
+ 'monitor/output_configurator.cc',
+ 'monitor/output_configurator.h',
'network/network_sms_handler.cc',
'network/network_sms_handler.h',
],
+ 'link_settings': {
+ 'libraries': [
+ '-lXext',
+ ],
+ },
},
{
# This target contains mocks that can be used to write unit tests.
diff --git a/chromeos/monitor/DEPS b/chromeos/monitor/DEPS
new file mode 100644
index 0000000..d6abdda
--- /dev/null
+++ b/chromeos/monitor/DEPS
@@ -0,0 +1,3 @@
+include_rules = [
+ "+dbus",
+]
diff --git a/chromeos/monitor/output_configurator.cc b/chromeos/monitor/output_configurator.cc
new file mode 100644
index 0000000..d4fdbc0
--- /dev/null
+++ b/chromeos/monitor/output_configurator.cc
@@ -0,0 +1,607 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chromeos/monitor/output_configurator.h"
+
+#include "base/chromeos/chromeos_version.h"
+#include "base/logging.h"
+#include "base/message_pump_aurax11.h"
+#include "chromeos/dbus/dbus_thread_manager.h"
+#include "dbus/bus.h"
+#include "dbus/exported_object.h"
+#include "dbus/message.h"
+#include "dbus/object_path.h"
+#include "third_party/cros_system_api/dbus/service_constants.h"
+
+namespace chromeos {
+
+namespace {
+// DPI measurements.
+const float kMmInInch = 25.4;
+const float kDpi96 = 96.0;
+const float kPixelsToMmScale = kMmInInch / kDpi96;
+
+// Prefixes for the built-in displays.
+const char kInternal_LVDS[] = "LVDS";
+const char kInternal_eDP[] = "eDP";
+
+// TODO: Determine if we need to organize modes in a way which provides better
+// than O(n) lookup time. In many call sites, for example, the "next" mode is
+// typically what we are looking for so using this helper might be too
+// expensive.
+static XRRModeInfo* ModeInfoForID(XRRScreenResources* screen, RRMode modeID) {
+ XRRModeInfo* result = NULL;
+ for (int i = 0; (i < screen->nmode) && (result == NULL); i++)
+ if (modeID == screen->modes[i].id)
+ result = &screen->modes[i];
+
+ // We can't fail to find a mode referenced from the same screen.
+ CHECK(result != NULL);
+ return result;
+}
+
+// Identifies the modes which will be used by the respective outputs when in a
+// mirror mode. This means that the two modes will have the same resolution.
+// The RROutput IDs |one| and |two| are used to look up the modes and
+// |out_one_mode| and |out_two_mode| are the out-parameters for the respective
+// modes.
+// Returns false if it fails to find a compatible set of modes.
+static bool FindMirrorModeForOutputs(Display* display,
+ XRRScreenResources* screen,
+ RROutput one,
+ RROutput two,
+ RRMode* out_one_mode,
+ RRMode* out_two_mode) {
+ XRROutputInfo* primary = XRRGetOutputInfo(display, screen, one);
+ XRROutputInfo* secondary = XRRGetOutputInfo(display, screen, two);
+
+ int one_index = 0;
+ int two_index = 0;
+ bool found = false;
+ while (!found &&
+ (one_index < primary->nmode) &&
+ (two_index < secondary->nmode)) {
+ RRMode one_id = primary->modes[one_index];
+ RRMode two_id = secondary->modes[two_index];
+ XRRModeInfo* one_mode = ModeInfoForID(screen, one_id);
+ XRRModeInfo* two_mode = ModeInfoForID(screen, two_id);
+ int one_width = one_mode->width;
+ int one_height = one_mode->height;
+ int two_width = two_mode->width;
+ int two_height = two_mode->height;
+ if ((one_width == two_width) && (one_height == two_height)) {
+ *out_one_mode = one_id;
+ *out_two_mode = two_id;
+ found = true;
+ } else {
+ // The sort order of the modes is NOT by mode area but is sorted by width,
+ // then by height within each like width.
+ if (one_width > two_width) {
+ one_index += 1;
+ } else if (one_width < two_width) {
+ two_index += 1;
+ } else {
+ if (one_height > two_height) {
+ one_index += 1;
+ } else {
+ two_index += 1;
+ }
+ }
+ }
+ }
+ XRRFreeOutputInfo(primary);
+ XRRFreeOutputInfo(secondary);
+ return found;
+}
+
+// A helper to call XRRSetCrtcConfig with the given options but some of our
+// default output count and rotation arguments.
+static void ConfigureCrtc(Display *display,
+ XRRScreenResources* screen,
+ RRCrtc crtc,
+ int x,
+ int y,
+ RRMode mode,
+ RROutput output) {
+ const Rotation kRotate = RR_Rotate_0;
+ RROutput* outputs = NULL;
+ int num_outputs = 0;
+
+ // Check the output and mode argument - if either are None, we should disable.
+ if ((output != None) && (mode != None)) {
+ outputs = &output;
+ num_outputs = 1;
+ }
+
+ XRRSetCrtcConfig(display,
+ screen,
+ crtc,
+ CurrentTime,
+ x,
+ y,
+ mode,
+ kRotate,
+ outputs,
+ num_outputs);
+ if (num_outputs == 1) {
+ // We are enabling a display so make sure it is turned on.
+ CHECK(DPMSEnable(display));
+ CHECK(DPMSForceLevel(display, DPMSModeOn));
+ }
+}
+
+// Called to set the frame buffer (underling XRR "screen") size. Has a
+// side-effect of disabling all CRTCs.
+static void CreateFrameBuffer(Display* display,
+ XRRScreenResources* screen,
+ Window window,
+ int width,
+ int height) {
+ // Note that setting the screen size fails if any CRTCs are currently
+ // pointing into it so disable them all.
+ for (int i = 0; i < screen->ncrtc; ++i) {
+ const int x = 0;
+ const int y = 0;
+ const RRMode kMode = None;
+ const RROutput kOutput = None;
+
+ ConfigureCrtc(display,
+ screen,
+ screen->crtcs[i],
+ x,
+ y,
+ kMode,
+ kOutput);
+ }
+ int mm_width = width * kPixelsToMmScale;
+ int mm_height = height * kPixelsToMmScale;
+ XRRSetScreenSize(display, window, width, height, mm_width, mm_height);
+}
+} // namespace
+
+bool OutputConfigurator::TryRecacheOutputs(Display* display,
+ XRRScreenResources* screen) {
+ bool outputs_did_change = false;
+ int previous_connected_count = 0;
+ int new_connected_count = 0;
+
+ if (output_count_ != screen->noutput) {
+ outputs_did_change = true;
+ } else {
+ // The outputs might have changed so compare the connected states in the
+ // screen to our existing cache.
+ for (int i = 0; (i < output_count_) && !outputs_did_change; ++i) {
+ RROutput thisID = screen->outputs[i];
+ XRROutputInfo* output = XRRGetOutputInfo(display, screen, thisID);
+ bool now_connected = (RR_Connected == output->connection);
+ outputs_did_change = (now_connected != output_cache_[i].is_connected);
+ XRRFreeOutputInfo(output);
+
+ if (output_cache_[i].is_connected)
+ previous_connected_count += 1;
+ if (now_connected)
+ new_connected_count += 1;
+ }
+ }
+
+ if (outputs_did_change) {
+ // We now know that we need to recache so free and re-alloc the buffer.
+ output_count_ = screen->noutput;
+ if (output_count_ == 0) {
+ output_cache_.reset(NULL);
+ } else {
+ // Ideally, this would be allocated inline in the OutputConfigurator
+ // instance since we support at most 2 connected outputs but this dynamic
+ // allocation was specifically requested.
+ output_cache_.reset(new CachedOutputDescription[output_count_]);
+ }
+
+ // TODO: This approach to finding CRTCs only supports two. Expand on this.
+ RRCrtc used_crtc = None;
+ primary_output_index_ = -1;
+ secondary_output_index_ = -1;
+
+ for (int i = 0; i < output_count_; ++i) {
+ RROutput this_id = screen->outputs[i];
+ XRROutputInfo* output = XRRGetOutputInfo(display, screen, this_id);
+ bool is_connected = (RR_Connected == output->connection);
+ RRCrtc crtc = None;
+ RRMode ideal_mode = None;
+ int x = 0;
+ int y = 0;
+ bool is_internal = false;
+
+ if (is_connected) {
+ for (int j = 0; (j < output->ncrtc) && (None == crtc); ++j) {
+ RRCrtc possible = output->crtcs[j];
+ if (possible != used_crtc) {
+ crtc = possible;
+ used_crtc = possible;
+ }
+ }
+
+ const char* name = output->name;
+ is_internal =
+ (strncmp(kInternal_LVDS,
+ name,
+ arraysize(kInternal_LVDS) - 1) == 0) ||
+ (strncmp(kInternal_eDP,
+ name,
+ arraysize(kInternal_eDP) - 1) == 0);
+ if (output->nmode > 0)
+ ideal_mode = output->modes[0];
+
+ if (crtc != None) {
+ XRRCrtcInfo* crtcInfo = XRRGetCrtcInfo(display, screen, crtc);
+ x = crtcInfo->x;
+ y = crtcInfo->y;
+ XRRFreeCrtcInfo(crtcInfo);
+ }
+
+ // Save this for later mirror mode detection.
+ if (primary_output_index_ == -1)
+ primary_output_index_ = i;
+ else if (secondary_output_index_ == -1)
+ secondary_output_index_ = i;
+ }
+ XRRFreeOutputInfo(output);
+
+ // Now save the cached state for this output (we will default to mirror
+ // disabled and detect that after we have identified the first two
+ // connected outputs).
+ VLOG(1) << "Recache output index: " << i
+ << ", output id: " << this_id
+ << ", crtc id: " << crtc
+ << ", ideal mode id: " << ideal_mode
+ << ", x: " << x
+ << ", y: " << y
+ << ", is connected: " << is_connected
+ << ", is_internal: " << is_internal;
+ output_cache_[i].output = this_id;
+ output_cache_[i].crtc = crtc;
+ output_cache_[i].mirror_mode = None;
+ output_cache_[i].ideal_mode = ideal_mode;
+ output_cache_[i].x = x;
+ output_cache_[i].y = y;
+ output_cache_[i].is_connected = is_connected;
+ output_cache_[i].is_powered_on = true;
+ output_cache_[i].is_internal = is_internal;
+ }
+
+ // Now, detect the mirror modes if we have two connected outputs.
+ if ((primary_output_index_ != -1) && (secondary_output_index_ != -1)) {
+ mirror_supported_ = FindMirrorModeForOutputs(
+ display,
+ screen,
+ output_cache_[primary_output_index_].output,
+ output_cache_[secondary_output_index_].output,
+ &output_cache_[primary_output_index_].mirror_mode,
+ &output_cache_[secondary_output_index_].mirror_mode);
+
+ RRMode primary_mode = output_cache_[primary_output_index_].mirror_mode;
+ RRMode second_mode = output_cache_[secondary_output_index_].mirror_mode;
+ VLOG(1) << "Mirror mode supported " << mirror_supported_
+ << " primary " << primary_mode
+ << " secondary " << second_mode;
+ }
+ }
+ return outputs_did_change;
+}
+
+OutputConfigurator::OutputConfigurator()
+ : is_running_on_chrome_os_(false),
+ output_count_(0),
+ output_cache_(NULL),
+ mirror_supported_(false),
+ primary_output_index_(-1),
+ secondary_output_index_(-1),
+ xrandr_event_base_(0),
+ output_state_(STATE_INVALID) {
+ // We only want to apply our logic if we are running on ChromeOS.
+ is_running_on_chrome_os_ = base::chromeos::IsRunningOnChromeOS();
+ LOG_IF(ERROR, !is_running_on_chrome_os_)
+ << "OutputConfigurator detected not ChromeOS. All configuration calls "
+ "will fail";
+
+ if (is_running_on_chrome_os_) {
+ // Send the signal to powerd to tell it that we will take over output
+ // control.
+ // Note that this can be removed once the legacy powerd support is removed.
+ chromeos::DBusThreadManager* manager = chromeos::DBusThreadManager::Get();
+ dbus::Bus* bus = manager->GetSystemBus();
+ dbus::ExportedObject* remote_object = bus->GetExportedObject(
+ dbus::ObjectPath(power_manager::kPowerManagerServicePath));
+ dbus::Signal signal(power_manager::kPowerManagerInterface,
+ power_manager::kUseNewMonitorConfigSignal);
+ CHECK(signal.raw_message() != NULL);
+ remote_object->SendSignal(&signal);
+
+ // Cache the initial output state.
+ Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
+ CHECK(display != NULL);
+ XGrabServer(display);
+ Window window = DefaultRootWindow(display);
+ XRRScreenResources* screen = XRRGetScreenResources(display, window);
+ CHECK(screen != NULL);
+ bool did_detect_outputs = TryRecacheOutputs(display, screen);
+ CHECK(did_detect_outputs);
+ State state = GetDefaultState();
+ UpdateCacheAndXrandrToState(display, screen, window, state);
+ // Find xrandr_event_base_ since we need it to interpret events, later.
+ int error_base_ignored = 0;
+ XRRQueryExtension(display, &xrandr_event_base_, &error_base_ignored);
+ // Relinquish X resources.
+ XRRFreeScreenResources(screen);
+ XUngrabServer(display);
+ }
+}
+
+OutputConfigurator::~OutputConfigurator() {
+}
+
+void OutputConfigurator::UpdateCacheAndXrandrToState(
+ Display* display,
+ XRRScreenResources* screen,
+ Window window,
+ State new_state) {
+ // Default rules:
+ // - single display = rebuild framebuffer and set to ideal_mode.
+ // - multi display = rebuild framebuffer and set to mirror_mode.
+
+ // First, calculate the width and height of the framebuffer (we could retain
+ // the existing buffer, if it isn't resizing, but that causes an odd display
+ // state where the CRTCs are repositioned over the root windows before Chrome
+ // can move them). It is a feature worth considering, though, and wouldn't
+ // be difficult to implement (just check the current framebuffer size before
+ // changing it).
+ int width = 0;
+ int height = 0;
+ int primary_height = 0;
+ int secondary_height = 0;
+ if (new_state == STATE_SINGLE) {
+ CHECK_NE(-1, primary_output_index_);
+
+ XRRModeInfo* ideal_mode = ModeInfoForID(
+ screen,
+ output_cache_[primary_output_index_].ideal_mode);
+ width = ideal_mode->width;
+ height = ideal_mode->height;
+ } else if (new_state == STATE_DUAL_MIRROR) {
+ CHECK_NE(-1, primary_output_index_);
+ CHECK_NE(-1, secondary_output_index_);
+
+ XRRModeInfo* mirror_mode = ModeInfoForID(
+ screen,
+ output_cache_[primary_output_index_].mirror_mode);
+ width = mirror_mode->width;
+ height = mirror_mode->height;
+ } else if ((new_state == STATE_DUAL_PRIMARY_ONLY) ||
+ (new_state == STATE_DUAL_SECONDARY_ONLY)) {
+ CHECK_NE(-1, primary_output_index_);
+ CHECK_NE(-1, secondary_output_index_);
+
+ XRRModeInfo* one_ideal = ModeInfoForID(
+ screen,
+ output_cache_[primary_output_index_].ideal_mode);
+ XRRModeInfo* two_ideal = ModeInfoForID(
+ screen,
+ output_cache_[secondary_output_index_].ideal_mode);
+ width = std::max<int>(one_ideal->width, two_ideal->width);
+ height = one_ideal->height + two_ideal->height;
+ primary_height = one_ideal->height;
+ secondary_height = two_ideal->height;
+ }
+ CreateFrameBuffer(display, screen, window, width, height);
+
+ // Now, tile the outputs appropriately.
+ const int x = 0;
+ const int y = 0;
+ switch (new_state) {
+ case STATE_SINGLE:
+ ConfigureCrtc(display,
+ screen,
+ output_cache_[primary_output_index_].crtc,
+ x,
+ y,
+ output_cache_[primary_output_index_].ideal_mode,
+ output_cache_[primary_output_index_].output);
+ break;
+ case STATE_DUAL_MIRROR:
+ case STATE_DUAL_PRIMARY_ONLY:
+ case STATE_DUAL_SECONDARY_ONLY: {
+ RRMode primary_mode = output_cache_[primary_output_index_].mirror_mode;
+ RRMode secondary_mode =
+ output_cache_[secondary_output_index_].mirror_mode;
+ int primary_y = y;
+ int secondary_y = y;
+
+ if (new_state != STATE_DUAL_MIRROR) {
+ primary_mode = output_cache_[primary_output_index_].ideal_mode;
+ secondary_mode = output_cache_[secondary_output_index_].ideal_mode;
+ }
+ if (new_state == STATE_DUAL_PRIMARY_ONLY)
+ secondary_y = y + primary_height;
+ if (new_state == STATE_DUAL_SECONDARY_ONLY)
+ primary_y = y + secondary_height;
+
+ ConfigureCrtc(display,
+ screen,
+ output_cache_[primary_output_index_].crtc,
+ x,
+ primary_y,
+ primary_mode,
+ output_cache_[primary_output_index_].output);
+ ConfigureCrtc(display,
+ screen,
+ output_cache_[secondary_output_index_].crtc,
+ x,
+ secondary_y,
+ secondary_mode,
+ output_cache_[secondary_output_index_].output);
+ }
+ break;
+ case STATE_HEADLESS:
+ // Do nothing.
+ break;
+ default:
+ NOTREACHED() << "Unhandled state " << new_state;
+ }
+ output_state_ = new_state;
+}
+
+bool OutputConfigurator::RecacheAndUseDefaultState() {
+ Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
+ CHECK(display != NULL);
+ XGrabServer(display);
+ Window window = DefaultRootWindow(display);
+ XRRScreenResources* screen = XRRGetScreenResources(display, window);
+ CHECK(screen != NULL);
+
+ bool did_detect_change = TryRecacheOutputs(display, screen);
+ if (did_detect_change) {
+ State state = GetDefaultState();
+ UpdateCacheAndXrandrToState(display, screen, window, state);
+ }
+ XRRFreeScreenResources(screen);
+ XUngrabServer(display);
+ return did_detect_change;
+}
+
+State OutputConfigurator::GetDefaultState() const {
+ State state = STATE_HEADLESS;
+ if (-1 != primary_output_index_) {
+ if (-1 != secondary_output_index_)
+ state = mirror_supported_ ? STATE_DUAL_MIRROR : STATE_DUAL_PRIMARY_ONLY;
+ else
+ state = STATE_SINGLE;
+ }
+ return state;
+}
+
+bool OutputConfigurator::CycleDisplayMode() {
+ VLOG(1) << "CycleDisplayMode";
+ bool did_change = false;
+ if (is_running_on_chrome_os_) {
+ Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
+ CHECK(display != NULL);
+ XGrabServer(display);
+ Window window = DefaultRootWindow(display);
+ XRRScreenResources* screen = XRRGetScreenResources(display, window);
+ CHECK(screen != NULL);
+
+ // Rules:
+ // - if there are 0 or 1 displays, do nothing and return false.
+ // - use y-coord of CRTCs to determine if we are mirror, primary-first, or
+ // secondary-first. The cycle order is:
+ // mirror->primary->secondary->mirror.
+ State new_state = STATE_INVALID;
+ switch (output_state_) {
+ case STATE_DUAL_MIRROR:
+ new_state = STATE_DUAL_PRIMARY_ONLY;
+ break;
+ case STATE_DUAL_PRIMARY_ONLY:
+ new_state = STATE_DUAL_SECONDARY_ONLY;
+ break;
+ case STATE_DUAL_SECONDARY_ONLY:
+ new_state = mirror_supported_ ?
+ STATE_DUAL_MIRROR :
+ STATE_DUAL_PRIMARY_ONLY;
+ break;
+ default:
+ // Do nothing - we aren't in a mode which we can rotate.
+ break;
+ }
+ if (STATE_INVALID != new_state) {
+ UpdateCacheAndXrandrToState(display,
+ screen,
+ window,
+ new_state);
+ did_change = true;
+ }
+
+ XRRFreeScreenResources(screen);
+ XUngrabServer(display);
+ }
+ return did_change;
+}
+
+bool OutputConfigurator::ScreenPowerSet(bool power_on, bool all_displays) {
+ VLOG(1) << "OutputConfigurator::SetScreensOn " << power_on
+ << " all displays " << all_displays;
+ bool success = false;
+ if (is_running_on_chrome_os_) {
+ Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
+ CHECK(display != NULL);
+ XGrabServer(display);
+ Window window = DefaultRootWindow(display);
+ XRRScreenResources* screen = XRRGetScreenResources(display, window);
+ CHECK(screen != NULL);
+
+ // Set the CRTCs based on whether we want to turn the power on or off and
+ // select the outputs to operate on by name or all_displays.
+ for (int i = 0; i < output_count_; ++i) {
+ if (all_displays || output_cache_[i].is_internal) {
+ const int x = output_cache_[i].x;
+ const int y = output_cache_[i].y;
+ RROutput output = output_cache_[i].output;
+ RRCrtc crtc = output_cache_[i].crtc;
+ RRMode mode = None;
+ if (power_on) {
+ mode = (STATE_DUAL_MIRROR == output_state_) ?
+ output_cache_[i].mirror_mode :
+ output_cache_[i].ideal_mode;
+ }
+
+ VLOG(1) << "SET POWER crtc: " << crtc
+ << ", mode " << mode
+ << ", output " << output
+ << ", x " << x
+ << ", y " << y;
+ ConfigureCrtc(display,
+ screen,
+ crtc,
+ x,
+ y,
+ mode,
+ output);
+ output_cache_[i].is_powered_on = power_on;
+ success = true;
+ }
+ }
+
+ // Force the DPMS on since the driver doesn't always detect that it should
+ // turn on.
+ if (power_on) {
+ CHECK(DPMSEnable(display));
+ CHECK(DPMSForceLevel(display, DPMSModeOn));
+ }
+
+ XRRFreeScreenResources(screen);
+ XUngrabServer(display);
+ }
+ return success;
+}
+
+bool OutputConfigurator::Dispatch(const base::NativeEvent& event) {
+ // Ignore this event if the Xrandr extension isn't supported.
+ if (is_running_on_chrome_os_ &&
+ (event->type - xrandr_event_base_ == RRNotify)) {
+ XEvent* xevent = static_cast<XEvent*>(event);
+ XRRNotifyEvent* notify_event =
+ reinterpret_cast<XRRNotifyEvent*>(xevent);
+ if (notify_event->subtype == RRNotify_OutputChange) {
+ XRROutputChangeNotifyEvent* output_change_event =
+ reinterpret_cast<XRROutputChangeNotifyEvent*>(xevent);
+ if ((output_change_event->connection == RR_Connected) ||
+ (output_change_event->connection == RR_Disconnected)) {
+ RecacheAndUseDefaultState();
+ }
+ // Ignore the case of RR_UnkownConnection.
+ }
+ }
+ return true;
+}
+
+} // namespace chromeos
+
diff --git a/chromeos/monitor/output_configurator.h b/chromeos/monitor/output_configurator.h
new file mode 100644
index 0000000..b59474f2
--- /dev/null
+++ b/chromeos/monitor/output_configurator.h
@@ -0,0 +1,145 @@
+// Copyright (c) 2012 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROMEOS_MONITOR_OUTPUT_CONFIGURATOR_H_
+#define CHROMEOS_MONITOR_OUTPUT_CONFIGURATOR_H_
+#pragma once
+
+#include <X11/Xlib.h>
+#include <X11/extensions/dpms.h>
+#include <X11/extensions/Xrandr.h>
+
+// Xlib defines Status as int which causes our include of dbus/bus.h to fail
+// when it tries to name an enum Status. Thus, we need to undefine it (note
+// that this will cause a problem if code needs to use the Status type).
+// RootWindow causes similar problems in that there is a Chromium type with that
+// name.
+#undef Status
+#undef RootWindow
+
+#include "base/basictypes.h"
+#include "base/event_types.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.h"
+#include "chromeos/chromeos_export.h"
+
+namespace chromeos {
+
+// The information we need to cache from an output to implement operations such
+// as power state but also to eliminate duplicate operations within a given
+// action (determining which CRTC to use for a given output, for example).
+struct CachedOutputDescription {
+ RROutput output;
+ RRCrtc crtc;
+ RRMode mirror_mode;
+ RRMode ideal_mode;
+ int x;
+ int y;
+ bool is_connected;
+ bool is_powered_on;
+ bool is_internal;
+};
+
+// Used to describe the state of a multi-monitor configuration.
+enum State {
+ STATE_INVALID,
+ STATE_HEADLESS,
+ STATE_SINGLE,
+ STATE_DUAL_MIRROR,
+ STATE_DUAL_PRIMARY_ONLY,
+ STATE_DUAL_SECONDARY_ONLY,
+};
+
+// This class interacts directly with the underlying Xrandr API to manipulate
+// CTRCs and Outputs. It will likely grow more state, over time, or expose
+// Output info in other ways as more of the Chrome display code grows up around
+// it.
+class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
+ public:
+ OutputConfigurator();
+ virtual ~OutputConfigurator();
+
+ // Called when the user hits ctrl-F4 to request a display mode change.
+ // This method should only return false if it was called in a single-head or
+ // headless mode.
+ bool CycleDisplayMode();
+
+ // Called when powerd notifies us that some set of displays should be turned
+ // on or off. This requires enabling or disabling the CRTC associated with
+ // the display(s) in question so that the low power state is engaged.
+ bool ScreenPowerSet(bool power_on, bool all_displays);
+
+ // Called when an RRNotify event is received. The implementation is
+ // interested in the cases of RRNotify events which correspond to output
+ // add/remove events. Note that Output add/remove events are sent in response
+ // to our own reconfiguration operations so spurious events are common.
+ // Spurious events will have no effect.
+ virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
+
+ private:
+ // Updates |output_count_|, |output_cache_|, |mirror_supported_|,
+ // |primary_output_index_|, and |secondary_output_index_| with new data.
+ // Returns true if the update succeeded or false if it was skipped since no
+ // actual change was observed.
+ // Note that |output_state_| is not updated by this call.
+ bool TryRecacheOutputs(Display* display, XRRScreenResources* screen);
+
+ // Uses the data stored in |output_cache_| and the given |new_state| to
+ // configure the Xrandr interface and then updates |output_state_| to reflect
+ // the new state.
+ void UpdateCacheAndXrandrToState(Display* display,
+ XRRScreenResources* screen,
+ Window window,
+ State new_state);
+
+ // A helper to re-cache instance variable state and transition into the
+ // appropriate default state for the observed displays.
+ bool RecacheAndUseDefaultState();
+
+ // Checks the |primary_output_index_|, |secondary_output_index_|, and
+ // |mirror_supported_| to see how many displays are currently connected and
+ // returns the state which is most appropriate as a default state for those
+ // displays.
+ State GetDefaultState() const;
+
+ // This is detected by the constructor to determine whether or not we should
+ // be enabled. If we aren't running on ChromeOS, we can't assume that the
+ // Xrandr X11 extension is supported.
+ // If this flag is set to false, any attempts to change the output
+ // configuration to immediately fail without changing the state.
+ bool is_running_on_chrome_os_;
+
+ // The number of outputs in the output_cache_ array.
+ int output_count_;
+
+ // The list of cached output descriptions (|output_count_| elements long).
+ scoped_array<CachedOutputDescription> output_cache_;
+
+ // True if |output_cache_| describes a permutation of outputs which support a
+ // mirrored device mode.
+ bool mirror_supported_;
+
+ // The index of the primary connected output in |output_cache_|. -1 if there
+ // is no primary output. This implies the machine currently has no outputs.
+ int primary_output_index_;
+
+ // The index of the secondary connected output in |output_cache_|. -1 if
+ // there is no secondary output. This implies the machine currently has one
+ // output.
+ int secondary_output_index_;
+
+ // The base of the event numbers used to represent XRandr events used in
+ // decoding events regarding output add/remove.
+ int xrandr_event_base_;
+
+ // The display state as derived from the outputs observed in |output_cache_|.
+ // This is used for rotating display modes.
+ State output_state_;
+
+ DISALLOW_COPY_AND_ASSIGN(OutputConfigurator);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_MONITOR_OUTPUT_CONFIGURATOR_H_
diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt
index acaf3e0b..6e00830 100644
--- a/tools/valgrind/memcheck/suppressions.txt
+++ b/tools/valgrind/memcheck/suppressions.txt
@@ -630,13 +630,14 @@
fun:_vgnU_freeres
}
-# XRandRInfo object seems to be leaking inside XRRFindDisplay.
+# XRandRInfo object seems to be leaking inside XRRFindDisplay. This happens the
+# first time it is called, no matter who the caller is. We have observed this
+# problem with both XRRSelectInput and XRRQueryExtension.
{
bug_119677
Memcheck:Leak
fun:malloc
fun:XRRFindDisplay
- fun:XRRSelectInput
}
{
Flash Player Leak
diff --git a/ui/aura/monitor_change_observer_x11.cc b/ui/aura/monitor_change_observer_x11.cc
index 09ce293..b6409f3 100644
--- a/ui/aura/monitor_change_observer_x11.cc
+++ b/ui/aura/monitor_change_observer_x11.cc
@@ -49,7 +49,6 @@ MonitorChangeObserverX11::MonitorChangeObserverX11()
: xdisplay_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
x_root_window_(DefaultRootWindow(xdisplay_)),
xrandr_event_base_(0) {
- XRRSelectInput(xdisplay_, x_root_window_, RRScreenChangeNotifyMask);
int error_base_ignored;
XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
static_cast<DispatcherLinux*>(Env::GetInstance()->GetDispatcher())->
diff --git a/ui/aura/root_window_host_linux.cc b/ui/aura/root_window_host_linux.cc
index b0b73a9..c9fe9c2 100644
--- a/ui/aura/root_window_host_linux.cc
+++ b/ui/aura/root_window_host_linux.cc
@@ -553,6 +553,8 @@ RootWindowHostLinux::RootWindowHostLinux(const gfx::Rect& bounds)
static int root_window_number = 0;
std::string name = StringPrintf("aura_root_%d", root_window_number++);
XStoreName(xdisplay_, xwindow_, name.c_str());
+ XRRSelectInput(xdisplay_, x_root_window_,
+ RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
}
RootWindowHostLinux::~RootWindowHostLinux() {