summaryrefslogtreecommitdiffstats
path: root/chromeos/display/output_configurator.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chromeos/display/output_configurator.cc')
-rw-r--r--chromeos/display/output_configurator.cc804
1 files changed, 804 insertions, 0 deletions
diff --git a/chromeos/display/output_configurator.cc b/chromeos/display/output_configurator.cc
new file mode 100644
index 0000000..4543a52
--- /dev/null
+++ b/chromeos/display/output_configurator.cc
@@ -0,0 +1,804 @@
+// 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/display/output_configurator.h"
+
+#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/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 "dbus/object_proxy.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;
+
+// The DPI threshold to detech high density screen.
+// Higher DPI than this will use device_scale_factor=2
+// Should be kept in sync with display_change_observer_x11.cc
+const unsigned int kHighDensityDIPThreshold = 160;
+
+// Prefixes for the built-in displays.
+const char kInternal_LVDS[] = "LVDS";
+const char kInternal_eDP[] = "eDP";
+
+// Gap between screens so cursor at bottom of active display doesn't partially
+// appear on top of inactive display. Higher numbers guard against larger
+// cursors, but also waste more memory. We will double this gap for screens
+// with a device_scale_factor of 2. While this gap will not guard against all
+// possible cursors in X, it should handle the ones we actually use. See
+// crbug.com/130188
+const int kVerticalGap = 30;
+
+// 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);
+}
+
+// A helper to get the current CRTC, Mode, and height for a given output. This
+// is read from the XRandR configuration and not any of our caches.
+static void GetOutputConfiguration(Display* display,
+ XRRScreenResources* screen,
+ RROutput output,
+ RRCrtc* crtc,
+ RRMode* mode,
+ int* height) {
+ XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, output);
+ CHECK(output_info != NULL);
+ *crtc = output_info->crtc;
+ XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(display, screen, *crtc);
+ if (crtc_info != NULL) {
+ *mode = crtc_info->mode;
+ *height = crtc_info->height;
+ XRRFreeCrtcInfo(crtc_info);
+ }
+ XRRFreeOutputInfo(output_info);
+}
+
+// A helper to determine the device_scale_factor given pixel width and mm_width.
+// This currently only reports two scale factors (1.0 and 2.0)
+static float ComputeDeviceScaleFactor(unsigned int width,
+ unsigned long mm_width) {
+ float device_scale_factor = 1.0f;
+ if (mm_width > 0 && (kMmInInch * width / mm_width) > kHighDensityDIPThreshold)
+ device_scale_factor = 2.0f;
+ return device_scale_factor;
+}
+
+} // 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;
+ unsigned long mm_width = output->mm_width;
+ unsigned long mm_height = output->mm_height;
+ 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
+ << ", mm_width: " << mm_width
+ << ", mm_height: " << mm_height;
+ 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;
+ output_cache_[i].mm_width = mm_width;
+ output_cache_[i].mm_height = mm_height;
+ }
+
+ // 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_(base::chromeos::IsRunningOnChromeOS()),
+ 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) {
+ 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 current_state = InferCurrentState(display, screen);
+ if (current_state == STATE_INVALID) {
+ // Unknown state. Transition into the default state.
+ State state = GetDefaultState();
+ UpdateCacheAndXrandrToState(display, screen, window, state);
+ } else {
+ // This is a valid state so just save it to |output_state_|.
+ output_state_ = current_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);
+ CheckIsProjectingAndNotify();
+ }
+}
+
+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;
+ int vertical_gap = 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);
+
+ // Compute the device scale factor for the topmost display. We only need
+ // to take this device's scale factor into account as we are creating a gap
+ // to avoid the cursor drawing onto the second (unused) display when the
+ // cursor is near the bottom of the topmost display.
+ float top_scale_factor;
+ if (new_state == STATE_DUAL_PRIMARY_ONLY) {
+ top_scale_factor = ComputeDeviceScaleFactor(one_ideal->width,
+ output_cache_[primary_output_index_].mm_width);
+ } else {
+ top_scale_factor = ComputeDeviceScaleFactor(two_ideal->width,
+ output_cache_[secondary_output_index_].mm_width);
+ }
+ vertical_gap = kVerticalGap * top_scale_factor;
+
+ width = std::max<int>(one_ideal->width, two_ideal->width);
+ height = one_ideal->height + two_ideal->height + vertical_gap;
+ 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 + vertical_gap;
+ if (new_state == STATE_DUAL_SECONDARY_ONLY)
+ primary_y = y + secondary_height + vertical_gap;
+
+ 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;
+}
+
+State OutputConfigurator::InferCurrentState(Display* display,
+ XRRScreenResources* screen) const {
+ // STATE_INVALID will be our default or "unknown" state.
+ State state = STATE_INVALID;
+ // First step: count the number of connected outputs.
+ if (secondary_output_index_ == -1) {
+ // No secondary display.
+ if (primary_output_index_ == -1) {
+ // No primary display implies HEADLESS.
+ state = STATE_HEADLESS;
+ } else {
+ // The common case of primary-only.
+ // The only sanity check we require in this case is that the current mode
+ // of the output's CRTC is the ideal mode we determined for it.
+ RRCrtc primary_crtc = None;
+ RRMode primary_mode = None;
+ int primary_height = 0;
+ GetOutputConfiguration(display,
+ screen,
+ output_cache_[primary_output_index_].output,
+ &primary_crtc,
+ &primary_mode,
+ &primary_height);
+ if (primary_mode == output_cache_[primary_output_index_].ideal_mode)
+ state = STATE_SINGLE;
+ }
+ } else {
+ // We have two displays attached so we need to look at their configuration.
+ // Note that, for simplicity, we will only detect the states that we would
+ // have used and will assume anything unexpected is INVALID (which should
+ // not happen in any expected usage scenario).
+ RRCrtc primary_crtc = None;
+ RRMode primary_mode = None;
+ int primary_height = 0;
+ GetOutputConfiguration(display,
+ screen,
+ output_cache_[primary_output_index_].output,
+ &primary_crtc,
+ &primary_mode,
+ &primary_height);
+ RRCrtc secondary_crtc = None;
+ RRMode secondary_mode = None;
+ int secondary_height = 0;
+ GetOutputConfiguration(display,
+ screen,
+ output_cache_[secondary_output_index_].output,
+ &secondary_crtc,
+ &secondary_mode,
+ &secondary_height);
+ // Make sure the CRTCs are matched to the expected outputs.
+ if ((output_cache_[primary_output_index_].crtc == primary_crtc) &&
+ (output_cache_[secondary_output_index_].crtc == secondary_crtc)) {
+ // Check the mode matching: either both mirror or both ideal.
+ if ((output_cache_[primary_output_index_].mirror_mode == primary_mode) &&
+ (output_cache_[secondary_output_index_].mirror_mode ==
+ secondary_mode)) {
+ // We are already in mirror mode.
+ state = STATE_DUAL_MIRROR;
+ } else if ((output_cache_[primary_output_index_].ideal_mode ==
+ primary_mode) &&
+ (output_cache_[secondary_output_index_].ideal_mode ==
+ secondary_mode)) {
+ // Both outputs are in their "ideal" mode so check their Y-offsets to
+ // see which "ideal" configuration this is.
+ if (primary_height == output_cache_[secondary_output_index_].y) {
+ // Secondary is tiled first.
+ state = STATE_DUAL_SECONDARY_ONLY;
+ } else if (secondary_height == output_cache_[primary_output_index_].y) {
+ // Primary is tiled first.
+ state = STATE_DUAL_PRIMARY_ONLY;
+ }
+ }
+ }
+ }
+
+ return state;
+}
+
+bool OutputConfigurator::CycleDisplayMode() {
+ VLOG(1) << "CycleDisplayMode";
+ bool did_change = false;
+ if (is_running_on_chrome_os_) {
+ // 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)
+ did_change = SetDisplayMode(new_state);
+ }
+ 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::SetDisplayMode(State new_state) {
+ if (output_state_ == STATE_INVALID ||
+ output_state_ == STATE_HEADLESS ||
+ output_state_ == STATE_SINGLE)
+ return false;
+
+ Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
+ CHECK(display != NULL);
+ XGrabServer(display);
+ Window window = DefaultRootWindow(display);
+ XRRScreenResources* screen = XRRGetScreenResources(display, window);
+ CHECK(screen != NULL);
+
+ UpdateCacheAndXrandrToState(display,
+ screen,
+ window,
+ new_state);
+ XRRFreeScreenResources(screen);
+ XUngrabServer(display);
+ return true;
+}
+
+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();
+ CheckIsProjectingAndNotify();
+ }
+ // Ignore the case of RR_UnkownConnection.
+ }
+ }
+ return true;
+}
+
+void OutputConfigurator::CheckIsProjectingAndNotify() {
+ // Determine if there is an "internal" output and how many outputs are
+ // connected.
+ bool has_internal_output = false;
+ int connected_output_count = 0;
+ for (int i = 0; i < output_count_; ++i) {
+ if (output_cache_[i].is_connected) {
+ connected_output_count += 1;
+ has_internal_output |= output_cache_[i].is_internal;
+ }
+ }
+
+ // "Projecting" is defined as having more than 1 output connected while at
+ // least one of them is an internal output.
+ bool is_projecting = has_internal_output && (connected_output_count > 1);
+ chromeos::DBusThreadManager* manager = chromeos::DBusThreadManager::Get();
+ dbus::Bus* bus = manager->GetSystemBus();
+ dbus::ObjectProxy* power_manager_proxy = bus->GetObjectProxy(
+ power_manager::kPowerManagerServiceName,
+ dbus::ObjectPath(power_manager::kPowerManagerServicePath));
+ dbus::MethodCall method_call(
+ power_manager::kPowerManagerInterface,
+ power_manager::kSetIsProjectingMethod);
+ dbus::MessageWriter writer(&method_call);
+ writer.AppendBool(is_projecting);
+ power_manager_proxy->CallMethod(
+ &method_call,
+ dbus::ObjectProxy::TIMEOUT_USE_DEFAULT,
+ dbus::ObjectProxy::EmptyResponseCallback());
+}
+
+} // namespace chromeos