summaryrefslogtreecommitdiffstats
path: root/chromeos/display
diff options
context:
space:
mode:
authoroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-16 06:24:48 +0000
committeroshima@chromium.org <oshima@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2013-05-16 06:24:48 +0000
commit2c8a0aa7ec6ea9bf947e32089a3308ddbbb9a10f (patch)
treefa0306f677af7a193913ca239e15ba3835500717 /chromeos/display
parent9024c66a1de0a5dba9a76c57e9e8020304c31995 (diff)
downloadchromium_src-2c8a0aa7ec6ea9bf947e32089a3308ddbbb9a10f.zip
chromium_src-2c8a0aa7ec6ea9bf947e32089a3308ddbbb9a10f.tar.gz
chromium_src-2c8a0aa7ec6ea9bf947e32089a3308ddbbb9a10f.tar.bz2
Move chromeos specific utility functions for display to chromeos/display
BUG=240168 Review URL: https://chromiumcodereview.appspot.com/15067012 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@200466 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos/display')
-rw-r--r--chromeos/display/output_configurator.cc22
-rw-r--r--chromeos/display/output_configurator.h15
-rw-r--r--chromeos/display/output_configurator_unittest.cc36
-rw-r--r--chromeos/display/output_util.cc319
-rw-r--r--chromeos/display/output_util.h56
-rw-r--r--chromeos/display/output_util_unittest.cc208
-rw-r--r--chromeos/display/real_output_configurator_delegate.cc7
7 files changed, 626 insertions, 37 deletions
diff --git a/chromeos/display/output_configurator.cc b/chromeos/display/output_configurator.cc
index cca82e7..fab354e 100644
--- a/chromeos/display/output_configurator.cc
+++ b/chromeos/display/output_configurator.cc
@@ -12,16 +12,13 @@
#include "base/logging.h"
#include "base/strings/string_number_conversions.h"
#include "base/time.h"
+#include "chromeos/display/output_util.h"
#include "chromeos/display/real_output_configurator_delegate.h"
namespace chromeos {
namespace {
-// Prefixes for the built-in displays.
-const char kInternal_LVDS[] = "LVDS";
-const char kInternal_eDP[] = "eDP";
-
// The delay to perform configuration after RRNotify. See the comment
// in |Dispatch()|.
const int64 kConfigureDelayMs = 500;
@@ -93,7 +90,8 @@ OutputConfigurator::OutputSnapshot::OutputSnapshot()
is_internal(false),
is_aspect_preserving_scaling(false),
touch_device_id(0),
- index(0) {}
+ display_id(0),
+ has_display_id(false) {}
OutputConfigurator::CoordinateTransformation::CoordinateTransformation()
: x_scale(1.0),
@@ -142,11 +140,6 @@ bool OutputConfigurator::TestApi::SendOutputChangeEvents(bool connected) {
return true;
}
-// static
-bool OutputConfigurator::IsInternalOutputName(const std::string& name) {
- return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0;
-}
-
OutputConfigurator::OutputConfigurator()
: state_controller_(NULL),
configure_display_(base::chromeos::IsRunningOnChromeOS()),
@@ -512,7 +505,14 @@ OutputState OutputConfigurator::GetOutputState(
} else {
// With either both outputs on or both outputs off, use one of the
// dual modes.
- return state_controller_->GetStateForOutputs(outputs);
+ std::vector<int64> display_ids;
+ for (size_t i = 0; i < outputs.size(); ++i) {
+ // If display id isn't available, switch to extended mode.
+ if (!outputs[i].has_display_id)
+ return STATE_DUAL_EXTENDED;
+ display_ids.push_back(outputs[i].display_id);
+ }
+ return state_controller_->GetStateForDisplayIds(display_ids);
}
}
default:
diff --git a/chromeos/display/output_configurator.h b/chromeos/display/output_configurator.h
index 43060ac..84de2d0 100644
--- a/chromeos/display/output_configurator.h
+++ b/chromeos/display/output_configurator.h
@@ -6,6 +6,7 @@
#define CHROMEOS_DISPLAY_OUTPUT_CONFIGURATOR_H_
#include <map>
+#include <string>
#include <vector>
#include "base/basictypes.h"
@@ -62,9 +63,10 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
// XInput device ID or 0 if this output isn't a touchscreen.
int touch_device_id;
- // TODO(oshima): Move xrandr related functions to here
- // from ui/base/x and replace this with display id.
- int index;
+ // Display id for this output.
+ int64 display_id;
+
+ bool has_display_id;
};
struct CoordinateTransformation {
@@ -108,8 +110,8 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
virtual ~StateController() {}
// Called when displays are detected.
- virtual OutputState GetStateForOutputs(
- const std::vector<OutputSnapshot>& outputs) const = 0;
+ virtual OutputState GetStateForDisplayIds(
+ const std::vector<int64>& display_ids) const = 0;
};
// Interface for classes that perform actions on behalf of OutputController.
@@ -216,9 +218,6 @@ class CHROMEOS_EXPORT OutputConfigurator : public MessageLoop::Dispatcher {
// See crbug.com/130188 for initial discussion.
static const int kVerticalGap = 60;
- // Returns true if an output named |name| is an internal display.
- static bool IsInternalOutputName(const std::string& name);
-
OutputConfigurator();
virtual ~OutputConfigurator();
diff --git a/chromeos/display/output_configurator_unittest.cc b/chromeos/display/output_configurator_unittest.cc
index 3d4fbfc..8f69ce7 100644
--- a/chromeos/display/output_configurator_unittest.cc
+++ b/chromeos/display/output_configurator_unittest.cc
@@ -208,8 +208,8 @@ class TestStateController : public OutputConfigurator::StateController {
void set_state(OutputState state) { state_ = state; }
// OutputConfigurator::StateController overrides:
- virtual OutputState GetStateForOutputs(
- const OutputSnapshotList& outputs) const OVERRIDE { return state_; }
+ virtual OutputState GetStateForDisplayIds(
+ const std::vector<int64>& outputs) const OVERRIDE { return state_; }
private:
OutputState state_;
@@ -240,6 +240,7 @@ class OutputConfiguratorTest : public testing::Test {
o->is_internal = true;
o->is_aspect_preserving_scaling = true;
o->touch_device_id = 0;
+ o->has_display_id = true;
o = &outputs_[1];
o->output = 2;
@@ -252,6 +253,7 @@ class OutputConfiguratorTest : public testing::Test {
o->is_internal = false;
o->is_aspect_preserving_scaling = true;
o->touch_device_id = 0;
+ o->has_display_id = true;
UpdateOutputs(2);
delegate_->AddMode(kSmallModeId, kSmallModeWidth, kSmallModeHeight, false);
@@ -309,19 +311,6 @@ class OutputConfiguratorTest : public testing::Test {
} // namespace
-TEST_F(OutputConfiguratorTest, IsInternalOutputName) {
- EXPECT_TRUE(OutputConfigurator::IsInternalOutputName("LVDS"));
- EXPECT_TRUE(OutputConfigurator::IsInternalOutputName("eDP"));
- EXPECT_TRUE(OutputConfigurator::IsInternalOutputName("LVDSxx"));
- EXPECT_TRUE(OutputConfigurator::IsInternalOutputName("eDPzz"));
-
- EXPECT_FALSE(OutputConfigurator::IsInternalOutputName("xyz"));
- EXPECT_FALSE(OutputConfigurator::IsInternalOutputName("abcLVDS"));
- EXPECT_FALSE(OutputConfigurator::IsInternalOutputName("cdeeDP"));
- EXPECT_FALSE(OutputConfigurator::IsInternalOutputName("LVD"));
- EXPECT_FALSE(OutputConfigurator::IsInternalOutputName("eD"));
-}
-
TEST_F(OutputConfiguratorTest, ConnectSecondOutput) {
InitWithSingleOutput();
@@ -594,4 +583,21 @@ TEST_F(OutputConfiguratorTest, InvalidOutputStates) {
EXPECT_TRUE(configurator_.SetDisplayMode(STATE_DUAL_EXTENDED));
}
+TEST_F(OutputConfiguratorTest, GetOutputStateForDisplays) {
+ outputs_[0].has_display_id = false;
+ UpdateOutputs(2);
+
+ configurator_.Init(false, 0);
+ configurator_.Start();
+
+ state_controller_.set_state(STATE_DUAL_MIRROR);
+ test_api_.SendOutputChangeEvents(true);
+ EXPECT_EQ(STATE_DUAL_EXTENDED, configurator_.output_state());
+
+ outputs_[0].has_display_id = true;
+ UpdateOutputs(2);
+ test_api_.SendOutputChangeEvents(true);
+ EXPECT_EQ(STATE_DUAL_MIRROR, configurator_.output_state());
+}
+
} // namespace chromeos
diff --git a/chromeos/display/output_util.cc b/chromeos/display/output_util.cc
new file mode 100644
index 0000000..aa235b0
--- /dev/null
+++ b/chromeos/display/output_util.cc
@@ -0,0 +1,319 @@
+// 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/output_util.h"
+
+#include <X11/Xlib.h>
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xatom.h>
+
+#include "base/message_loop.h"
+#include "base/string_util.h"
+#include "base/sys_byteorder.h"
+
+namespace chromeos {
+namespace {
+
+// Prefixes for the built-in displays.
+const char kInternal_LVDS[] = "LVDS";
+const char kInternal_eDP[] = "eDP";
+
+// Returns 64-bit persistent ID for the specified manufacturer's ID and
+// product_code, and the index of the output it is connected to.
+// |output_index| is used to distinguish the displays of the same type. For
+// example, swapping two identical display between two outputs will not be
+// treated as swap. The 'serial number' field in EDID isn't used here because
+// it is not guaranteed to have unique number and it may have the same fixed
+// value (like 0).
+int64 GetID(uint16 manufacturer_id,
+ uint16 product_code,
+ uint8 output_index) {
+ return ((static_cast<int64>(manufacturer_id) << 24) |
+ (static_cast<int64>(product_code) << 8) | output_index);
+}
+
+bool IsRandRAvailable() {
+ int randr_version_major = 0;
+ int randr_version_minor = 0;
+ static bool is_randr_available = XRRQueryVersion(
+ base::MessagePumpAuraX11::GetDefaultXDisplay(),
+ &randr_version_major, &randr_version_minor);
+ return is_randr_available;
+}
+
+// Get the EDID data from the |output| and stores to |prop|. |nitem| will store
+// the number of characters |prop| will have. It doesn't take the ownership of
+// |prop|, so caller must release it by XFree().
+// Returns true if EDID property is successfully obtained. Otherwise returns
+// false and does not touch |prop| and |nitems|.
+bool GetEDIDProperty(XID output, unsigned long* nitems, unsigned char** prop) {
+ if (!IsRandRAvailable())
+ return false;
+
+ Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay();
+
+ static Atom edid_property = XInternAtom(
+ base::MessagePumpAuraX11::GetDefaultXDisplay(),
+ RR_PROPERTY_RANDR_EDID, false);
+
+ bool has_edid_property = false;
+ int num_properties = 0;
+ Atom* properties = XRRListOutputProperties(display, output, &num_properties);
+ for (int i = 0; i < num_properties; ++i) {
+ if (properties[i] == edid_property) {
+ has_edid_property = true;
+ break;
+ }
+ }
+ XFree(properties);
+ if (!has_edid_property)
+ return false;
+
+ Atom actual_type;
+ int actual_format;
+ unsigned long bytes_after;
+ XRRGetOutputProperty(display,
+ output,
+ edid_property,
+ 0, // offset
+ 128, // length
+ false, // _delete
+ false, // pending
+ AnyPropertyType, // req_type
+ &actual_type,
+ &actual_format,
+ nitems,
+ &bytes_after,
+ prop);
+ DCHECK_EQ(XA_INTEGER, actual_type);
+ DCHECK_EQ(8, actual_format);
+ return true;
+}
+
+// Gets some useful data from the specified output device, such like
+// manufacturer's ID, product code, and human readable name. Returns false if it
+// fails to get those data and doesn't touch manufacturer ID/product code/name.
+// NULL can be passed for unwanted output parameters.
+bool GetOutputDeviceData(XID output,
+ uint16* manufacturer_id,
+ uint16* product_code,
+ std::string* human_readable_name) {
+ unsigned long nitems = 0;
+ unsigned char *prop = NULL;
+ if (!GetEDIDProperty(output, &nitems, &prop))
+ return false;
+
+ bool result = ParseOutputDeviceData(
+ prop, nitems, manufacturer_id, product_code, human_readable_name);
+ XFree(prop);
+ return result;
+}
+
+} // namespace
+
+std::string GetDisplayName(XID output_id) {
+ std::string display_name;
+ GetOutputDeviceData(output_id, NULL, NULL, &display_name);
+ return display_name;
+}
+
+bool GetDisplayId(XID output_id, size_t output_index, int64* display_id_out) {
+ uint16 manufacturer_id = 0;
+ uint16 product_code = 0;
+ if (GetOutputDeviceData(
+ output_id, &manufacturer_id, &product_code, NULL) &&
+ manufacturer_id != 0) {
+ // An ID based on display's index will be assigned later if this call
+ // fails.
+ *display_id_out = GetID(manufacturer_id, product_code, output_index);
+ return true;
+ }
+ return false;
+}
+
+bool ParseOutputDeviceData(const unsigned char* prop,
+ unsigned long nitems,
+ uint16* manufacturer_id,
+ uint16* product_code,
+ std::string* human_readable_name) {
+ // See http://en.wikipedia.org/wiki/Extended_display_identification_data
+ // for the details of EDID data format. We use the following data:
+ // bytes 8-9: manufacturer EISA ID, in big-endian
+ // bytes 10-11: represents product code, in little-endian
+ // bytes 54-125: four descriptors (18-bytes each) which may contain
+ // the display name.
+ const unsigned int kManufacturerOffset = 8;
+ const unsigned int kManufacturerLength = 2;
+ const unsigned int kProductCodeOffset = 10;
+ const unsigned int kProductCodeLength = 2;
+ const unsigned int kDescriptorOffset = 54;
+ const unsigned int kNumDescriptors = 4;
+ const unsigned int kDescriptorLength = 18;
+ // The specifier types.
+ const unsigned char kMonitorNameDescriptor = 0xfc;
+
+ if (manufacturer_id) {
+ if (nitems < kManufacturerOffset + kManufacturerLength) {
+ LOG(ERROR) << "too short EDID data: manifacturer id";
+ return false;
+ }
+
+ *manufacturer_id =
+ *reinterpret_cast<const uint16*>(prop + kManufacturerOffset);
+#if defined(ARCH_CPU_LITTLE_ENDIAN)
+ *manufacturer_id = base::ByteSwap(*manufacturer_id);
+#endif
+ }
+
+ if (product_code) {
+ if (nitems < kProductCodeOffset + kProductCodeLength) {
+ LOG(ERROR) << "too short EDID data: product code";
+ return false;
+ }
+
+ *product_code = base::ByteSwapToLE16(
+ *reinterpret_cast<const uint16*>(prop + kProductCodeOffset));
+ }
+
+ if (!human_readable_name)
+ return true;
+
+ human_readable_name->clear();
+ for (unsigned int i = 0; i < kNumDescriptors; ++i) {
+ if (nitems < kDescriptorOffset + (i + 1) * kDescriptorLength)
+ break;
+
+ const unsigned char* desc_buf =
+ prop + kDescriptorOffset + i * kDescriptorLength;
+ // If the descriptor contains the display name, it has the following
+ // structure:
+ // bytes 0-2, 4: \0
+ // byte 3: descriptor type, defined above.
+ // bytes 5-17: text data, ending with \r, padding with spaces
+ // we should check bytes 0-2 and 4, since it may have other values in
+ // case that the descriptor contains other type of data.
+ if (desc_buf[0] == 0 && desc_buf[1] == 0 && desc_buf[2] == 0 &&
+ desc_buf[4] == 0) {
+ if (desc_buf[3] == kMonitorNameDescriptor) {
+ std::string found_name(
+ reinterpret_cast<const char*>(desc_buf + 5), kDescriptorLength - 5);
+ TrimWhitespaceASCII(found_name, TRIM_TRAILING, human_readable_name);
+ break;
+ }
+ }
+ }
+
+ if (human_readable_name->empty()) {
+ LOG(ERROR) << "invalid EDID: empty readable name";
+ return false;
+ }
+
+ // Verify if the |human_readable_name| consists of printable characters only.
+ for (size_t i = 0; i < human_readable_name->size(); ++i) {
+ char c = (*human_readable_name)[i];
+ if (!isascii(c) || !isprint(c)) {
+ human_readable_name->clear();
+ LOG(ERROR) << "invalid EDID: human unreadable char in name";
+ return false;
+ }
+ }
+
+ return true;
+}
+
+bool GetOutputOverscanFlag(XID output, bool* flag) {
+ unsigned long nitems = 0;
+ unsigned char *prop = NULL;
+ if (!GetEDIDProperty(output, &nitems, &prop))
+ return false;
+
+ bool found = ParseOutputOverscanFlag(prop, nitems, flag);
+ XFree(prop);
+ return found;
+}
+
+bool ParseOutputOverscanFlag(const unsigned char* prop,
+ unsigned long nitems,
+ bool *flag) {
+ // See http://en.wikipedia.org/wiki/Extended_display_identification_data
+ // for the extension format of EDID. Also see EIA/CEA-861 spec for
+ // the format of the extensions and how video capability is encoded.
+ // - byte 0: tag. should be 02h.
+ // - byte 1: revision. only cares revision 3 (03h).
+ // - byte 4-: data block.
+ const unsigned int kExtensionBase = 128;
+ const unsigned int kExtensionSize = 128;
+ const unsigned int kNumExtensionsOffset = 126;
+ const unsigned int kDataBlockOffset = 4;
+ const unsigned char kCEAExtensionTag = '\x02';
+ const unsigned char kExpectedExtensionRevision = '\x03';
+ const unsigned char kExtendedTag = 7;
+ const unsigned char kExtendedVideoCapabilityTag = 0;
+ const unsigned int kPTOverscan = 4;
+ const unsigned int kITOverscan = 2;
+ const unsigned int kCEOverscan = 0;
+
+ if (nitems <= kNumExtensionsOffset)
+ return false;
+
+ unsigned char num_extensions = prop[kNumExtensionsOffset];
+
+ for (size_t i = 0; i < num_extensions; ++i) {
+ // Skip parsing the whole extension if size is not enough.
+ if (nitems < kExtensionBase + (i + 1) * kExtensionSize)
+ break;
+
+ const unsigned char* extension = prop + kExtensionBase + i * kExtensionSize;
+ unsigned char tag = extension[0];
+ unsigned char revision = extension[1];
+ if (tag != kCEAExtensionTag || revision != kExpectedExtensionRevision)
+ continue;
+
+ unsigned char timing_descriptors_start =
+ std::min(extension[2], static_cast<unsigned char>(kExtensionSize));
+ const unsigned char* data_block = extension + kDataBlockOffset;
+ while (data_block < extension + timing_descriptors_start) {
+ // A data block is encoded as:
+ // - byte 1 high 3 bits: tag. '07' for extended tags.
+ // - byte 1 remaining bits: the length of data block.
+ // - byte 2: the extended tag. '0' for video capability.
+ // - byte 3: the capability.
+ unsigned char tag = data_block[0] >> 5;
+ unsigned char payload_length = data_block[0] & 0x1f;
+ if (static_cast<unsigned long>(data_block + payload_length - prop) >
+ nitems)
+ break;
+
+ if (tag != kExtendedTag || payload_length < 2) {
+ data_block += payload_length + 1;
+ continue;
+ }
+
+ unsigned char extended_tag_code = data_block[1];
+ if (extended_tag_code != kExtendedVideoCapabilityTag) {
+ data_block += payload_length + 1;
+ continue;
+ }
+
+ // The difference between preferred, IT, and CE video formats
+ // doesn't matter. Sets |flag| to true if any of these flags are true.
+ if ((data_block[2] & (1 << kPTOverscan)) ||
+ (data_block[2] & (1 << kITOverscan)) ||
+ (data_block[2] & (1 << kCEOverscan))) {
+ *flag = true;
+ } else {
+ *flag = false;
+ }
+ return true;
+ }
+ }
+
+ return false;
+}
+
+bool IsInternalOutputName(const std::string& name) {
+ return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0;
+}
+
+} // namespace chromeos
diff --git a/chromeos/display/output_util.h b/chromeos/display/output_util.h
new file mode 100644
index 0000000..5ecda9b
--- /dev/null
+++ b/chromeos/display/output_util.h
@@ -0,0 +1,56 @@
+// 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_OUTPUT_UTIL_H_
+#define CHROMEOS_DISPLAY_OUTPUT_UTIL_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "chromeos/chromeos_export.h"
+
+// Forward declarations for Xlib and Xrandr.
+// This is so unused X definitions don't pollute the namespace.
+typedef unsigned long XID;
+
+namespace chromeos {
+
+// Generates the display id for the pair of |output| and |index| and store
+// in |display_id_out|. Returns true if the display id is successfully
+// generated, or false otherwise.
+CHROMEOS_EXPORT bool GetDisplayId(XID output, size_t index,
+ int64* display_id_out);
+
+// Generates the human readable string from EDID obtained for |output|.
+CHROMEOS_EXPORT std::string GetDisplayName(XID output);
+
+// Gets the overscan flag from |output| and stores to |flag|. Returns true if
+// the flag is found. Otherwise returns false and doesn't touch |flag|. The
+// output will produce overscan if |flag| is set to true, but the output may
+// still produce overscan even though it returns true and |flag| is set to
+// false.
+CHROMEOS_EXPORT bool GetOutputOverscanFlag(XID output, bool* flag);
+
+// Parses |prop| as EDID data and stores extracted data into |manufacturer_id|,
+// |product_code|, and |human_readable_name| and returns true. NULL can be
+// passed for unwanted output parameters. This is exported for
+// x11_util_unittest.cc.
+CHROMEOS_EXPORT bool ParseOutputDeviceData(const unsigned char* prop,
+ unsigned long nitems,
+ uint16* manufacturer_id,
+ uint16* product_code,
+ std::string* human_readable_name);
+
+// Parses |prop| as EDID data and stores the overscan flag to |flag|. Returns
+// true if the flag is found. This is exported for x11_util_unittest.cc.
+CHROMEOS_EXPORT bool ParseOutputOverscanFlag(const unsigned char* prop,
+ unsigned long nitems,
+ bool* flag);
+
+// Returns true if an output named |name| is an internal display.
+CHROMEOS_EXPORT bool IsInternalOutputName(const std::string& name);
+
+} // namespace chromeos
+
+#endif // CHROMEOS_DISPLAY_OUTPUT_UTIL_H_
diff --git a/chromeos/display/output_util_unittest.cc b/chromeos/display/output_util_unittest.cc
new file mode 100644
index 0000000..cd724ae
--- /dev/null
+++ b/chromeos/display/output_util_unittest.cc
@@ -0,0 +1,208 @@
+// 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/output_util.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chromeos {
+
+namespace {
+
+// Returns the number of characters in the string literal but doesn't count its
+// terminator NULL byte.
+#define charsize(str) (arraysize(str) - 1)
+
+// Sample EDID data extracted from real devices.
+const unsigned char kNormalDisplay[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x22\xf0\x6c\x28\x01\x01\x01\x01"
+ "\x02\x16\x01\x04\xb5\x40\x28\x78\xe2\x8d\x85\xad\x4f\x35\xb1\x25"
+ "\x0e\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\xe2\x68\x00\xa0\xa0\x40\x2e\x60\x30\x20"
+ "\x36\x00\x81\x90\x21\x00\x00\x1a\xbc\x1b\x00\xa0\x50\x20\x17\x30"
+ "\x30\x20\x36\x00\x81\x90\x21\x00\x00\x1a\x00\x00\x00\xfc\x00\x48"
+ "\x50\x20\x5a\x52\x33\x30\x77\x0a\x20\x20\x20\x20\x00\x00\x00\xff"
+ "\x00\x43\x4e\x34\x32\x30\x32\x31\x33\x37\x51\x0a\x20\x20\x00\x71";
+
+const unsigned char kInternalDisplay[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\xa3\x42\x31\x00\x00\x00\x00"
+ "\x00\x15\x01\x03\x80\x1a\x10\x78\x0a\xd3\xe5\x95\x5c\x60\x90\x27"
+ "\x19\x50\x54\x00\x00\x00\x01\x01\x01\x01\x01\x01\x01\x01\x01\x01"
+ "\x01\x01\x01\x01\x01\x01\x9e\x1b\x00\xa0\x50\x20\x12\x30\x10\x30"
+ "\x13\x00\x05\xa3\x10\x00\x00\x19\x00\x00\x00\x0f\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x23\x87\x02\x64\x00\x00\x00\x00\xfe\x00\x53"
+ "\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x00\x00\x00\xfe"
+ "\x00\x31\x32\x31\x41\x54\x31\x31\x2d\x38\x30\x31\x0a\x20\x00\x45";
+
+const unsigned char kOverscanDisplay[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x4c\x2d\xfe\x08\x00\x00\x00\x00"
+ "\x29\x15\x01\x03\x80\x10\x09\x78\x0a\xee\x91\xa3\x54\x4c\x99\x26"
+ "\x0f\x50\x54\xbd\xef\x80\x71\x4f\x81\xc0\x81\x00\x81\x80\x95\x00"
+ "\xa9\xc0\xb3\x00\x01\x01\x02\x3a\x80\x18\x71\x38\x2d\x40\x58\x2c"
+ "\x45\x00\xa0\x5a\x00\x00\x00\x1e\x66\x21\x56\xaa\x51\x00\x1e\x30"
+ "\x46\x8f\x33\x00\xa0\x5a\x00\x00\x00\x1e\x00\x00\x00\xfd\x00\x18"
+ "\x4b\x0f\x51\x17\x00\x0a\x20\x20\x20\x20\x20\x20\x00\x00\x00\xfc"
+ "\x00\x53\x41\x4d\x53\x55\x4e\x47\x0a\x20\x20\x20\x20\x20\x01\x1d"
+ "\x02\x03\x1f\xf1\x47\x90\x04\x05\x03\x20\x22\x07\x23\x09\x07\x07"
+ "\x83\x01\x00\x00\xe2\x00\x0f\x67\x03\x0c\x00\x20\x00\xb8\x2d\x01"
+ "\x1d\x80\x18\x71\x1c\x16\x20\x58\x2c\x25\x00\xa0\x5a\x00\x00\x00"
+ "\x9e\x01\x1d\x00\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\xa0\x5a\x00"
+ "\x00\x00\x1e\x8c\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\xa0"
+ "\x5a\x00\x00\x00\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
+ "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xc6";
+
+// The EDID info misdetecting overscan once. see crbug.com/226318
+const unsigned char kMisdetecedDisplay[] =
+ "\x00\xff\xff\xff\xff\xff\xff\x00\x10\xac\x64\x40\x4c\x30\x30\x32"
+ "\x0c\x15\x01\x03\x80\x40\x28\x78\xea\x8d\x85\xad\x4f\x35\xb1\x25"
+ "\x0e\x50\x54\xa5\x4b\x00\x71\x4f\x81\x00\x81\x80\xd1\x00\xa9\x40"
+ "\x01\x01\x01\x01\x01\x01\x28\x3c\x80\xa0\x70\xb0\x23\x40\x30\x20"
+ "\x36\x00\x81\x91\x21\x00\x00\x1a\x00\x00\x00\xff\x00\x50\x48\x35"
+ "\x4e\x59\x31\x33\x4e\x32\x30\x30\x4c\x0a\x00\x00\x00\xfc\x00\x44"
+ "\x45\x4c\x4c\x20\x55\x33\x30\x31\x31\x0a\x20\x20\x00\x00\x00\xfd"
+ "\x00\x31\x56\x1d\x5e\x12\x00\x0a\x20\x20\x20\x20\x20\x20\x01\x38"
+ "\x02\x03\x29\xf1\x50\x90\x05\x04\x03\x02\x07\x16\x01\x06\x11\x12"
+ "\x15\x13\x14\x1f\x20\x23\x0d\x7f\x07\x83\x0f\x00\x00\x67\x03\x0c"
+ "\x00\x10\x00\x38\x2d\xe3\x05\x03\x01\x02\x3a\x80\x18\x71\x38\x2d"
+ "\x40\x58\x2c\x45\x00\x81\x91\x21\x00\x00\x1e\x01\x1d\x80\x18\x71"
+ "\x1c\x16\x20\x58\x2c\x25\x00\x81\x91\x21\x00\x00\x9e\x01\x1d\x00"
+ "\x72\x51\xd0\x1e\x20\x6e\x28\x55\x00\x81\x91\x21\x00\x00\x1e\x8c"
+ "\x0a\xd0\x8a\x20\xe0\x2d\x10\x10\x3e\x96\x00\x81\x91\x21\x00\x00"
+ "\x18\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x94";
+
+}
+
+TEST(OutputUtilTest, ParseEDID) {
+ uint16 manufacturer_id = 0;
+ uint16 product_code = 0;
+ std::string human_readable_name;
+ EXPECT_TRUE(ParseOutputDeviceData(
+ kNormalDisplay, charsize(kNormalDisplay),
+ &manufacturer_id, &product_code, &human_readable_name));
+ EXPECT_EQ(0x22f0u, manufacturer_id);
+ EXPECT_EQ(0x286cu, product_code);
+ EXPECT_EQ("HP ZR30w", human_readable_name);
+
+ manufacturer_id = 0;
+ product_code = 0;
+ human_readable_name.clear();
+ EXPECT_TRUE(ParseOutputDeviceData(
+ kInternalDisplay, charsize(kInternalDisplay),
+ &manufacturer_id, &product_code, NULL));
+ EXPECT_EQ(0x4ca3u, manufacturer_id);
+ EXPECT_EQ(0x3142u, product_code);
+ EXPECT_EQ("", human_readable_name);
+
+ // Internal display doesn't have name.
+ EXPECT_FALSE(ParseOutputDeviceData(
+ kInternalDisplay, charsize(kInternalDisplay),
+ NULL, NULL, &human_readable_name));
+
+ manufacturer_id = 0;
+ product_code = 0;
+ human_readable_name.clear();
+ EXPECT_TRUE(ParseOutputDeviceData(
+ kOverscanDisplay, charsize(kOverscanDisplay),
+ &manufacturer_id, &product_code, &human_readable_name));
+ EXPECT_EQ(0x4c2du, manufacturer_id);
+ EXPECT_EQ(0x08feu, product_code);
+ EXPECT_EQ("SAMSUNG", human_readable_name);
+}
+
+TEST(OutputUtilTest, ParseBrokenEDID) {
+ uint16 manufacturer_id = 0;
+ uint16 product_code = 0;
+ std::string human_readable_name;
+
+ // length == 0
+ EXPECT_FALSE(ParseOutputDeviceData(
+ kNormalDisplay, 0,
+ &manufacturer_id, &product_code, &human_readable_name));
+
+ // name is broken. Copying kNormalDisplay and substitute its name data by
+ // some control code.
+ std::string display_data(
+ reinterpret_cast<const char*>(kNormalDisplay), charsize(kNormalDisplay));
+
+ // display's name data is embedded in byte 95-107 in this specific example.
+ // Fix here too when the contents of kNormalDisplay is altered.
+ display_data[97] = '\x1b';
+ EXPECT_FALSE(ParseOutputDeviceData(
+ reinterpret_cast<const unsigned char*>(display_data.data()),
+ display_data.size(),
+ &manufacturer_id, &product_code, &human_readable_name));
+
+ // If |human_readable_name| isn't specified, it skips parsing the name.
+ manufacturer_id = 0;
+ product_code = 0;
+ EXPECT_TRUE(ParseOutputDeviceData(
+ reinterpret_cast<const unsigned char*>(display_data.data()),
+ display_data.size(),
+ &manufacturer_id, &product_code, NULL));
+ EXPECT_EQ(0x22f0u, manufacturer_id);
+ EXPECT_EQ(0x286cu, product_code);
+}
+
+TEST(OutputUtilTest, ParseOverscanFlag) {
+ bool flag = false;
+ EXPECT_FALSE(ParseOutputOverscanFlag(
+ kNormalDisplay, charsize(kNormalDisplay), &flag));
+
+ flag = false;
+ EXPECT_FALSE(ParseOutputOverscanFlag(
+ kInternalDisplay, charsize(kInternalDisplay), &flag));
+
+ flag = false;
+ EXPECT_TRUE(ParseOutputOverscanFlag(
+ kOverscanDisplay, charsize(kOverscanDisplay), &flag));
+ EXPECT_TRUE(flag);
+
+ flag = false;
+ EXPECT_FALSE(ParseOutputOverscanFlag(
+ kMisdetecedDisplay, charsize(kMisdetecedDisplay), &flag));
+
+ flag = false;
+ // Copy |kOverscanDisplay| and set flags to false in it. The overscan flags
+ // are embedded at byte 150 in this specific example. Fix here too when the
+ // contents of kOverscanDisplay is altered.
+ std::string display_data(reinterpret_cast<const char*>(kOverscanDisplay),
+ charsize(kOverscanDisplay));
+ display_data[150] = '\0';
+ EXPECT_TRUE(ParseOutputOverscanFlag(
+ reinterpret_cast<const unsigned char*>(display_data.data()),
+ display_data.size(), &flag));
+ EXPECT_FALSE(flag);
+}
+
+TEST(OutputUtilTest, ParseBrokenOverscanData) {
+ // Do not fill valid data here because it anyway fails to parse the data.
+ scoped_ptr<unsigned char[]> data(new unsigned char[126]);
+ bool flag = false;
+ EXPECT_FALSE(ParseOutputOverscanFlag(data.get(), 0, &flag));
+ EXPECT_FALSE(ParseOutputOverscanFlag(data.get(), 126, &flag));
+
+ // extending data because ParseOutputOverscanFlag() will access the data.
+ data.reset(new unsigned char[150]);
+ // The number of CEA extensions is stored at byte 126.
+ data[126] = '\x01';
+ EXPECT_FALSE(ParseOutputOverscanFlag(data.get(), 128, &flag));
+ EXPECT_FALSE(ParseOutputOverscanFlag(data.get(), 150, &flag));
+}
+
+TEST(OutputUtilTest, IsInternalOutputName) {
+ EXPECT_TRUE(IsInternalOutputName("LVDS"));
+ EXPECT_TRUE(IsInternalOutputName("eDP"));
+ EXPECT_TRUE(IsInternalOutputName("LVDSxx"));
+ EXPECT_TRUE(IsInternalOutputName("eDPzz"));
+
+ EXPECT_FALSE(IsInternalOutputName("xyz"));
+ EXPECT_FALSE(IsInternalOutputName("abcLVDS"));
+ EXPECT_FALSE(IsInternalOutputName("cdeeDP"));
+ EXPECT_FALSE(IsInternalOutputName("LVD"));
+ EXPECT_FALSE(IsInternalOutputName("eD"));
+}
+
+} // namespace chromeos
diff --git a/chromeos/display/real_output_configurator_delegate.cc b/chromeos/display/real_output_configurator_delegate.cc
index 531a46d..a0bb007 100644
--- a/chromeos/display/real_output_configurator_delegate.cc
+++ b/chromeos/display/real_output_configurator_delegate.cc
@@ -17,6 +17,7 @@
#include "base/message_pump_aurax11.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager_client.h"
+#include "chromeos/display/output_util.h"
namespace chromeos {
@@ -28,8 +29,7 @@ const float kDpi96 = 96.0;
const float kPixelsToMmScale = kMmInInch / kDpi96;
bool IsInternalOutput(const XRROutputInfo* output_info) {
- return OutputConfigurator::IsInternalOutputName(
- std::string(output_info->name));
+ return IsInternalOutputName(std::string(output_info->name));
}
RRMode GetOutputNativeMode(const XRROutputInfo* output_info) {
@@ -121,6 +121,8 @@ RealOutputConfiguratorDelegate::GetOutputs() {
if (is_connected) {
OutputConfigurator::OutputSnapshot to_populate;
to_populate.output = this_id;
+ to_populate.has_display_id =
+ GetDisplayId(this_id, i, &to_populate.display_id);
(outputs.empty() ? one_info : two_info) = output_info;
// Now, look up the current CRTC and any related info.
@@ -147,7 +149,6 @@ RealOutputConfiguratorDelegate::GetOutputs() {
to_populate.is_aspect_preserving_scaling =
IsOutputAspectPreservingScaling(this_id);
to_populate.touch_device_id = None;
- to_populate.index = i;
VLOG(1) << "Found display #" << outputs.size()
<< " with output " << to_populate.output