From 2e236a564aa06711455408a2b13ac8f52ac79343 Mon Sep 17 00:00:00 2001 From: "oshima@chromium.org" Date: Wed, 27 Jun 2012 22:21:47 +0000 Subject: Relanding r144499: Rename the remaining usage of Monitor to Display BUG=none TEST=none Committed: http://src.chromium.org/viewvc/chrome?view=rev&revision=144499 Review URL: https://chromiumcodereview.appspot.com/10675011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@144585 0039d316-1c4b-4281-b951-d872f2087c98 --- chromeos/chromeos.gyp | 4 +- chromeos/display/DEPS | 3 + chromeos/display/output_configurator.cc | 804 ++++++++++++++++++++++++++++++++ chromeos/display/output_configurator.h | 165 +++++++ chromeos/monitor/DEPS | 3 - chromeos/monitor/output_configurator.cc | 804 -------------------------------- chromeos/monitor/output_configurator.h | 165 ------- 7 files changed, 974 insertions(+), 974 deletions(-) create mode 100644 chromeos/display/DEPS create mode 100644 chromeos/display/output_configurator.cc create mode 100644 chromeos/display/output_configurator.h delete mode 100644 chromeos/monitor/DEPS delete mode 100644 chromeos/monitor/output_configurator.cc delete mode 100644 chromeos/monitor/output_configurator.h (limited to 'chromeos') diff --git a/chromeos/chromeos.gyp b/chromeos/chromeos.gyp index dff9498..9870c9e 100644 --- a/chromeos/chromeos.gyp +++ b/chromeos/chromeos.gyp @@ -103,8 +103,8 @@ 'dbus/sms_client.h', 'dbus/update_engine_client.cc', 'dbus/update_engine_client.h', - 'monitor/output_configurator.cc', - 'monitor/output_configurator.h', + 'display/output_configurator.cc', + 'display/output_configurator.h', 'network/network_sms_handler.cc', 'network/network_sms_handler.h', ], diff --git a/chromeos/display/DEPS b/chromeos/display/DEPS new file mode 100644 index 0000000..d6abdda --- /dev/null +++ b/chromeos/display/DEPS @@ -0,0 +1,3 @@ +include_rules = [ + "+dbus", +] 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 +#include +#include + +// 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(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(event); + XRRNotifyEvent* notify_event = + reinterpret_cast(xevent); + if (notify_event->subtype == RRNotify_OutputChange) { + XRROutputChangeNotifyEvent* output_change_event = + reinterpret_cast(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 diff --git a/chromeos/display/output_configurator.h b/chromeos/display/output_configurator.h new file mode 100644 index 0000000..2b6d2d4 --- /dev/null +++ b/chromeos/display/output_configurator.h @@ -0,0 +1,165 @@ +// 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_DISPLAY_OUTPUT_CONFIGURATOR_H_ +#define CHROMEOS_DISPLAY_OUTPUT_CONFIGURATOR_H_ +#pragma once + +#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" + +// Forward declarations for Xlib and Xrandr. +// This is so unused X definitions don't pollute the namespace. +typedef unsigned long XID; +typedef XID Window; +typedef XID RROutput; +typedef XID RRCrtc; +typedef XID RRMode; + +struct _XRRScreenResources; +typedef _XRRScreenResources XRRScreenResources; + +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; + unsigned long mm_width; + unsigned long mm_height; +}; + +// Used to describe the state of a multi-display 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(); + + State output_state() const { return output_state_; } + + // 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); + + // Force switching the display mode to |new_state|. This method is used when + // the user explicitly changes the display mode in the options UI. Returns + // false if it was called in a single-head or headless mode. + bool SetDisplayMode(State new_state); + + // 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; + + // Called during start-up to determine what the current state of the displays + // appears to be, by investigating how the outputs compare to the data stored + // in |output_cache_|. Returns STATE_INVALID if the current display state + // doesn't match any supported state. |output_cache_| must be up-to-date with + // regards to the state of X or this method may return incorrect results. + State InferCurrentState(Display* display, XRRScreenResources* screen) const; + + // Scans the |output_cache_| to determine whether or not we are in a + // "projecting" state and then calls the DBus kSetIsProjectingMethod on powerd + // with the result. + void CheckIsProjectingAndNotify(); + + // 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 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_DISPLAY_OUTPUT_CONFIGURATOR_H_ diff --git a/chromeos/monitor/DEPS b/chromeos/monitor/DEPS deleted file mode 100644 index d6abdda..0000000 --- a/chromeos/monitor/DEPS +++ /dev/null @@ -1,3 +0,0 @@ -include_rules = [ - "+dbus", -] diff --git a/chromeos/monitor/output_configurator.cc b/chromeos/monitor/output_configurator.cc deleted file mode 100644 index 8e47f07..0000000 --- a/chromeos/monitor/output_configurator.cc +++ /dev/null @@ -1,804 +0,0 @@ -// 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 -#include -#include - -// 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 monitor_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 monitor doesn't partially -// appear on top of inactive monitor. 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(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(event); - XRRNotifyEvent* notify_event = - reinterpret_cast(xevent); - if (notify_event->subtype == RRNotify_OutputChange) { - XRROutputChangeNotifyEvent* output_change_event = - reinterpret_cast(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 diff --git a/chromeos/monitor/output_configurator.h b/chromeos/monitor/output_configurator.h deleted file mode 100644 index b98bcb4..0000000 --- a/chromeos/monitor/output_configurator.h +++ /dev/null @@ -1,165 +0,0 @@ -// 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 "base/basictypes.h" -#include "base/event_types.h" -#include "base/memory/scoped_ptr.h" -#include "base/message_loop.h" -#include "chromeos/chromeos_export.h" - -// Forward declarations for Xlib and Xrandr. -// This is so unused X definitions don't pollute the namespace. -typedef unsigned long XID; -typedef XID Window; -typedef XID RROutput; -typedef XID RRCrtc; -typedef XID RRMode; - -struct _XRRScreenResources; -typedef _XRRScreenResources XRRScreenResources; - -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; - unsigned long mm_width; - unsigned long mm_height; -}; - -// 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(); - - State output_state() const { return output_state_; } - - // 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); - - // Force switching the display mode to |new_state|. This method is used when - // the user explicitly changes the display mode in the options UI. Returns - // false if it was called in a single-head or headless mode. - bool SetDisplayMode(State new_state); - - // 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; - - // Called during start-up to determine what the current state of the displays - // appears to be, by investigating how the outputs compare to the data stored - // in |output_cache_|. Returns STATE_INVALID if the current display state - // doesn't match any supported state. |output_cache_| must be up-to-date with - // regards to the state of X or this method may return incorrect results. - State InferCurrentState(Display* display, XRRScreenResources* screen) const; - - // Scans the |output_cache_| to determine whether or not we are in a - // "projecting" state and then calls the DBus kSetIsProjectingMethod on powerd - // with the result. - void CheckIsProjectingAndNotify(); - - // 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 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_ -- cgit v1.1