diff options
author | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-05 02:16:35 +0000 |
---|---|---|
committer | derat@chromium.org <derat@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-04-05 02:16:35 +0000 |
commit | e98603298834ec12798d02f4e5b77c377eb830c7 (patch) | |
tree | 76cb402e3f4421e75954292f617638eefed36e6e /chromeos/display | |
parent | 9123168625a008a23f8c39d717bbf2a2ed7604d5 (diff) | |
download | chromium_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.cc | 1080 | ||||
-rw-r--r-- | chromeos/display/output_configurator.h | 220 | ||||
-rw-r--r-- | chromeos/display/real_output_configurator_delegate.cc | 567 | ||||
-rw-r--r-- | chromeos/display/real_output_configurator_delegate.h | 108 |
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_ |