summaryrefslogtreecommitdiffstats
path: root/chromeos/display
diff options
context:
space:
mode:
authorderat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-05 02:16:35 +0000
committerderat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-04-05 02:16:35 +0000
commite98603298834ec12798d02f4e5b77c377eb830c7 (patch)
tree76cb402e3f4421e75954292f617638eefed36e6e /chromeos/display
parent9123168625a008a23f8c39d717bbf2a2ed7604d5 (diff)
downloadchromium_src-e98603298834ec12798d02f4e5b77c377eb830c7.zip
chromium_src-e98603298834ec12798d02f4e5b77c377eb830c7.tar.gz
chromium_src-e98603298834ec12798d02f4e5b77c377eb830c7.tar.bz2
chromeos: Refactor OutputConfigurator.
This moves OutputConfigurator's X11 code into a separate delegate class to make testing possible. It also removes some ugliness in the form of Displays, XRRScreenResources, and Windows getting passed between methods. No functional changes are intended. BUG=225536 Review URL: https://chromiumcodereview.appspot.com/13430025 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@192449 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos/display')
-rw-r--r--chromeos/display/output_configurator.cc1080
-rw-r--r--chromeos/display/output_configurator.h220
-rw-r--r--chromeos/display/real_output_configurator_delegate.cc567
-rw-r--r--chromeos/display/real_output_configurator_delegate.h108
4 files changed, 957 insertions, 1018 deletions
diff --git a/chromeos/display/output_configurator.cc b/chromeos/display/output_configurator.cc
index fb1c9c0..a07a9de 100644
--- a/chromeos/display/output_configurator.cc
+++ b/chromeos/display/output_configurator.cc
@@ -4,107 +4,22 @@
#include "chromeos/display/output_configurator.h"
-#include <cmath>
-
-#include <X11/Xatom.h>
#include <X11/Xlib.h>
-#include <X11/extensions/dpms.h>
-#include <X11/extensions/XInput.h>
-#include <X11/extensions/XInput2.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/bind.h"
#include "base/chromeos/chromeos_version.h"
-#include "base/debug/trace_event.h"
#include "base/logging.h"
-#include "base/message_pump_aurax11.h"
-#include "base/metrics/histogram.h"
-#include "base/perftimer.h"
#include "base/strings/string_number_conversions.h"
#include "base/time.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/display/real_output_configurator_delegate.h"
namespace chromeos {
-struct OutputSnapshot {
- RROutput output;
- RRCrtc crtc;
- RRMode current_mode;
- int height;
- int y;
- RRMode native_mode;
- RRMode mirror_mode;
- bool is_internal;
- bool is_aspect_preserving_scaling;
- int touch_device_id;
- int output_index;
-};
-
-struct CoordinateTransformation {
- // Initialize to identity transformation
- CoordinateTransformation()
- : x_scale(1.0),
- x_offset(0.0),
- y_scale(1.0),
- y_offset(0.0) {
- }
-
- float x_scale;
- float x_offset;
- float y_scale;
- float y_offset;
-};
-
-struct CrtcConfig {
- CrtcConfig()
- : crtc(None),
- x(0),
- y(0),
- mode(None),
- output(None) {}
-
- CrtcConfig(RRCrtc crtc, int x, int y, RRMode mode, RROutput output)
- : crtc(crtc),
- x(x),
- y(y),
- mode(mode),
- output(output) {}
-
- RRCrtc crtc;
- int x;
- int y;
- RRMode mode;
- RROutput output;
-};
-
-enum MirrorModeType {
- MIRROR_MODE_NONE,
- MIRROR_MODE_ASPECT_PRESERVING,
- MIRROR_MODE_FALLBACK,
- MIRROR_MODE_TYPE_COUNT
-};
-
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";
@@ -138,223 +53,8 @@ std::string DisplayPowerStateToString(DisplayPowerState state) {
}
}
-// 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.
-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];
-
- return result;
-}
-
-// A helper to call XRRSetCrtcConfig with the given options but some of our
-// default output count and rotation arguments.
-void ConfigureCrtc(Display* display,
- XRRScreenResources* screen,
- CrtcConfig* config) {
- VLOG(1) << "ConfigureCrtc crtc: " << config->crtc
- << ", mode " << config->mode
- << ", output " << config->output
- << ", x " << config->x
- << ", y " << config->y;
-
- 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 ((config->output != None) && (config->mode != None)) {
- outputs = &config->output;
- num_outputs = 1;
- }
-
- XRRSetCrtcConfig(display,
- screen,
- config->crtc,
- CurrentTime,
- config->x,
- config->y,
- config->mode,
- kRotate,
- outputs,
- num_outputs);
-}
-
-// Destroys unused Crtcs, and parks used Crtcs in a way which allows a
-// framebuffer resize. This is faster than turning them off, resizing,
-// then turning them back on.
-void DestroyUnusedCrtcs(Display* display,
- XRRScreenResources* screen,
- Window window,
- CrtcConfig* config1,
- CrtcConfig* config2) {
- // Setting the screen size will fail if any CRTC doesn't fit afterwards.
- // At the same time, turning CRTCs off and back on uses up a lot of time.
- // This function tries to be smart to avoid too many off/on cycles:
- // - We disable all the CRTCs we won't need after the FB resize.
- // - We set the new modes on CRTCs, if they fit in both the old and new
- // FBs, and park them at (0,0)
- // - We disable the CRTCs we will need but don't fit in the old FB. Those
- // will be reenabled after the resize.
- // We don't worry about the cached state of the outputs here since we are
- // not interested in the state we are setting - we just try to get the CRTCs
- // out of the way so we can rebuild the frame buffer.
- for (int i = 0; i < screen->ncrtc; ++i) {
- // Default config is to disable the crtcs.
- CrtcConfig config(screen->crtcs[i], 0, 0, None, None);
-
- // If we are going to use that CRTC later, prepare it now.
- if (config1 && screen->crtcs[i] == config1->crtc) {
- config = *config1;
- config.x = 0;
- config.y = 0;
- } else if (config2 && screen->crtcs[i] == config2->crtc) {
- config = *config2;
- config.x = 0;
- config.y = 0;
- }
-
- if (config.mode != None) {
- // In case our CRTC doesn't fit in our current framebuffer, disable it.
- // It'll get reenabled after we resize the framebuffer.
- XRRModeInfo* mode_info = ModeInfoForID(screen, config.mode);
- int current_width = DisplayWidth(display, DefaultScreen(display));
- int current_height = DisplayHeight(display, DefaultScreen(display));
- if (static_cast<int>(mode_info->width) > current_width ||
- static_cast<int>(mode_info->height) > current_height) {
- config.mode = None;
- config.output = None;
- }
- }
-
- ConfigureCrtc(display, screen, &config);
- }
-}
-
-// Called to set the frame buffer (underling XRR "screen") size. Has a
-// side-effect of disabling all CRTCs.
-void CreateFrameBuffer(Display* display,
- XRRScreenResources* screen,
- Window window,
- int width,
- int height,
- CrtcConfig* config1,
- CrtcConfig* config2) {
- TRACE_EVENT0("chromeos", "OutputConfigurator::CreateFrameBuffer");
-
- int current_width = DisplayWidth(display, DefaultScreen(display));
- int current_height = DisplayHeight(display, DefaultScreen(display));
- VLOG(1) << "CreateFrameBuffer " << width << " x " << height
- << " (current=" << current_width << " x " << current_height << ")";
- if (width == current_width && height == current_height)
- return;
-
- DestroyUnusedCrtcs(display, screen, window, config1, config2);
- int mm_width = width * kPixelsToMmScale;
- int mm_height = height * kPixelsToMmScale;
- XRRSetScreenSize(display, window, width, height, mm_width, mm_height);
-}
-
-// Configures X input's Coordinate Transformation Matrix property.
-// |display| is used to make X calls.
-// |touch_device_id| is X's id of touchscreen device to configure.
-// |ctm| contains the desired transformation parameters.
-// The offsets in it should be normalized,
-// so that 1 corresponds to x or y axis size for the respectful offset.
-void ConfigureCTM(Display* display,
- int touch_device_id,
- const CoordinateTransformation& ctm) {
- int ndevices;
- XIDeviceInfo* info = XIQueryDevice(display, touch_device_id, &ndevices);
- Atom prop = XInternAtom(display, "Coordinate Transformation Matrix", False);
- Atom float_atom = XInternAtom(display, "FLOAT", False);
- if (ndevices == 1 && prop != None && float_atom != None) {
- Atom type;
- int format;
- unsigned long num_items;
- unsigned long bytes_after;
- unsigned char* data = NULL;
- // Verify that the property exists with correct format, type, etc.
- int status = XIGetProperty(display,
- info->deviceid,
- prop,
- 0, // Irrelevant - we are not interested in data
- 0, // Irrelevant - we are not interested in data
- False, // Leave the property as is
- AnyPropertyType,
- &type,
- &format,
- &num_items,
- &bytes_after,
- &data);
- if (data)
- XFree(data);
- if (status == Success && type == float_atom && format == 32) {
- float value[3][3] = {
- { ctm.x_scale, 0.0, ctm.x_offset },
- { 0.0, ctm.y_scale, ctm.y_offset },
- { 0.0, 0.0, 1.0 }
- };
- XIChangeProperty(display,
- info->deviceid,
- prop,
- type,
- format,
- PropModeReplace,
- reinterpret_cast<unsigned char*>(value),
- 9);
- }
- }
- XIFreeDeviceInfo(info);
-}
-
-// Computes the relevant transformation for mirror mode.
-// |screen| is used to make X calls.
-// |output| is the output on which mirror mode is being applied.
-// Returns the transformation, which would be identity if computations fail.
-CoordinateTransformation GetMirrorModeCTM(XRRScreenResources* screen,
- const OutputSnapshot* output) {
- CoordinateTransformation ctm; // Default to identity
- XRRModeInfo* native_mode_info = ModeInfoForID(screen, output->native_mode);
- XRRModeInfo* mirror_mode_info = ModeInfoForID(screen, output->mirror_mode);
- if (native_mode_info == NULL || mirror_mode_info == NULL)
- return ctm;
-
- if (native_mode_info->height == 0 || mirror_mode_info->height == 0 ||
- native_mode_info->width == 0 || mirror_mode_info->width == 0)
- return ctm;
-
- float native_mode_ar = static_cast<float>(native_mode_info->width) /
- static_cast<float>(native_mode_info->height);
- float mirror_mode_ar = static_cast<float>(mirror_mode_info->width) /
- static_cast<float>(mirror_mode_info->height);
-
- if (mirror_mode_ar > native_mode_ar) { // Letterboxing
- ctm.x_scale = 1.0;
- ctm.x_offset = 0.0;
- ctm.y_scale = mirror_mode_ar / native_mode_ar;
- ctm.y_offset = (native_mode_ar / mirror_mode_ar - 1.0) * 0.5;
- return ctm;
- }
- if (native_mode_ar > mirror_mode_ar) { // Pillarboxing
- ctm.y_scale = 1.0;
- ctm.y_offset = 0.0;
- ctm.x_scale = native_mode_ar / mirror_mode_ar;
- ctm.x_offset = (mirror_mode_ar / native_mode_ar - 1.0) * 0.5;
- return ctm;
- }
-
- return ctm; // Same aspect ratio - return identity
-}
-
-OutputState InferCurrentState(Display* display,
- XRRScreenResources* screen,
- const std::vector<OutputSnapshot>& outputs) {
- TRACE_EVENT0("chromeos", "OutputConfigurator::InferCurrentState");
+OutputState InferCurrentState(
+ const std::vector<OutputConfigurator::OutputSnapshot>& outputs) {
OutputState state = STATE_INVALID;
switch (outputs.size()) {
case 0:
@@ -367,7 +67,7 @@ OutputState InferCurrentState(Display* display,
RRMode primary_mode = outputs[0].current_mode;
RRMode secondary_mode = outputs[1].current_mode;
- if ((outputs[0].y == 0) && (outputs[1].y == 0)) {
+ if (outputs[0].y == 0 && outputs[1].y == 0) {
// Displays in the same spot so this is either mirror or unknown.
// Note that we should handle no configured CRTC as a "wildcard" since
// that allows us to preserve mirror mode state while power is switched
@@ -396,7 +96,7 @@ OutputState InferCurrentState(Display* display,
if (primary_native && secondary_native) {
// Just check the relative locations.
int secondary_offset = outputs[0].height + kVerticalGap;
- if ((outputs[0].y == 0) && (outputs[1].y == secondary_offset)) {
+ if (outputs[0].y == 0 && outputs[1].y == secondary_offset) {
state = STATE_DUAL_EXTENDED;
} else {
// Unexpected locations.
@@ -415,40 +115,10 @@ OutputState InferCurrentState(Display* display,
return state;
}
-RRCrtc GetNextCrtcAfter(Display* display,
- XRRScreenResources* screen,
- RROutput output,
- RRCrtc previous) {
- RRCrtc crtc = None;
- XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, output);
-
- for (int i = 0; (i < output_info->ncrtc) && (crtc == None); ++i) {
- RRCrtc this_crtc = output_info->crtcs[i];
-
- if (previous != this_crtc)
- crtc = this_crtc;
- }
- XRRFreeOutputInfo(output_info);
- return crtc;
-}
-
-XRRScreenResources* GetScreenResourcesAndRecordUMA(Display* display,
- Window window) {
- // This call to XRRGetScreenResources is implicated in a hang bug so
- // instrument it to see its typical running time (crbug.com/134449).
- // TODO(disher): Remove these UMA calls once crbug.com/134449 is resolved.
- UMA_HISTOGRAM_BOOLEAN("Display.XRRGetScreenResources_completed", false);
- PerfTimer histogram_timer;
- XRRScreenResources* screen = XRRGetScreenResources(display, window);
- base::TimeDelta duration = histogram_timer.Elapsed();
- UMA_HISTOGRAM_BOOLEAN("Display.XRRGetScreenResources_completed", true);
- UMA_HISTOGRAM_LONG_TIMES("Display.XRRGetScreenResources_duration", duration);
- return screen;
-}
-
// Determine if there is an "internal" output and how many outputs are
// connected.
-bool IsProjecting(const std::vector<OutputSnapshot>& outputs) {
+bool IsProjecting(
+ const std::vector<OutputConfigurator::OutputSnapshot>& outputs) {
bool has_internal_output = false;
int connected_output_count = outputs.size();
for (size_t i = 0; i < outputs.size(); ++i)
@@ -459,152 +129,98 @@ bool IsProjecting(const std::vector<OutputSnapshot>& outputs) {
return has_internal_output && (connected_output_count > 1);
}
-// Returns whether the |output| is configured to preserve aspect when scaling.
-bool IsOutputAspectPreservingScaling(Display* display,
- RROutput output) {
- bool ret = false;
+} // namespace
- Atom scaling_prop = XInternAtom(display, "scaling mode", False);
- Atom full_aspect_atom = XInternAtom(display, "Full aspect", False);
- if (scaling_prop == None || full_aspect_atom == None)
- return false;
+OutputConfigurator::OutputSnapshot::OutputSnapshot()
+ : output(None),
+ crtc(None),
+ current_mode(None),
+ native_mode(None),
+ mirror_mode(None),
+ y(0),
+ height(0),
+ is_internal(false),
+ is_aspect_preserving_scaling(false),
+ touch_device_id(0) {}
+
+OutputConfigurator::CoordinateTransformation::CoordinateTransformation()
+ : x_scale(1.0),
+ x_offset(0.0),
+ y_scale(1.0),
+ y_offset(0.0) {}
+
+OutputConfigurator::CrtcConfig::CrtcConfig()
+ : crtc(None),
+ x(0),
+ y(0),
+ mode(None),
+ output(None) {}
- int nprop = 0;
- Atom* props = XRRListOutputProperties(display, output, &nprop);
- for (int j = 0; j < nprop && !ret; j++) {
- Atom prop = props[j];
- if (scaling_prop == prop) {
- unsigned char* values = NULL;
- int actual_format;
- unsigned long nitems;
- unsigned long bytes_after;
- Atom actual_type;
- int success;
-
- success = XRRGetOutputProperty(display,
- output,
- prop,
- 0,
- 100,
- False,
- False,
- AnyPropertyType,
- &actual_type,
- &actual_format,
- &nitems,
- &bytes_after,
- &values);
- if (success == Success && actual_type == XA_ATOM &&
- actual_format == 32 && nitems == 1) {
- Atom value = reinterpret_cast<Atom*>(values)[0];
- if (full_aspect_atom == value)
- ret = true;
- }
- if (values)
- XFree(values);
- }
- }
- if (props)
- XFree(props);
+OutputConfigurator::CrtcConfig::CrtcConfig(RRCrtc crtc,
+ int x, int y,
+ RRMode mode,
+ RROutput output)
+ : crtc(crtc),
+ x(x),
+ y(y),
+ mode(mode),
+ output(output) {}
- return ret;
+// static
+bool OutputConfigurator::IsInternalOutputName(const std::string& name) {
+ return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0;
}
-} // namespace
-
OutputConfigurator::OutputConfigurator()
- // If we aren't running on ChromeOS (like linux desktop),
- // don't try to configure display.
- : delegate_(NULL),
+ : state_controller_(NULL),
+ delegate_(new RealOutputConfiguratorDelegate()),
configure_display_(base::chromeos::IsRunningOnChromeOS()),
- is_panel_fitting_enabled_(false),
connected_output_count_(0),
xrandr_event_base_(0),
output_state_(STATE_INVALID),
- power_state_(DISPLAY_POWER_ALL_ON),
- mirror_mode_will_preserve_aspect_(false),
- mirror_mode_preserved_aspect_(false),
- last_enter_state_time_() {
+ power_state_(DISPLAY_POWER_ALL_ON) {
}
-OutputConfigurator::~OutputConfigurator() {
- RecordPreviousStateUMA();
-}
+OutputConfigurator::~OutputConfigurator() {}
void OutputConfigurator::Init(bool is_panel_fitting_enabled,
uint32 background_color_argb) {
- TRACE_EVENT0("chromeos", "OutputConfigurator::Init");
if (!configure_display_)
return;
- is_panel_fitting_enabled_ = is_panel_fitting_enabled;
// Cache the initial output state.
- Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
- CHECK(display != NULL);
- Window window = DefaultRootWindow(display);
- XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window);
- CHECK(screen != NULL);
- std::vector<OutputSnapshot> outputs = GetDualOutputs(display, screen);
- if (outputs.size() > 1 && background_color_argb) {
- // Configuring CRTCs/Framebuffer clears the boot screen image.
- // Set the same background color while configuring the
- // display to minimize the duration of black screen at boot
- // time. The background is filled with black later in
- // ash::DisplayManager.
- // crbug.com/171050.
- XSetWindowAttributes swa = {0};
- XColor color;
- Colormap colormap = DefaultColormap(display, 0);
- // XColor uses 16 bits per color.
- color.red = (background_color_argb & 0x00FF0000) >> 8;
- color.green = (background_color_argb & 0x0000FF00);
- color.blue = (background_color_argb & 0x000000FF) << 8;
- color.flags = DoRed | DoGreen | DoBlue;
- XAllocColor(display, colormap, &color);
- swa.background_pixel = color.pixel;
- XChangeWindowAttributes(display, window, CWBackPixel, &swa);
- XFreeColors(display, colormap, &color.pixel, 1, 0);
- }
- XRRFreeScreenResources(screen);
+ delegate_->SetPanelFittingEnabled(is_panel_fitting_enabled);
+ delegate_->GrabServer();
+ std::vector<OutputSnapshot> outputs = delegate_->GetOutputs();
+ if (outputs.size() > 1 && background_color_argb)
+ delegate_->SetBackgroundColor(background_color_argb);
+ delegate_->UngrabServer();
}
void OutputConfigurator::Start() {
- Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
- Window window = DefaultRootWindow(display);
-
- XGrabServer(display);
- XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window);
- CHECK(screen != NULL);
+ delegate_->GrabServer();
// Detect our initial state.
- std::vector<OutputSnapshot> outputs = GetDualOutputs(display, screen);
+ std::vector<OutputSnapshot> outputs = delegate_->GetOutputs();
connected_output_count_ = outputs.size();
- output_state_ = InferCurrentState(display, screen, outputs);
+ output_state_ = InferCurrentState(outputs);
// Ensure that we are in a supported state with all connected displays powered
// on.
- OutputState starting_state = GetNextState(display,
- screen,
- STATE_INVALID,
- outputs);
+ OutputState starting_state = GetNextState(outputs);
if (output_state_ != starting_state &&
- EnterState(display, screen, window, starting_state, power_state_,
- outputs)) {
+ EnterState(starting_state, power_state_, outputs)) {
output_state_ = starting_state;
}
bool is_projecting = IsProjecting(outputs);
- // 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);
+ delegate_->InitXRandRExtension(&xrandr_event_base_);
// Force the DPMS on chrome startup as the driver doesn't always detect
// that all displays are on when signing out.
- CHECK(DPMSEnable(display));
- CHECK(DPMSForceLevel(display, DPMSModeOn));
+ delegate_->ForceDPMSOn();
// Relinquish X resources.
- XRRFreeScreenResources(screen);
- XUngrabServer(display);
+ delegate_->UngrabServer();
chromeos::DBusThreadManager::Get()->GetPowerManagerClient()->
SetIsProjecting(is_projecting);
@@ -616,7 +232,6 @@ void OutputConfigurator::Stop() {
bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state,
int flags) {
- TRACE_EVENT0("chromeos", "OutputConfigurator::SetDisplayPower");
VLOG(1) << "SetDisplayPower: power_state="
<< DisplayPowerStateToString(power_state) << " flags=" << flags;
@@ -625,37 +240,28 @@ bool OutputConfigurator::SetDisplayPower(DisplayPowerState power_state,
if (power_state == power_state_ && !(flags & kSetDisplayPowerForceProbe))
return true;
- Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
- CHECK(display);
- XGrabServer(display);
- Window window = DefaultRootWindow(display);
- XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window);
- CHECK(screen);
- std::vector<OutputSnapshot> outputs = GetDualOutputs(display, screen);
+ delegate_->GrabServer();
+ std::vector<OutputSnapshot> outputs = delegate_->GetOutputs();
connected_output_count_ = outputs.size();
bool only_if_single_internal_display =
flags & kSetDisplayPowerOnlyIfSingleInternalDisplay;
bool single_internal_display = outputs.size() == 1 && outputs[0].is_internal;
if ((single_internal_display || !only_if_single_internal_display) &&
- EnterState(display, screen, window, output_state_, power_state,
- outputs)) {
+ EnterState(output_state_, power_state, outputs)) {
power_state_ = power_state;
if (power_state != DISPLAY_POWER_ALL_OFF) {
// Force the DPMS on since the driver doesn't always detect that it
// should turn on. This is needed when coming back from idle suspend.
- CHECK(DPMSEnable(display));
- CHECK(DPMSForceLevel(display, DPMSModeOn));
+ delegate_->ForceDPMSOn();
}
}
- XRRFreeScreenResources(screen);
- XUngrabServer(display);
+ delegate_->UngrabServer();
return true;
}
bool OutputConfigurator::SetDisplayMode(OutputState new_state) {
- TRACE_EVENT0("chromeos", "OutputConfigurator::SetDisplayMode");
if (output_state_ == STATE_INVALID ||
output_state_ == STATE_HEADLESS ||
output_state_ == STATE_SINGLE)
@@ -664,20 +270,12 @@ bool OutputConfigurator::SetDisplayMode(OutputState new_state) {
if (output_state_ == new_state)
return true;
- Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
- CHECK(display != NULL);
- XGrabServer(display);
- Window window = DefaultRootWindow(display);
- XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window);
- CHECK(screen != NULL);
-
- std::vector<OutputSnapshot> outputs = GetDualOutputs(display, screen);
+ delegate_->GrabServer();
+ std::vector<OutputSnapshot> outputs = delegate_->GetOutputs();
connected_output_count_ = outputs.size();
- if (EnterState(display, screen, window, new_state, power_state_, outputs))
+ if (EnterState(new_state, power_state_, outputs))
output_state_ = new_state;
-
- XRRFreeScreenResources(screen);
- XUngrabServer(display);
+ delegate_->UngrabServer();
if (output_state_ == new_state) {
NotifyOnDisplayChanged();
@@ -689,9 +287,8 @@ bool OutputConfigurator::SetDisplayMode(OutputState new_state) {
}
bool OutputConfigurator::Dispatch(const base::NativeEvent& event) {
- TRACE_EVENT0("chromeos", "OutputConfigurator::Dispatch");
if (event->type - xrandr_event_base_ == RRScreenChangeNotify)
- XRRUpdateConfiguration(event);
+ delegate_->UpdateXRandRConfiguration(event);
// Ignore this event if the Xrandr extension isn't supported, or
// the device is being shutdown.
if (!configure_display_ ||
@@ -727,31 +324,21 @@ bool OutputConfigurator::Dispatch(const base::NativeEvent& event) {
}
void OutputConfigurator::ConfigureOutputs() {
- TRACE_EVENT0("chromeos", "OutputConfigurator::ConfigureOutputs");
configure_timer_.reset();
- Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
- CHECK(display != NULL);
- XGrabServer(display);
- Window window = DefaultRootWindow(display);
- XRRScreenResources* screen = GetScreenResourcesAndRecordUMA(display, window);
- CHECK(screen != NULL);
-
- std::vector<OutputSnapshot> outputs = GetDualOutputs(display, screen);
+ delegate_->GrabServer();
+ std::vector<OutputSnapshot> outputs = delegate_->GetOutputs();
int new_output_count = outputs.size();
// Don't skip even if the output counts didn't change because
// a display might have been swapped during the suspend.
connected_output_count_ = new_output_count;
- OutputState new_state =
- GetNextState(display, screen, STATE_INVALID, outputs);
+ OutputState new_state = GetNextState(outputs);
// When a display was swapped, the state moves from
// STATE_DUAL_EXTENDED to STATE_DUAL_EXTENDED, so don't rely on
// the state chagne to tell if it was successful.
- bool success =
- EnterState(display, screen, window, new_state, power_state_, outputs);
+ bool success = EnterState(new_state, power_state_, outputs);
bool is_projecting = IsProjecting(outputs);
- XRRFreeScreenResources(screen);
- XUngrabServer(display);
+ delegate_->UngrabServer();
if (success) {
output_state_ = new_state;
@@ -772,11 +359,6 @@ void OutputConfigurator::RemoveObserver(Observer* observer) {
observers_.RemoveObserver(observer);
}
-// static
-bool OutputConfigurator::IsInternalOutputName(const std::string& name) {
- return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0;
-}
-
void OutputConfigurator::SuspendDisplays() {
// If the display is off due to user inactivity and there's only a single
// internal display connected, switch to the all-on state before
@@ -791,7 +373,7 @@ void OutputConfigurator::SuspendDisplays() {
// We need to make sure that the monitor configuration we just did actually
// completes before we return, because otherwise the X message could be
// racing with the HandleSuspendReadiness message.
- XSync(base::MessagePumpAuraX11::GetDefaultXDisplay(), 0);
+ delegate_->SyncWithServer();
}
void OutputConfigurator::ResumeDisplays() {
@@ -801,341 +383,17 @@ void OutputConfigurator::ResumeDisplays() {
}
void OutputConfigurator::NotifyOnDisplayChanged() {
- TRACE_EVENT0("chromeos", "OutputConfigurator::NotifyOnDisplayChanged");
FOR_EACH_OBSERVER(Observer, observers_, OnDisplayModeChanged());
}
-std::vector<OutputSnapshot> OutputConfigurator::GetDualOutputs(
- Display* display,
- XRRScreenResources* screen) {
- std::vector<OutputSnapshot> outputs;
- XRROutputInfo* one_info = NULL;
- XRROutputInfo* two_info = NULL;
-
- for (int i = 0; (i < screen->noutput) && (outputs.size() < 2); ++i) {
- RROutput this_id = screen->outputs[i];
- XRROutputInfo* output_info = XRRGetOutputInfo(display, screen, this_id);
- bool is_connected = (output_info->connection == RR_Connected);
-
- if (is_connected) {
- OutputSnapshot to_populate;
- to_populate.output_index = i;
-
- if (outputs.size() == 0) {
- one_info = output_info;
- } else {
- two_info = output_info;
- }
-
- to_populate.output = this_id;
- // Now, look up the corresponding CRTC and any related info.
- to_populate.crtc = output_info->crtc;
- if (None != to_populate.crtc) {
- XRRCrtcInfo* crtc_info =
- XRRGetCrtcInfo(display, screen, to_populate.crtc);
- to_populate.current_mode = crtc_info->mode;
- to_populate.height = crtc_info->height;
- to_populate.y = crtc_info->y;
- XRRFreeCrtcInfo(crtc_info);
- } else {
- to_populate.current_mode = 0;
- to_populate.height = 0;
- to_populate.y = 0;
- }
- // Find the native_mode and leave the mirror_mode for the pass after the
- // loop.
- to_populate.native_mode = GetOutputNativeMode(output_info);
- to_populate.mirror_mode = 0;
-
- // See if this output refers to an internal display.
- to_populate.is_internal = IsInternalOutput(output_info);
-
- to_populate.is_aspect_preserving_scaling =
- IsOutputAspectPreservingScaling(display, this_id);
- to_populate.touch_device_id = None;
-
- VLOG(1) << "Found display #" << outputs.size()
- << " with output " << (int)to_populate.output
- << " crtc " << (int)to_populate.crtc
- << " current mode " << (int)to_populate.current_mode;
- outputs.push_back(to_populate);
- } else {
- XRRFreeOutputInfo(output_info);
- }
- }
-
- if (outputs.size() == 2) {
- bool one_is_internal = IsInternalOutput(one_info);
- bool two_is_internal = IsInternalOutput(two_info);
- int internal_outputs = (one_is_internal ? 1 : 0) +
- (two_is_internal ? 1 : 0);
-
- DCHECK(internal_outputs < 2);
- LOG_IF(WARNING, internal_outputs == 2) << "Two internal outputs detected.";
-
- bool can_mirror = false;
-
- for (int attempt = 0; attempt < 2 && !can_mirror; attempt++) {
- // Try preserving external output's aspect ratio on the first attempt
- // If that fails, fall back to the highest matching resolution
- bool preserve_aspect = attempt == 0;
-
- if (internal_outputs == 1) {
- if (one_is_internal) {
- can_mirror = FindOrCreateMirrorMode(display,
- screen,
- one_info,
- two_info,
- outputs[0].output,
- is_panel_fitting_enabled_,
- preserve_aspect,
- &outputs[0].mirror_mode,
- &outputs[1].mirror_mode);
- } else { // if (two_is_internal)
- can_mirror = FindOrCreateMirrorMode(display,
- screen,
- two_info,
- one_info,
- outputs[1].output,
- is_panel_fitting_enabled_,
- preserve_aspect,
- &outputs[1].mirror_mode,
- &outputs[0].mirror_mode);
- }
- } else { // if (internal_outputs == 0)
- // No panel fitting for external outputs, so fall back to exact match
- can_mirror = FindOrCreateMirrorMode(display,
- screen,
- one_info,
- two_info,
- outputs[0].output,
- false,
- preserve_aspect,
- &outputs[0].mirror_mode,
- &outputs[1].mirror_mode);
- if (!can_mirror && preserve_aspect) {
- // FindOrCreateMirrorMode will try to preserve aspect ratio of
- // what it thinks is external display, so if it didn't succeed
- // with one, maybe it will succeed with the other.
- // This way we will have correct aspect ratio on at least one of them.
- can_mirror = FindOrCreateMirrorMode(display,
- screen,
- two_info,
- one_info,
- outputs[1].output,
- false,
- preserve_aspect,
- &outputs[1].mirror_mode,
- &outputs[0].mirror_mode);
- }
- }
-
- if (can_mirror) {
- if (preserve_aspect) {
- UMA_HISTOGRAM_ENUMERATION(
- "Display.GetDualOutputs.detected_mirror_mode",
- MIRROR_MODE_ASPECT_PRESERVING,
- MIRROR_MODE_TYPE_COUNT);
- } else {
- UMA_HISTOGRAM_ENUMERATION(
- "Display.GetDualOutputs.detected_mirror_mode",
- MIRROR_MODE_FALLBACK,
- MIRROR_MODE_TYPE_COUNT);
- }
- mirror_mode_will_preserve_aspect_ = preserve_aspect;
- }
- }
-
- if (!can_mirror) {
- // We can't mirror so set mirror_mode to None.
- outputs[0].mirror_mode = None;
- outputs[1].mirror_mode = None;
-
- UMA_HISTOGRAM_ENUMERATION(
- "Display.GetDualOutputs.detected_mirror_mode",
- MIRROR_MODE_NONE,
- MIRROR_MODE_TYPE_COUNT);
- mirror_mode_will_preserve_aspect_ = false;
- }
- }
-
- GetTouchscreens(display, screen, outputs);
-
- XRRFreeOutputInfo(one_info);
- XRRFreeOutputInfo(two_info);
- return outputs;
-}
-
-bool OutputConfigurator::FindOrCreateMirrorMode(Display* display,
- XRRScreenResources* screen,
- XRROutputInfo* internal_info,
- XRROutputInfo* external_info,
- RROutput internal_output_id,
- bool try_creating,
- bool preserve_aspect,
- RRMode* internal_mirror_mode,
- RRMode* external_mirror_mode) {
- RRMode internal_mode_id = GetOutputNativeMode(internal_info);
- RRMode external_mode_id = GetOutputNativeMode(external_info);
-
- if (internal_mode_id == None || external_mode_id == None)
- return false;
-
- XRRModeInfo* internal_native_mode = ModeInfoForID(screen, internal_mode_id);
- XRRModeInfo* external_native_mode = ModeInfoForID(screen, external_mode_id);
-
- // Check if some external output resolution can be mirrored on internal.
- // Prefer the modes in the order that X sorts them,
- // assuming this is the order in which they look better on the monitor.
- // If X's order is not satisfactory, we can either fix X's sorting,
- // or implement our sorting here.
- for (int i = 0; i < external_info->nmode; i++) {
- external_mode_id = external_info->modes[i];
- XRRModeInfo* external_mode = ModeInfoForID(screen, external_mode_id);
- bool is_native_aspect_ratio =
- external_native_mode->width * external_mode->height ==
- external_native_mode->height * external_mode->width;
- if (preserve_aspect && !is_native_aspect_ratio)
- continue; // Allow only aspect ratio preserving modes for mirroring
-
- // Try finding exact match
- for (int j = 0; j < internal_info->nmode; j++) {
- internal_mode_id = internal_info->modes[j];
- XRRModeInfo* internal_mode = ModeInfoForID(screen, internal_mode_id);
- bool is_internal_interlaced = internal_mode->modeFlags & RR_Interlace;
- bool is_external_interlaced = external_mode->modeFlags & RR_Interlace;
- if (internal_mode->width == external_mode->width &&
- internal_mode->height == external_mode->height &&
- is_internal_interlaced == is_external_interlaced) {
- *internal_mirror_mode = internal_mode_id;
- *external_mirror_mode = external_mode_id;
- return true; // Mirror mode found
- }
- }
-
- // Try to create a matching internal output mode by panel fitting
- if (try_creating) {
- // We can downscale by 1.125, and upscale indefinitely
- // Downscaling looks ugly, so, can fit == can upscale
- // Also, internal panels don't support fitting interlaced modes
- bool can_fit =
- internal_native_mode->width >= external_mode->width &&
- internal_native_mode->height >= external_mode->height &&
- !(external_mode->modeFlags & RR_Interlace);
- if (can_fit) {
- XRRAddOutputMode(display, internal_output_id, external_mode_id);
- *internal_mirror_mode = *external_mirror_mode = external_mode_id;
- return true; // Mirror mode created
- }
- }
- }
-
- return false;
-}
-
-void OutputConfigurator::GetTouchscreens(Display* display,
- XRRScreenResources* screen,
- std::vector<OutputSnapshot>& outputs) {
- int ndevices = 0;
- Atom valuator_x = XInternAtom(display, "Abs MT Position X", False);
- Atom valuator_y = XInternAtom(display, "Abs MT Position Y", False);
- if (valuator_x == None || valuator_y == None)
- return;
-
- XIDeviceInfo* info = XIQueryDevice(display, XIAllDevices, &ndevices);
- for (int i = 0; i < ndevices; i++) {
- if (!info[i].enabled || info[i].use != XIFloatingSlave)
- continue; // Assume all touchscreens are floating slaves
-
- double width = -1.0;
- double height = -1.0;
- bool is_direct_touch = false;
-
- for (int j = 0; j < info[i].num_classes; j++) {
- XIAnyClassInfo* class_info = info[i].classes[j];
-
- if (class_info->type == XIValuatorClass) {
- XIValuatorClassInfo* valuator_info =
- reinterpret_cast<XIValuatorClassInfo*>(class_info);
-
- if (valuator_x == valuator_info->label) {
- // Ignore X axis valuator with unexpected properties
- if (valuator_info->number == 0 && valuator_info->mode == Absolute &&
- valuator_info->min == 0.0) {
- width = valuator_info->max;
- }
- } else if (valuator_y == valuator_info->label) {
- // Ignore Y axis valuator with unexpected properties
- if (valuator_info->number == 1 && valuator_info->mode == Absolute &&
- valuator_info->min == 0.0) {
- height = valuator_info->max;
- }
- }
- }
-#if defined(USE_XI2_MT)
- if (class_info->type == XITouchClass) {
- XITouchClassInfo* touch_info =
- reinterpret_cast<XITouchClassInfo*>(class_info);
- is_direct_touch = touch_info->mode == XIDirectTouch;
- }
-#endif
- }
-
- // Touchscreens should have absolute X and Y axes,
- // and be direct touch devices.
- if (width > 0.0 && height > 0.0 && is_direct_touch) {
- size_t k = 0;
- for (; k < outputs.size(); k++) {
- if (outputs[k].native_mode == None ||
- outputs[k].touch_device_id != None)
- continue;
- XRRModeInfo* native_mode = ModeInfoForID(screen,
- outputs[k].native_mode);
- if (native_mode == NULL)
- continue;
-
- // Allow 1 pixel difference between screen and touchscreen resolutions.
- // Because in some cases for monitor resolution 1024x768 touchscreen's
- // resolution would be 1024x768, but for some 1023x767.
- // It really depends on touchscreen's firmware configuration.
- if (std::abs(native_mode->width - width) <= 1.0 &&
- std::abs(native_mode->height - height) <= 1.0) {
- outputs[k].touch_device_id = info[i].deviceid;
-
- VLOG(1) << "Found touchscreen for output #" << k
- << " id " << outputs[k].touch_device_id
- << " width " << width
- << " height " << height;
- break;
- }
- }
-
- VLOG_IF(1, k == outputs.size())
- << "No matching output - ignoring touchscreen id " << info[i].deviceid
- << " width " << width
- << " height " << height;
- }
- }
-
- XIFreeDeviceInfo(info);
-}
-
bool OutputConfigurator::EnterState(
- Display* display,
- XRRScreenResources* screen,
- Window window,
OutputState output_state,
DisplayPowerState power_state,
const std::vector<OutputSnapshot>& outputs) {
- TRACE_EVENT0("chromeos", "OutputConfigurator::EnterState");
-
- std::vector<RRCrtc> crtcs(outputs.size());
std::vector<bool> output_power(outputs.size());
bool all_outputs_off = true;
- RRCrtc prev_crtc = None;
- for (size_t i = 0; i < outputs.size(); prev_crtc = crtcs[i], ++i) {
- crtcs[i] = GetNextCrtcAfter(display, screen, outputs[i].output, prev_crtc);
+ for (size_t i = 0; i < outputs.size(); ++i) {
output_power[i] = power_state == DISPLAY_POWER_ALL_ON ||
(power_state == DISPLAY_POWER_INTERNAL_OFF_EXTERNAL_ON &&
!outputs[i].is_internal) ||
@@ -1151,71 +409,69 @@ bool OutputConfigurator::EnterState(
break;
case 1: {
// Re-allocate the framebuffer to fit.
- XRRModeInfo* mode_info = ModeInfoForID(screen, outputs[0].native_mode);
- if (!mode_info) {
- UMA_HISTOGRAM_COUNTS("Display.EnterState.single_failures", 1);
+ int width = 0, height = 0;
+ if (!delegate_->GetModeDetails(
+ outputs[0].native_mode, &width, &height, NULL)) {
return false;
}
- CrtcConfig config(crtcs[0], 0, 0,
+ CrtcConfig config(outputs[0].crtc, 0, 0,
output_power[0] ? outputs[0].native_mode : None,
outputs[0].output);
- CreateFrameBuffer(display, screen, window, mode_info->width,
- mode_info->height, &config, NULL);
- ConfigureCrtc(display, screen, &config);
+ delegate_->CreateFrameBuffer(width, height, &config, NULL);
+ delegate_->ConfigureCrtc(&config);
if (outputs[0].touch_device_id) {
// Restore identity transformation for single monitor in native mode.
- ConfigureCTM(display, outputs[0].touch_device_id,
- CoordinateTransformation());
+ delegate_->ConfigureCTM(outputs[0].touch_device_id,
+ CoordinateTransformation());
}
break;
}
case 2: {
if (output_state == STATE_DUAL_MIRROR) {
- XRRModeInfo* mode_info = ModeInfoForID(screen, outputs[0].mirror_mode);
- if (!mode_info) {
- UMA_HISTOGRAM_COUNTS("Display.EnterState.mirror_failures", 1);
+ int width = 0, height = 0;
+ if (!delegate_->GetModeDetails(
+ outputs[0].mirror_mode, &width, &height, NULL)) {
return false;
}
std::vector<CrtcConfig> configs(outputs.size());
for (size_t i = 0; i < outputs.size(); ++i) {
configs[i] = CrtcConfig(
- crtcs[i], 0, 0,
+ outputs[i].crtc, 0, 0,
output_power[i] ? outputs[i].mirror_mode : None,
outputs[i].output);
}
- CreateFrameBuffer(display, screen, window, mode_info->width,
- mode_info->height, &configs[0], &configs[1]);
+ delegate_->CreateFrameBuffer(width, height, &configs[0], &configs[1]);
for (size_t i = 0; i < outputs.size(); ++i) {
- ConfigureCrtc(display, screen, &configs[i]);
+ delegate_->ConfigureCrtc(&configs[i]);
if (outputs[i].touch_device_id) {
CoordinateTransformation ctm;
// CTM needs to be calculated if aspect preserving scaling is used.
// Otherwise, assume it is full screen, and use identity CTM.
if (outputs[i].mirror_mode != outputs[i].native_mode &&
outputs[i].is_aspect_preserving_scaling) {
- ctm = GetMirrorModeCTM(screen, &outputs[i]);
+ ctm = GetMirrorModeCTM(&outputs[i]);
}
- ConfigureCTM(display, outputs[i].touch_device_id, ctm);
+ delegate_->ConfigureCTM(outputs[i].touch_device_id, ctm);
}
}
} else { // STATE_DUAL_EXTENDED
- std::vector<XRRModeInfo*> mode_infos(outputs.size());
+ // Pairs are [width, height] corresponding to the given output's mode.
+ std::vector<std::pair<int, int> > mode_sizes(outputs.size());
std::vector<CrtcConfig> configs(outputs.size());
int width = 0, height = 0;
for (size_t i = 0; i < outputs.size(); ++i) {
- mode_infos[i] = ModeInfoForID(screen, outputs[i].native_mode);
- if (!mode_infos[i]) {
- UMA_HISTOGRAM_COUNTS("Display.EnterState.dual_failures", 1);
+ if (!delegate_->GetModeDetails(outputs[i].native_mode,
+ &(mode_sizes[i].first), &(mode_sizes[i].second), NULL)) {
return false;
}
configs[i] = CrtcConfig(
- crtcs[i], 0, height,
+ outputs[i].crtc, 0, height,
output_power[i] ? outputs[i].native_mode : None,
outputs[i].output);
@@ -1223,23 +479,22 @@ bool OutputConfigurator::EnterState(
// desktop configuration can be restored when the outputs are
// turned back on.
if (output_power[i] || all_outputs_off) {
- width = std::max<int>(width, mode_infos[i]->width);
- height += (height ? kVerticalGap : 0) + mode_infos[i]->height;
+ width = std::max<int>(width, mode_sizes[i].first);
+ height += (height ? kVerticalGap : 0) + mode_sizes[i].second;
}
}
- CreateFrameBuffer(display, screen, window, width, height,
- &configs[0], &configs[1]);
+ delegate_->CreateFrameBuffer(width, height, &configs[0], &configs[1]);
for (size_t i = 0; i < outputs.size(); ++i) {
- ConfigureCrtc(display, screen, &configs[i]);
+ delegate_->ConfigureCrtc(&configs[i]);
if (outputs[i].touch_device_id) {
CoordinateTransformation ctm;
- ctm.x_scale = static_cast<float>(mode_infos[i]->width) / width;
+ ctm.x_scale = static_cast<float>(mode_sizes[i].first) / width;
ctm.x_offset = static_cast<float>(configs[i].x) / width;
- ctm.y_scale = static_cast<float>(mode_infos[i]->height) / height;
+ ctm.y_scale = static_cast<float>(mode_sizes[i].second) / height;
ctm.y_offset = static_cast<float>(configs[i].y) / height;
- ConfigureCTM(display, outputs[i].touch_device_id, ctm);
+ delegate_->ConfigureCTM(outputs[i].touch_device_id, ctm);
}
}
}
@@ -1249,99 +504,68 @@ bool OutputConfigurator::EnterState(
NOTREACHED() << "Got " << outputs.size() << " outputs";
}
- RecordPreviousStateUMA();
return true;
}
-void OutputConfigurator::RecordPreviousStateUMA() {
- base::TimeDelta duration = base::TimeTicks::Now() - last_enter_state_time_;
-
- // |output_state_| can be used for the state being left,
- // since RecordPreviousStateUMA is called from EnterState,
- // and |output_state_| is always updated after EnterState is called.
- switch (output_state_) {
- case STATE_SINGLE:
- UMA_HISTOGRAM_LONG_TIMES("Display.EnterState.single_duration", duration);
- break;
- case STATE_DUAL_MIRROR:
- if (mirror_mode_preserved_aspect_)
- UMA_HISTOGRAM_LONG_TIMES("Display.EnterState.mirror_aspect_duration",
- duration);
- else
- UMA_HISTOGRAM_LONG_TIMES("Display.EnterState.mirror_fallback_duration",
- duration);
- break;
- case STATE_DUAL_EXTENDED:
- UMA_HISTOGRAM_LONG_TIMES("Display.EnterState.dual_primary_duration",
- duration);
- break;
- default:
- break;
- }
-
- mirror_mode_preserved_aspect_ = mirror_mode_will_preserve_aspect_;
- last_enter_state_time_ = base::TimeTicks::Now();
-}
-
OutputState OutputConfigurator::GetNextState(
- Display* display,
- XRRScreenResources* screen,
- OutputState current_state,
const std::vector<OutputSnapshot>& output_snapshots) const {
- TRACE_EVENT0("chromeos", "OutputConfigurator::GetNextState");
- OutputState state = STATE_INVALID;
-
switch (output_snapshots.size()) {
case 0:
- state = STATE_HEADLESS;
- break;
+ return STATE_HEADLESS;
case 1:
- state = STATE_SINGLE;
- break;
+ return STATE_SINGLE;
case 2: {
- bool mirror_supported = (0 != output_snapshots[0].mirror_mode) &&
- (0 != output_snapshots[1].mirror_mode);
- switch (current_state) {
- case STATE_DUAL_EXTENDED:
- state =
- mirror_supported ? STATE_DUAL_MIRROR : STATE_DUAL_EXTENDED;
- break;
- case STATE_DUAL_MIRROR:
- state = STATE_DUAL_EXTENDED;
- break;
- case STATE_INVALID: {
- std::vector<OutputInfo> outputs;
- for (size_t i = 0; i < output_snapshots.size(); ++i) {
- outputs.push_back(OutputInfo());
- outputs[i].output = output_snapshots[i].output;
- outputs[i].output_index = output_snapshots[i].output_index;
- }
- state = delegate_->GetStateForOutputs(outputs);
- break;
- }
- default:
- state = STATE_DUAL_EXTENDED;
+ std::vector<OutputInfo> outputs;
+ for (size_t i = 0; i < output_snapshots.size(); ++i) {
+ outputs.push_back(OutputInfo());
+ outputs[i].output = output_snapshots[i].output;
+ outputs[i].output_index = i;
}
- break;
+ return state_controller_->GetStateForOutputs(outputs);
}
default:
- CHECK(false);
+ NOTREACHED();
}
- return state;
+ return STATE_INVALID;
}
-// static
-bool OutputConfigurator::IsInternalOutput(const XRROutputInfo* output_info) {
- return IsInternalOutputName(std::string(output_info->name));
-}
+OutputConfigurator::CoordinateTransformation
+OutputConfigurator::GetMirrorModeCTM(
+ const OutputConfigurator::OutputSnapshot* output) {
+ CoordinateTransformation ctm; // Default to identity
+ int native_mode_width = 0, native_mode_height = 0;
+ int mirror_mode_width = 0, mirror_mode_height = 0;
+ if (!delegate_->GetModeDetails(output->native_mode,
+ &native_mode_width, &native_mode_height, NULL) ||
+ !delegate_->GetModeDetails(output->mirror_mode,
+ &mirror_mode_width, &mirror_mode_height, NULL))
+ return ctm;
-// static
-RRMode OutputConfigurator::GetOutputNativeMode(
- const XRROutputInfo* output_info) {
- if (output_info->nmode <= 0)
- return None;
+ if (native_mode_height == 0 || mirror_mode_height == 0 ||
+ native_mode_width == 0 || mirror_mode_width == 0)
+ return ctm;
+
+ float native_mode_ar = static_cast<float>(native_mode_width) /
+ static_cast<float>(native_mode_height);
+ float mirror_mode_ar = static_cast<float>(mirror_mode_width) /
+ static_cast<float>(mirror_mode_height);
+
+ if (mirror_mode_ar > native_mode_ar) { // Letterboxing
+ ctm.x_scale = 1.0;
+ ctm.x_offset = 0.0;
+ ctm.y_scale = mirror_mode_ar / native_mode_ar;
+ ctm.y_offset = (native_mode_ar / mirror_mode_ar - 1.0) * 0.5;
+ return ctm;
+ }
+ if (native_mode_ar > mirror_mode_ar) { // Pillarboxing
+ ctm.y_scale = 1.0;
+ ctm.y_offset = 0.0;
+ ctm.x_scale = native_mode_ar / mirror_mode_ar;
+ ctm.x_offset = (mirror_mode_ar / native_mode_ar - 1.0) * 0.5;
+ return ctm;
+ }
- return output_info->modes[0];
+ return ctm; // Same aspect ratio - return identity
}
} // namespace chromeos
diff --git a/chromeos/display/output_configurator.h b/chromeos/display/output_configurator.h
index 2a49204..2937088 100644
--- a/chromeos/display/output_configurator.h
+++ b/chromeos/display/output_configurator.h
@@ -19,22 +19,12 @@
// 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 _XDisplay;
-typedef struct _XDisplay Display;
-struct _XRROutputInfo;
-typedef _XRROutputInfo XRROutputInfo;
-struct _XRRScreenResources;
-typedef _XRRScreenResources XRRScreenResources;
-
namespace chromeos {
-struct OutputSnapshot;
-
// Used to describe the state of a multi-display configuration.
enum OutputState {
STATE_INVALID,
@@ -55,13 +45,58 @@ struct OutputInfo {
};
// 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.
+// CTRCs and Outputs.
class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
public:
+ // Information about an output's current state.
+ struct OutputSnapshot {
+ OutputSnapshot();
+
+ RROutput output;
+
+ // CRTC that should be used for this output. Not necessarily the CRTC
+ // that XRandR reports is currently being used.
+ RRCrtc crtc;
+
+ RRMode current_mode;
+ RRMode native_mode;
+ RRMode mirror_mode;
+
+ int y;
+ int height;
+
+ bool is_internal;
+ bool is_aspect_preserving_scaling;
+
+ // XInput device ID or 0 if this output isn't a touchscreen.
+ int touch_device_id;
+ };
+
+ struct CoordinateTransformation {
+ // Initialized to the identity transformation.
+ CoordinateTransformation();
+
+ float x_scale;
+ float x_offset;
+ float y_scale;
+ float y_offset;
+ };
+
+ struct CrtcConfig {
+ CrtcConfig();
+ CrtcConfig(RRCrtc crtc, int x, int y, RRMode mode, RROutput output);
+
+ RRCrtc crtc;
+ int x;
+ int y;
+ RRMode mode;
+ RROutput output;
+ };
+
class Observer {
public:
+ virtual ~Observer() {}
+
// Called when the change of the display mode finished. It will usually
// start the fading in the displays.
virtual void OnDisplayModeChanged() {}
@@ -71,13 +106,79 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
virtual void OnDisplayModeChangeFailed(OutputState failed_new_state) {}
};
- class Delegate {
+ // Interface for classes that make decisions about which output state
+ // should be used.
+ class StateController {
public:
+ virtual ~StateController() {}
+
// Called when displays are detected.
virtual OutputState GetStateForOutputs(
const std::vector<OutputInfo>& outputs) const = 0;
};
+ // Interface for classes that perform actions on behalf of OutputController.
+ class Delegate {
+ public:
+ virtual ~Delegate() {}
+
+ virtual void SetPanelFittingEnabled(bool enabled) = 0;
+
+ // Initializes the XRandR extension, saving the base event ID to
+ // |event_base|.
+ virtual void InitXRandRExtension(int* event_base) = 0;
+
+ // Tells XRandR to update its configuration in response to |event|, an
+ // RRScreenChangeNotify event.
+ virtual void UpdateXRandRConfiguration(const base::NativeEvent& event) = 0;
+
+ // Grabs the X server and refreshes XRandR-related resources. While
+ // the server is grabbed, other clients are blocked. Must be balanced
+ // by a call to UngrabServer().
+ virtual void GrabServer() = 0;
+
+ // Ungrabs the server and frees XRandR-related resources.
+ virtual void UngrabServer() = 0;
+
+ // Flushes all pending requests and waits for replies.
+ virtual void SyncWithServer() = 0;
+
+ // Sets the window's background color to |color_argb|.
+ virtual void SetBackgroundColor(uint32 color_argb) = 0;
+
+ // Enables DPMS and forces it to the "on" state.
+ virtual void ForceDPMSOn() = 0;
+
+ // Returns information about the current outputs.
+ virtual std::vector<OutputSnapshot> GetOutputs() = 0;
+
+ // Gets details corresponding to |mode|. Parameters may be NULL.
+ // Returns true on success.
+ virtual bool GetModeDetails(RRMode mode,
+ int* width,
+ int* height,
+ bool* interlaced) = 0;
+
+ // Calls XRRSetCrtcConfig() with the given options but some of our
+ // default output count and rotation arguments.
+ virtual void ConfigureCrtc(CrtcConfig* config) = 0;
+
+ // Called to set the frame buffer (underlying XRR "screen") size. Has
+ // a side-effect of disabling all CRTCs.
+ virtual void CreateFrameBuffer(int width,
+ int height,
+ CrtcConfig* config1,
+ CrtcConfig* config2) = 0;
+
+ // Configures XInput's Coordinate Transformation Matrix property.
+ // |touch_device_id| the ID of the touchscreen device to configure.
+ // |ctm| contains the desired transformation parameters. The offsets
+ // in it should be normalized so that 1 corresponds to the X or Y axis
+ // size for the corresponding offset.
+ virtual void ConfigureCTM(int touch_device_id,
+ const CoordinateTransformation& ctm) = 0;
+ };
+
// Flags that can be passed to SetDisplayPower().
static const int kSetDisplayPowerNoFlags = 0;
// Configure displays even if the passed-in state matches |power_state_|.
@@ -86,6 +187,9 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
// only connected display is external.
static const int kSetDisplayPowerOnlyIfSingleInternalDisplay = 1 << 1;
+ // Returns true if an output named |name| is an internal display.
+ static bool IsInternalOutputName(const std::string& name);
+
OutputConfigurator();
virtual ~OutputConfigurator();
@@ -98,7 +202,9 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
}
DisplayPowerState display_power_state() const { return power_state_; }
- void set_delegate(Delegate* delegate) { delegate_ = delegate; }
+ void set_state_controller(StateController* controller) {
+ state_controller_ = controller;
+ }
// Initialization, must be called right after constructor.
// |is_panel_fitting_enabled| indicates hardware panel fitting support.
@@ -133,9 +239,6 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
- // Tells if the output specified by |name| is for internal display.
- static bool IsInternalOutputName(const std::string& name);
-
// Sets all the displays into pre-suspend mode; usually this means
// configure them for their resume state. This allows faster resume on
// machines where display configuration is slow.
@@ -152,72 +255,23 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
// Fires OnDisplayModeChanged() event to the observers.
void NotifyOnDisplayChanged();
- // Returns a vector filled with properties of the first two connected outputs
- // found on |display| and |screen|.
- std::vector<OutputSnapshot> GetDualOutputs(Display* display,
- XRRScreenResources* screen);
-
- // Looks for a mode on internal and external outputs having same resolution.
- // |display| and |screen| parameters are needed for some XRandR calls.
- // |internal_info| and |external_info| are used to search for the modes.
- // |internal_output_id| is used to create a new mode, if applicable.
- // |try_creating|=true will enable creating panel-fitting mode
- // on the |internal_info| output instead of
- // only searching for a matching mode. Note: it may lead to a crash,
- // if |internal_info| is not capable of panel fitting.
- // |preserve_aspect|=true will limit the search / creation
- // only to the modes having the native aspect ratio of |external_info|.
- // |internal_mirror_mode| and |external_mirror_mode| are the out-parameters
- // for the modes on the two outputs which will have the same resolution.
- // Returns false if no mode appropriate for mirroring has been found/created.
- bool FindOrCreateMirrorMode(Display* display,
- XRRScreenResources* screen,
- XRROutputInfo* internal_info,
- XRROutputInfo* external_info,
- RROutput internal_output_id,
- bool try_creating,
- bool preserve_aspect,
- RRMode* internal_mirror_mode,
- RRMode* external_mirror_mode);
-
- // Searches for touchscreens among input devices,
- // and tries to match them up to screens in |outputs|.
- // |display| and |screen| are used to make X calls.
- // |outputs| is an array of detected screens.
- // If a touchscreen with same resolution as an output's native mode
- // is detected, its id will be stored in this output.
- void GetTouchscreens(Display* display,
- XRRScreenResources* screen,
- std::vector<OutputSnapshot>& outputs);
-
// Configures X to the state specified in |output_state| and
- // |power_state|. |display|, |screen| and |window| are used to change X
- // configuration. |outputs| contains information on the currently
+ // |power_state|. |outputs| contains information on the currently
// configured state, as well as how to apply the new state.
- bool EnterState(Display* display,
- XRRScreenResources* screen,
- Window window,
- OutputState output_state,
+ bool EnterState(OutputState output_state,
DisplayPowerState power_state,
const std::vector<OutputSnapshot>& outputs);
- // Outputs UMA metrics of previous state (the state that is being left).
- // Updates |mirror_mode_preserved_aspect_| and |last_enter_state_time_|.
- void RecordPreviousStateUMA();
-
// Returns next state.
- OutputState GetNextState(Display* display,
- XRRScreenResources* screen,
- OutputState current_state,
- const std::vector<OutputSnapshot>& outputs) const;
-
-
- // Tells if the output specified by |output_info| is for internal display.
- static bool IsInternalOutput(const XRROutputInfo* output_info);
+ OutputState GetNextState(const std::vector<OutputSnapshot>& outputs) const;
- // Returns output's native mode, None if not found.
- static RRMode GetOutputNativeMode(const XRROutputInfo* output_info);
+ // Computes the relevant transformation for mirror mode.
+ // |output| is the output on which mirror mode is being applied.
+ // Returns the transformation, which would be identity if computations fail.
+ CoordinateTransformation GetMirrorModeCTM(
+ const OutputConfigurator::OutputSnapshot* output);
+ StateController* state_controller_;
Delegate* delegate_;
// This is detected by the constructor to determine whether or not we should
@@ -227,10 +281,6 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
// configuration to immediately fail without changing the state.
bool configure_display_;
- // This is set externally in Init,
- // and is used to enable modes which rely on panel fitting.
- bool is_panel_fitting_enabled_;
-
// The number of outputs that are connected.
int connected_output_count_;
@@ -251,16 +301,6 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
// |Dispatch()|.
scoped_ptr<base::OneShotTimer<OutputConfigurator> > configure_timer_;
- // Next 3 members are used for UMA of time spent in various states.
- // Indicates that current OutputSnapshot has aspect preserving mirror mode.
- bool mirror_mode_will_preserve_aspect_;
-
- // Indicates that last entered mirror mode preserved aspect.
- bool mirror_mode_preserved_aspect_;
-
- // Indicates the time at which |output_state_| was entered.
- base::TimeTicks last_enter_state_time_;
-
DISALLOW_COPY_AND_ASSIGN(OutputConfigurator);
};
diff --git a/chromeos/display/real_output_configurator_delegate.cc b/chromeos/display/real_output_configurator_delegate.cc
new file mode 100644
index 0000000..46df404
--- /dev/null
+++ b/chromeos/display/real_output_configurator_delegate.cc
@@ -0,0 +1,567 @@
+// Copyright (c) 2013 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/real_output_configurator_delegate.h"
+
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+#include <X11/extensions/dpms.h>
+#include <X11/extensions/XInput.h>
+#include <X11/extensions/XInput2.h>
+#include <X11/extensions/Xrandr.h>
+
+#include <cmath>
+
+#include "base/logging.h"
+#include "base/message_pump_aurax11.h"
+
+namespace chromeos {
+
+namespace {
+
+// DPI measurements.
+const float kMmInInch = 25.4;
+const float kDpi96 = 96.0;
+const float kPixelsToMmScale = kMmInInch / kDpi96;
+
+bool IsInternalOutput(const XRROutputInfo* output_info) {
+ return OutputConfigurator::IsInternalOutputName(
+ std::string(output_info->name));
+}
+
+RRMode GetOutputNativeMode(const XRROutputInfo* output_info) {
+ return output_info->nmode > 0 ? output_info->modes[0] : None;
+}
+
+} // namespace
+
+RealOutputConfiguratorDelegate::RealOutputConfiguratorDelegate()
+ : display_(base::MessagePumpAuraX11::GetDefaultXDisplay()),
+ window_(DefaultRootWindow(display_)),
+ screen_(NULL),
+ is_panel_fitting_enabled_(false) {
+}
+
+RealOutputConfiguratorDelegate::~RealOutputConfiguratorDelegate() {
+}
+
+void RealOutputConfiguratorDelegate::SetPanelFittingEnabled(bool enabled) {
+ is_panel_fitting_enabled_ = enabled;
+}
+
+void RealOutputConfiguratorDelegate::InitXRandRExtension(int* event_base) {
+ int error_base_ignored = 0;
+ XRRQueryExtension(display_, event_base, &error_base_ignored);
+}
+
+void RealOutputConfiguratorDelegate::UpdateXRandRConfiguration(
+ const base::NativeEvent& event) {
+ XRRUpdateConfiguration(event);
+}
+
+void RealOutputConfiguratorDelegate::GrabServer() {
+ CHECK(!screen_) << "Server already grabbed";
+ XGrabServer(display_);
+ screen_ = XRRGetScreenResources(display_, window_);
+ CHECK(screen_);
+}
+
+void RealOutputConfiguratorDelegate::UngrabServer() {
+ CHECK(screen_) << "Server not grabbed";
+ XRRFreeScreenResources(screen_);
+ screen_ = NULL;
+ XUngrabServer(display_);
+}
+
+void RealOutputConfiguratorDelegate::SyncWithServer() {
+ XSync(display_, 0);
+}
+
+void RealOutputConfiguratorDelegate::SetBackgroundColor(uint32 color_argb) {
+ // Configuring CRTCs/Framebuffer clears the boot screen image. Set the
+ // same background color while configuring the display to minimize the
+ // duration of black screen at boot time. The background is filled with
+ // black later in ash::DisplayManager. crbug.com/171050.
+ XSetWindowAttributes swa = {0};
+ XColor color;
+ Colormap colormap = DefaultColormap(display_, 0);
+ // XColor uses 16 bits per color.
+ color.red = (color_argb & 0x00FF0000) >> 8;
+ color.green = (color_argb & 0x0000FF00);
+ color.blue = (color_argb & 0x000000FF) << 8;
+ color.flags = DoRed | DoGreen | DoBlue;
+ XAllocColor(display_, colormap, &color);
+ swa.background_pixel = color.pixel;
+ XChangeWindowAttributes(display_, window_, CWBackPixel, &swa);
+ XFreeColors(display_, colormap, &color.pixel, 1, 0);
+}
+
+void RealOutputConfiguratorDelegate::ForceDPMSOn() {
+ CHECK(DPMSEnable(display_));
+ CHECK(DPMSForceLevel(display_, DPMSModeOn));
+}
+
+std::vector<OutputConfigurator::OutputSnapshot>
+RealOutputConfiguratorDelegate::GetOutputs() {
+ CHECK(screen_) << "Server not grabbed";
+
+ std::vector<OutputConfigurator::OutputSnapshot> outputs;
+ XRROutputInfo* one_info = NULL;
+ XRROutputInfo* two_info = NULL;
+ RRCrtc last_used_crtc = None;
+
+ for (int i = 0; i < screen_->noutput && outputs.size() < 2; ++i) {
+ RROutput this_id = screen_->outputs[i];
+ XRROutputInfo* output_info = XRRGetOutputInfo(display_, screen_, this_id);
+ bool is_connected = (output_info->connection == RR_Connected);
+
+ if (is_connected) {
+ OutputConfigurator::OutputSnapshot to_populate;
+ to_populate.output = this_id;
+ (outputs.empty() ? one_info : two_info) = output_info;
+
+ // Now, look up the current CRTC and any related info.
+ if (output_info->crtc) {
+ XRRCrtcInfo* crtc_info = XRRGetCrtcInfo(
+ display_, screen_, output_info->crtc);
+ to_populate.current_mode = crtc_info->mode;
+ to_populate.height = crtc_info->height;
+ to_populate.y = crtc_info->y;
+ XRRFreeCrtcInfo(crtc_info);
+ }
+
+ // Assign a CRTC that isn't already in use.
+ for (int j = 0; j < output_info->ncrtc; ++j) {
+ if (output_info->crtcs[j] != last_used_crtc) {
+ to_populate.crtc = output_info->crtcs[j];
+ last_used_crtc = to_populate.crtc;
+ break;
+ }
+ }
+
+ to_populate.native_mode = GetOutputNativeMode(output_info);
+ to_populate.is_internal = IsInternalOutput(output_info);
+ to_populate.is_aspect_preserving_scaling =
+ IsOutputAspectPreservingScaling(this_id);
+ to_populate.touch_device_id = None;
+
+ VLOG(1) << "Found display #" << outputs.size()
+ << " with output " << to_populate.output
+ << " crtc " << to_populate.crtc
+ << " current mode " << to_populate.current_mode;
+ outputs.push_back(to_populate);
+ } else {
+ XRRFreeOutputInfo(output_info);
+ }
+ }
+
+ if (outputs.size() == 2) {
+ bool one_is_internal = IsInternalOutput(one_info);
+ bool two_is_internal = IsInternalOutput(two_info);
+ int internal_outputs = (one_is_internal ? 1 : 0) +
+ (two_is_internal ? 1 : 0);
+ DCHECK_LT(internal_outputs, 2);
+ LOG_IF(WARNING, internal_outputs == 2)
+ << "Two internal outputs detected.";
+
+ bool can_mirror = false;
+ for (int attempt = 0; !can_mirror && attempt < 2; ++attempt) {
+ // Try preserving external output's aspect ratio on the first attempt.
+ // If that fails, fall back to the highest matching resolution.
+ bool preserve_aspect = attempt == 0;
+
+ if (internal_outputs == 1) {
+ if (one_is_internal) {
+ can_mirror = FindOrCreateMirrorMode(one_info, two_info,
+ outputs[0].output, is_panel_fitting_enabled_, preserve_aspect,
+ &outputs[0].mirror_mode, &outputs[1].mirror_mode);
+ } else { // if (two_is_internal)
+ can_mirror = FindOrCreateMirrorMode(two_info, one_info,
+ outputs[1].output, is_panel_fitting_enabled_, preserve_aspect,
+ &outputs[1].mirror_mode, &outputs[0].mirror_mode);
+ }
+ } else { // if (internal_outputs == 0)
+ // No panel fitting for external outputs, so fall back to exact match.
+ can_mirror = FindOrCreateMirrorMode(one_info, two_info,
+ outputs[0].output, false, preserve_aspect,
+ &outputs[0].mirror_mode, &outputs[1].mirror_mode);
+ if (!can_mirror && preserve_aspect) {
+ // FindOrCreateMirrorMode will try to preserve aspect ratio of
+ // what it thinks is external display, so if it didn't succeed
+ // with one, maybe it will succeed with the other. This way we
+ // will have correct aspect ratio on at least one of them.
+ can_mirror = FindOrCreateMirrorMode(two_info, one_info,
+ outputs[1].output, false, preserve_aspect,
+ &outputs[1].mirror_mode, &outputs[0].mirror_mode);
+ }
+ }
+ }
+ }
+
+ GetTouchscreens(&outputs);
+ XRRFreeOutputInfo(one_info);
+ XRRFreeOutputInfo(two_info);
+ return outputs;
+}
+
+bool RealOutputConfiguratorDelegate::GetModeDetails(RRMode mode,
+ int* width,
+ int* height,
+ bool* interlaced) {
+ CHECK(screen_) << "Server not grabbed";
+ // 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.
+ for (int i = 0; i < screen_->nmode; ++i) {
+ if (mode == screen_->modes[i].id) {
+ const XRRModeInfo& info = screen_->modes[i];
+ if (width)
+ *width = info.width;
+ if (height)
+ *height = info.height;
+ if (interlaced)
+ *interlaced = info.modeFlags & RR_Interlace;
+ return true;
+ }
+ }
+ return false;
+}
+
+void RealOutputConfiguratorDelegate::ConfigureCrtc(
+ OutputConfigurator::CrtcConfig* config) {
+ VLOG(1) << "ConfigureCrtc crtc: " << config->crtc
+ << ", mode " << config->mode
+ << ", output " << config->output
+ << ", x " << config->x
+ << ", y " << config->y;
+ CHECK(screen_) << "Server not grabbed";
+
+ RROutput* outputs = NULL;
+ int num_outputs = 0;
+ if (config->output && config->mode) {
+ outputs = &config->output;
+ num_outputs = 1;
+ }
+
+ XRRSetCrtcConfig(display_,
+ screen_,
+ config->crtc,
+ CurrentTime,
+ config->x,
+ config->y,
+ config->mode,
+ RR_Rotate_0,
+ outputs,
+ num_outputs);
+}
+
+void RealOutputConfiguratorDelegate::CreateFrameBuffer(
+ int width,
+ int height,
+ OutputConfigurator::CrtcConfig* config1,
+ OutputConfigurator::CrtcConfig* config2) {
+ CHECK(screen_) << "Server not grabbed";
+ int current_width = DisplayWidth(display_, DefaultScreen(display_));
+ int current_height = DisplayHeight(display_, DefaultScreen(display_));
+ VLOG(1) << "CreateFrameBuffer " << width << " x " << height
+ << " (current=" << current_width << " x " << current_height << ")";
+ if (width == current_width && height == current_height)
+ return;
+
+ DestroyUnusedCrtcs(config1, config2);
+ int mm_width = width * kPixelsToMmScale;
+ int mm_height = height * kPixelsToMmScale;
+ XRRSetScreenSize(display_, window_, width, height, mm_width, mm_height);
+}
+
+void RealOutputConfiguratorDelegate::ConfigureCTM(
+ int touch_device_id,
+ const OutputConfigurator::CoordinateTransformation& ctm) {
+ int ndevices;
+ XIDeviceInfo* info = XIQueryDevice(display_, touch_device_id, &ndevices);
+ Atom prop = XInternAtom(
+ display_, "Coordinate Transformation Matrix", False);
+ Atom float_atom = XInternAtom(display_, "FLOAT", False);
+ if (ndevices == 1 && prop != None && float_atom != None) {
+ Atom type;
+ int format;
+ unsigned long num_items;
+ unsigned long bytes_after;
+ unsigned char* data = NULL;
+ // Verify that the property exists with correct format, type, etc.
+ int status = XIGetProperty(display_, info->deviceid, prop, 0, 0, False,
+ AnyPropertyType, &type, &format, &num_items, &bytes_after, &data);
+ if (data)
+ XFree(data);
+ if (status == Success && type == float_atom && format == 32) {
+ float value[3][3] = {
+ { ctm.x_scale, 0.0, ctm.x_offset },
+ { 0.0, ctm.y_scale, ctm.y_offset },
+ { 0.0, 0.0, 1.0 }
+ };
+ XIChangeProperty(display_,
+ info->deviceid,
+ prop,
+ type,
+ format,
+ PropModeReplace,
+ reinterpret_cast<unsigned char*>(value),
+ 9);
+ }
+ }
+ XIFreeDeviceInfo(info);
+}
+
+void RealOutputConfiguratorDelegate::DestroyUnusedCrtcs(
+ OutputConfigurator::CrtcConfig* config1,
+ OutputConfigurator::CrtcConfig* config2) {
+ CHECK(screen_) << "Server not grabbed";
+ // Setting the screen size will fail if any CRTC doesn't fit afterwards.
+ // At the same time, turning CRTCs off and back on uses up a lot of time.
+ // This function tries to be smart to avoid too many off/on cycles:
+ // - We disable all the CRTCs we won't need after the FB resize.
+ // - We set the new modes on CRTCs, if they fit in both the old and new
+ // FBs, and park them at (0,0)
+ // - We disable the CRTCs we will need but don't fit in the old FB. Those
+ // will be reenabled after the resize.
+ // We don't worry about the cached state of the outputs here since we are
+ // not interested in the state we are setting - we just try to get the CRTCs
+ // out of the way so we can rebuild the frame buffer.
+ for (int i = 0; i < screen_->ncrtc; ++i) {
+ // Default config is to disable the crtcs.
+ OutputConfigurator::CrtcConfig config(
+ screen_->crtcs[i], 0, 0, None, None);
+
+ // If we are going to use that CRTC later, prepare it now.
+ if (config1 && screen_->crtcs[i] == config1->crtc) {
+ config = *config1;
+ config.x = 0;
+ config.y = 0;
+ } else if (config2 && screen_->crtcs[i] == config2->crtc) {
+ config = *config2;
+ config.x = 0;
+ config.y = 0;
+ }
+
+ if (config.mode != None) {
+ // In case our CRTC doesn't fit in our current framebuffer, disable it.
+ // It'll get reenabled after we resize the framebuffer.
+ int mode_width = 0, mode_height = 0;
+ CHECK(GetModeDetails(config.mode, &mode_width, &mode_height, NULL));
+ int current_width = DisplayWidth(display_, DefaultScreen(display_));
+ int current_height = DisplayHeight(display_, DefaultScreen(display_));
+ if (mode_width > current_width || mode_height > current_height) {
+ config.mode = None;
+ config.output = None;
+ }
+ }
+
+ ConfigureCrtc(&config);
+ }
+}
+
+bool RealOutputConfiguratorDelegate::IsOutputAspectPreservingScaling(
+ RROutput id) {
+ bool ret = false;
+
+ Atom scaling_prop = XInternAtom(display_, "scaling mode", False);
+ Atom full_aspect_atom = XInternAtom(display_, "Full aspect", False);
+ if (scaling_prop == None || full_aspect_atom == None)
+ return false;
+
+ int nprop = 0;
+ Atom* props = XRRListOutputProperties(display_, id, &nprop);
+ for (int j = 0; j < nprop && !ret; j++) {
+ Atom prop = props[j];
+ if (scaling_prop == prop) {
+ unsigned char* values = NULL;
+ int actual_format;
+ unsigned long nitems;
+ unsigned long bytes_after;
+ Atom actual_type;
+ int success;
+
+ success = XRRGetOutputProperty(display_, id, prop, 0, 100, False, False,
+ AnyPropertyType, &actual_type, &actual_format, &nitems,
+ &bytes_after, &values);
+ if (success == Success && actual_type == XA_ATOM &&
+ actual_format == 32 && nitems == 1) {
+ Atom value = reinterpret_cast<Atom*>(values)[0];
+ if (full_aspect_atom == value)
+ ret = true;
+ }
+ if (values)
+ XFree(values);
+ }
+ }
+ if (props)
+ XFree(props);
+
+ return ret;
+}
+
+bool RealOutputConfiguratorDelegate::FindOrCreateMirrorMode(
+ XRROutputInfo* internal_info,
+ XRROutputInfo* external_info,
+ RROutput internal_output_id,
+ bool try_creating,
+ bool preserve_aspect,
+ RRMode* internal_mirror_mode,
+ RRMode* external_mirror_mode) {
+ RRMode internal_mode_id = GetOutputNativeMode(internal_info);
+ RRMode external_mode_id = GetOutputNativeMode(external_info);
+
+ if (internal_mode_id == None || external_mode_id == None)
+ return false;
+
+ int internal_native_width = 0, internal_native_height = 0;
+ int external_native_width = 0, external_native_height = 0;
+ CHECK(GetModeDetails(internal_mode_id, &internal_native_width,
+ &internal_native_height, NULL));
+ CHECK(GetModeDetails(external_mode_id, &external_native_width,
+ &external_native_height, NULL));
+
+ // Check if some external output resolution can be mirrored on internal.
+ // Prefer the modes in the order that X sorts them,
+ // assuming this is the order in which they look better on the monitor.
+ // If X's order is not satisfactory, we can either fix X's sorting,
+ // or implement our sorting here.
+ for (int i = 0; i < external_info->nmode; i++) {
+ external_mode_id = external_info->modes[i];
+ int external_width = 0, external_height = 0;
+ bool is_external_interlaced = false;
+ CHECK(GetModeDetails(external_mode_id, &external_width, &external_height,
+ &is_external_interlaced));
+ bool is_native_aspect_ratio =
+ external_native_width * external_height ==
+ external_native_height * external_width;
+ if (preserve_aspect && !is_native_aspect_ratio)
+ continue; // Allow only aspect ratio preserving modes for mirroring
+
+ // Try finding exact match
+ for (int j = 0; j < internal_info->nmode; j++) {
+ internal_mode_id = internal_info->modes[j];
+ int internal_width = 0, internal_height = 0;
+ bool is_internal_interlaced = false;
+ CHECK(GetModeDetails(internal_mode_id, &internal_width,
+ &internal_height, &is_internal_interlaced));
+ if (internal_width == external_width &&
+ internal_height == external_height &&
+ is_internal_interlaced == is_external_interlaced) {
+ *internal_mirror_mode = internal_mode_id;
+ *external_mirror_mode = external_mode_id;
+ return true; // Mirror mode found
+ }
+ }
+
+ // Try to create a matching internal output mode by panel fitting
+ if (try_creating) {
+ // We can downscale by 1.125, and upscale indefinitely
+ // Downscaling looks ugly, so, can fit == can upscale
+ // Also, internal panels don't support fitting interlaced modes
+ bool can_fit =
+ internal_native_width >= external_width &&
+ internal_native_height >= external_height &&
+ !is_external_interlaced;
+ if (can_fit) {
+ XRRAddOutputMode(display_, internal_output_id, external_mode_id);
+ *internal_mirror_mode = *external_mirror_mode = external_mode_id;
+ return true; // Mirror mode created
+ }
+ }
+ }
+
+ return false;
+}
+
+void RealOutputConfiguratorDelegate::GetTouchscreens(
+ std::vector<OutputConfigurator::OutputSnapshot>* outputs) {
+ int ndevices = 0;
+ Atom valuator_x = XInternAtom(display_, "Abs MT Position X", False);
+ Atom valuator_y = XInternAtom(display_, "Abs MT Position Y", False);
+ if (valuator_x == None || valuator_y == None)
+ return;
+
+ XIDeviceInfo* info = XIQueryDevice(display_, XIAllDevices, &ndevices);
+ for (int i = 0; i < ndevices; i++) {
+ if (!info[i].enabled || info[i].use != XIFloatingSlave)
+ continue; // Assume all touchscreens are floating slaves
+
+ double width = -1.0;
+ double height = -1.0;
+ bool is_direct_touch = false;
+
+ for (int j = 0; j < info[i].num_classes; j++) {
+ XIAnyClassInfo* class_info = info[i].classes[j];
+
+ if (class_info->type == XIValuatorClass) {
+ XIValuatorClassInfo* valuator_info =
+ reinterpret_cast<XIValuatorClassInfo*>(class_info);
+
+ if (valuator_x == valuator_info->label) {
+ // Ignore X axis valuator with unexpected properties
+ if (valuator_info->number == 0 && valuator_info->mode == Absolute &&
+ valuator_info->min == 0.0) {
+ width = valuator_info->max;
+ }
+ } else if (valuator_y == valuator_info->label) {
+ // Ignore Y axis valuator with unexpected properties
+ if (valuator_info->number == 1 && valuator_info->mode == Absolute &&
+ valuator_info->min == 0.0) {
+ height = valuator_info->max;
+ }
+ }
+ }
+#if defined(USE_XI2_MT)
+ if (class_info->type == XITouchClass) {
+ XITouchClassInfo* touch_info =
+ reinterpret_cast<XITouchClassInfo*>(class_info);
+ is_direct_touch = touch_info->mode == XIDirectTouch;
+ }
+#endif
+ }
+
+ // Touchscreens should have absolute X and Y axes,
+ // and be direct touch devices.
+ if (width > 0.0 && height > 0.0 && is_direct_touch) {
+ size_t k = 0;
+ for (; k < outputs->size(); k++) {
+ if ((*outputs)[k].native_mode == None ||
+ (*outputs)[k].touch_device_id != None)
+ continue;
+ int native_mode_width = 0, native_mode_height = 0;
+ if (!GetModeDetails((*outputs)[k].native_mode, &native_mode_width,
+ &native_mode_height, NULL))
+ continue;
+
+ // Allow 1 pixel difference between screen and touchscreen
+ // resolutions. Because in some cases for monitor resolution
+ // 1024x768 touchscreen's resolution would be 1024x768, but for
+ // some 1023x767. It really depends on touchscreen's firmware
+ // configuration.
+ if (std::abs(native_mode_width - width) <= 1.0 &&
+ std::abs(native_mode_height - height) <= 1.0) {
+ (*outputs)[k].touch_device_id = info[i].deviceid;
+
+ VLOG(1) << "Found touchscreen for output #" << k
+ << " id " << (*outputs)[k].touch_device_id
+ << " width " << width
+ << " height " << height;
+ break;
+ }
+ }
+
+ VLOG_IF(1, k == outputs->size())
+ << "No matching output - ignoring touchscreen"
+ << " id " << info[i].deviceid
+ << " width " << width
+ << " height " << height;
+ }
+ }
+
+ XIFreeDeviceInfo(info);
+}
+
+} // namespace chromeos
diff --git a/chromeos/display/real_output_configurator_delegate.h b/chromeos/display/real_output_configurator_delegate.h
new file mode 100644
index 0000000..8ad68c8
--- /dev/null
+++ b/chromeos/display/real_output_configurator_delegate.h
@@ -0,0 +1,108 @@
+// Copyright (c) 2013 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_REAL_OUTPUT_CONFIGURATOR_DELEGATE_H_
+#define CHROMEOS_DISPLAY_REAL_OUTPUT_CONFIGURATOR_DELEGATE_H_
+
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "chromeos/display/output_configurator.h"
+
+typedef XID Window;
+
+struct _XDisplay;
+typedef struct _XDisplay Display;
+struct _XRROutputInfo;
+typedef _XRROutputInfo XRROutputInfo;
+struct _XRRScreenResources;
+typedef _XRRScreenResources XRRScreenResources;
+
+namespace chromeos {
+
+class RealOutputConfiguratorDelegate : public OutputConfigurator::Delegate {
+ public:
+ RealOutputConfiguratorDelegate();
+ virtual ~RealOutputConfiguratorDelegate();
+
+ // OutputConfigurator::Delegate overrides:
+ virtual void SetPanelFittingEnabled(bool enabled) OVERRIDE;
+ virtual void InitXRandRExtension(int* event_base) OVERRIDE;
+ virtual void UpdateXRandRConfiguration(
+ const base::NativeEvent& event) OVERRIDE;
+ virtual void GrabServer() OVERRIDE;
+ virtual void UngrabServer() OVERRIDE;
+ virtual void SyncWithServer() OVERRIDE;
+ virtual void SetBackgroundColor(uint32 color_argb) OVERRIDE;
+ virtual void ForceDPMSOn() OVERRIDE;
+ virtual std::vector<OutputConfigurator::OutputSnapshot> GetOutputs() OVERRIDE;
+ virtual bool GetModeDetails(
+ RRMode mode,
+ int* width,
+ int* height,
+ bool* interlaced) OVERRIDE;
+ virtual void ConfigureCrtc(OutputConfigurator::CrtcConfig* config) OVERRIDE;
+ virtual void CreateFrameBuffer(
+ int width,
+ int height,
+ OutputConfigurator::CrtcConfig* config1,
+ OutputConfigurator::CrtcConfig* config2) OVERRIDE;
+ virtual void ConfigureCTM(
+ int touch_device_id,
+ const OutputConfigurator::CoordinateTransformation& ctm) OVERRIDE;
+
+ private:
+ // Destroys unused CRTCs and parks used CRTCs in a way which allows a
+ // framebuffer resize. This is faster than turning them off, resizing,
+ // then turning them back on.
+ void DestroyUnusedCrtcs(OutputConfigurator::CrtcConfig* config1,
+ OutputConfigurator::CrtcConfig* config2);
+
+ // Returns whether |id| is configured to preserve aspect when scaling.
+ bool IsOutputAspectPreservingScaling(RROutput id);
+
+ // Looks for a mode on internal and external outputs having same
+ // resolution. |internal_info| and |external_info| are used to search
+ // for the modes. |internal_output_id| is used to create a new mode, if
+ // applicable. |try_creating|=true will enable creating panel-fitting
+ // mode on the |internal_info| output instead of only searching for a
+ // matching mode. Note: it may lead to a crash, if |internal_info| is
+ // not capable of panel fitting. |preserve_aspect|=true will limit the
+ // search / creation only to the modes having the native aspect ratio of
+ // |external_info|. |internal_mirror_mode| and |external_mirror_mode|
+ // are the out-parameters for the modes on the two outputs which will
+ // have the same resolution. Returns false if no mode appropriate for
+ // mirroring has been found/created.
+ bool FindOrCreateMirrorMode(XRROutputInfo* internal_info,
+ XRROutputInfo* external_info,
+ RROutput internal_output_id,
+ bool try_creating,
+ bool preserve_aspect,
+ RRMode* internal_mirror_mode,
+ RRMode* external_mirror_mode);
+
+ // Searches for touchscreens among input devices,
+ // and tries to match them up to screens in |outputs|.
+ // |outputs| is an array of detected screens.
+ // If a touchscreen with same resolution as an output's native mode
+ // is detected, its id will be stored in this output.
+ void GetTouchscreens(
+ std::vector<OutputConfigurator::OutputSnapshot>* outputs);
+
+ Display* display_;
+ Window window_;
+
+ // Initialized when the server is grabbed and freed when it's ungrabbed.
+ XRRScreenResources* screen_;
+
+ // Used to enable modes which rely on panel fitting.
+ bool is_panel_fitting_enabled_;
+
+ DISALLOW_COPY_AND_ASSIGN(RealOutputConfiguratorDelegate);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_DISPLAY_REAL_OUTPUT_CONFIGURATOR_DELEGATE_H_