summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/base.gyp13
-rw-r--r--base/base.gypi6
-rw-r--r--base/x11/edid_parser_x11.cc196
-rw-r--r--base/x11/edid_parser_x11.h54
-rw-r--r--base/x11/edid_parser_x11_unittest.cc167
-rw-r--r--build/linux/system.gyp36
-rw-r--r--chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.cc17
-rw-r--r--chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.h1
-rw-r--r--chromeos/display/output_util.cc191
-rw-r--r--chromeos/display/output_util.h22
-rw-r--r--chromeos/display/output_util_unittest.cc108
-rw-r--r--chromeos/display/real_output_configurator_delegate.cc3
-rw-r--r--ui/views/views.gyp2
-rw-r--r--ui/views/widget/desktop_aura/desktop_screen_x11.cc291
-rw-r--r--ui/views/widget/desktop_aura/desktop_screen_x11.h95
-rw-r--r--ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc186
16 files changed, 1011 insertions, 377 deletions
diff --git a/base/base.gyp b/base/base.gyp
index fbc33b8..4aae198 100644
--- a/base/base.gyp
+++ b/base/base.gyp
@@ -77,6 +77,14 @@
'../build/linux/system.gyp:x11',
],
}],
+ ['use_aura==1 and use_x11==1', {
+ 'dependencies': [
+ '../build/linux/system.gyp:xrandr',
+ ],
+ 'export_dependent_settings': [
+ '../build/linux/system.gyp:xrandr',
+ ],
+ }],
['OS == "android" and _toolset == "host"', {
# Always build base as a static_library for host toolset, even if
# we're doing a component build. Specifically, we only care about the
@@ -810,6 +818,11 @@
'win/win_util_unittest.cc',
],
}],
+ ['use_aura==1 and use_x11==1', {
+ 'sources': [
+ 'x11/edid_parser_x11_unittest.cc',
+ ],
+ }],
['use_system_nspr==1', {
'dependencies': [
'third_party/nspr/nspr.gyp:nspr',
diff --git a/base/base.gypi b/base/base.gypi
index 8caee68..cafb10d 100644
--- a/base/base.gypi
+++ b/base/base.gypi
@@ -669,6 +669,12 @@
'win/wrapped_window_proc.h',
],
'conditions': [
+ ['use_aura==1 and use_x11==1', {
+ 'sources': [
+ 'x11/edid_parser_x11.cc',
+ 'x11/edid_parser_x11.h',
+ ],
+ }],
['google_tv==1', {
'sources': [
'android/context_types.cc',
diff --git a/base/x11/edid_parser_x11.cc b/base/x11/edid_parser_x11.cc
new file mode 100644
index 0000000..74b14b6
--- /dev/null
+++ b/base/x11/edid_parser_x11.cc
@@ -0,0 +1,196 @@
+// 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 "base/x11/edid_parser_x11.h"
+
+#include <X11/extensions/Xrandr.h>
+#include <X11/Xatom.h>
+#include <X11/Xlib.h>
+
+#include "base/hash.h"
+#include "base/message_loop/message_loop.h"
+#include "base/strings/string_util.h"
+#include "base/sys_byteorder.h"
+
+namespace {
+
+// Returns 64-bit persistent ID for the specified manufacturer's ID and
+// product_code_hash, 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,
+ uint32 product_code_hash,
+ uint8 output_index) {
+ return ((static_cast<int64>(manufacturer_id) << 40) |
+ (static_cast<int64>(product_code_hash) << 8) | output_index);
+}
+
+bool IsRandRAvailable() {
+ int randr_version_major = 0;
+ int randr_version_minor = 0;
+ static bool is_randr_available = XRRQueryVersion(
+ base::MessagePumpX11::GetDefaultXDisplay(),
+ &randr_version_major, &randr_version_minor);
+ return is_randr_available;
+}
+
+} // namespace
+
+namespace base {
+
+bool GetEDIDProperty(XID output, unsigned long* nitems, unsigned char** prop) {
+ if (!IsRandRAvailable())
+ return false;
+
+ Display* display = base::MessagePumpX11::GetDefaultXDisplay();
+
+ static Atom edid_property = XInternAtom(
+ base::MessagePumpX11::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;
+}
+
+bool GetDisplayId(XID output_id, size_t output_index, int64* display_id_out) {
+ unsigned long nitems = 0;
+ unsigned char* prop = NULL;
+ if (!GetEDIDProperty(output_id, &nitems, &prop))
+ return false;
+
+ bool result =
+ GetDisplayIdFromEDID(prop, nitems, output_index, display_id_out);
+ XFree(prop);
+ return result;
+}
+
+bool GetDisplayIdFromEDID(const unsigned char* prop,
+ unsigned long nitems,
+ size_t output_index,
+ int64* display_id_out) {
+ uint16 manufacturer_id = 0;
+ std::string product_name;
+
+ // ParseOutputDeviceData fails if it doesn't have product_name.
+ ParseOutputDeviceData(prop, nitems, &manufacturer_id, &product_name);
+
+ // Generates product specific value from product_name instead of product code.
+ // See crbug.com/240341
+ uint32 product_code_hash = product_name.empty() ?
+ 0 : base::Hash(product_name);
+ if (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_hash, output_index);
+ return true;
+ }
+ return false;
+}
+
+bool ParseOutputDeviceData(const unsigned char* prop,
+ unsigned long nitems,
+ uint16* manufacturer_id,
+ 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 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 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 (!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;
+ }
+ }
+ }
+
+ // 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;
+}
+
+} // namespace base
diff --git a/base/x11/edid_parser_x11.h b/base/x11/edid_parser_x11.h
new file mode 100644
index 0000000..0fba0b5
--- /dev/null
+++ b/base/x11/edid_parser_x11.h
@@ -0,0 +1,54 @@
+// 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 BASE_X11_EDID_PARSER_X11_H_
+#define BASE_X11_EDID_PARSER_X11_H_
+
+#include <string>
+
+#include "base/base_export.h"
+#include "base/basictypes.h"
+
+typedef unsigned long XID;
+
+// EDID (Extended Display Identification Data) is a format for monitor
+// metadata. This provides a parser for the data and an interface to get it
+// from XRandR.
+
+namespace base {
+
+// 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|.
+BASE_EXPORT bool GetEDIDProperty(XID output,
+ unsigned long* nitems,
+ unsigned char** prop);
+
+// Gets the EDID data from |output| and generates the display id through
+// |GetDisplayIdFromEDID|.
+BASE_EXPORT bool GetDisplayId(XID output, size_t index,
+ int64* display_id_out);
+
+// Generates the display id for the pair of |prop| with |nitems| length and
+// |index|, and store in |display_id_out|. Returns true if the display id is
+// successfully generated, or false otherwise.
+BASE_EXPORT bool GetDisplayIdFromEDID(const unsigned char* prop,
+ unsigned long nitems,
+ size_t index,
+ int64* display_id_out);
+
+// Parses |prop| as EDID data and stores extracted data into |manufacturer_id|
+// and |human_readable_name| and returns true. NULL can be passed for unwanted
+// output parameters. Some devices (especially internal displays) may not have
+// the field for |human_readable_name|, and it will return true in that case.
+BASE_EXPORT bool ParseOutputDeviceData(const unsigned char* prop,
+ unsigned long nitems,
+ uint16* manufacturer_id,
+ std::string* human_readable_name);
+
+} // namespace base
+
+#endif // BASE_X11_EDID_PARSER_X11_H_
diff --git a/base/x11/edid_parser_x11_unittest.cc b/base/x11/edid_parser_x11_unittest.cc
new file mode 100644
index 0000000..97e3ce1
--- /dev/null
+++ b/base/x11/edid_parser_x11_unittest.cc
@@ -0,0 +1,167 @@
+// 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 "base/x11/edid_parser_x11.h"
+
+#include "base/memory/scoped_ptr.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include <X11/extensions/Xrandr.h>
+
+namespace base {
+
+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";
+
+const unsigned char kLP2565A[] =
+ "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
+ "\x02\x12\x01\x03\x80\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
+ "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x81\x80\x81\x99\x71\x00\xA9\x00"
+ "\xA9\x40\xB3\x00\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20"
+ "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E"
+ "\x5E\x11\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
+ "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
+ "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\xA4";
+
+const unsigned char kLP2565B[] =
+ "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x26\x01\x01\x01\x01"
+ "\x02\x12\x01\x03\x6E\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
+ "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x71\x00\xA9\x00\xA9\x40\xA9\x4F"
+ "\xB3\x00\xD1\xC0\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20"
+ "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E"
+ "\x5E\x15\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
+ "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
+ "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\x45";
+
+} // namespace
+
+TEST(EdidParserX11Test, ParseEDID) {
+ uint16 manufacturer_id = 0;
+ std::string human_readable_name;
+ EXPECT_TRUE(ParseOutputDeviceData(
+ kNormalDisplay, charsize(kNormalDisplay),
+ &manufacturer_id, &human_readable_name));
+ EXPECT_EQ(0x22f0u, manufacturer_id);
+ EXPECT_EQ("HP ZR30w", human_readable_name);
+
+ manufacturer_id = 0;
+ human_readable_name.clear();
+ EXPECT_TRUE(ParseOutputDeviceData(
+ kInternalDisplay, charsize(kInternalDisplay),
+ &manufacturer_id, NULL));
+ EXPECT_EQ(0x4ca3u, manufacturer_id);
+ EXPECT_EQ("", human_readable_name);
+
+ // Internal display doesn't have name.
+ EXPECT_TRUE(ParseOutputDeviceData(
+ kInternalDisplay, charsize(kInternalDisplay),
+ NULL, &human_readable_name));
+ EXPECT_TRUE(human_readable_name.empty());
+
+ manufacturer_id = 0;
+ human_readable_name.clear();
+ EXPECT_TRUE(ParseOutputDeviceData(
+ kOverscanDisplay, charsize(kOverscanDisplay),
+ &manufacturer_id, &human_readable_name));
+ EXPECT_EQ(0x4c2du, manufacturer_id);
+ EXPECT_EQ("SAMSUNG", human_readable_name);
+}
+
+TEST(EdidParserX11Test, ParseBrokenEDID) {
+ uint16 manufacturer_id = 0;
+ std::string human_readable_name;
+
+ // length == 0
+ EXPECT_FALSE(ParseOutputDeviceData(
+ kNormalDisplay, 0,
+ &manufacturer_id, &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, &human_readable_name));
+
+ // If |human_readable_name| isn't specified, it skips parsing the name.
+ manufacturer_id = 0;
+ EXPECT_TRUE(ParseOutputDeviceData(
+ reinterpret_cast<const unsigned char*>(display_data.data()),
+ display_data.size(),
+ &manufacturer_id, NULL));
+ EXPECT_EQ(0x22f0u, manufacturer_id);
+}
+
+TEST(EdidParserX11Test, GetDisplayId) {
+ // EDID of kLP2565A and B are slightly different but actually the same device.
+ int64 id1 = -1;
+ int64 id2 = -1;
+ EXPECT_TRUE(GetDisplayIdFromEDID(kLP2565A, charsize(kLP2565A), 0, &id1));
+ EXPECT_TRUE(GetDisplayIdFromEDID(kLP2565B, charsize(kLP2565B), 0, &id2));
+ EXPECT_EQ(id1, id2);
+ EXPECT_NE(-1, id1);
+}
+
+TEST(EdidParserX11Test, GetDisplayIdFromInternal) {
+ int64 id = -1;
+ EXPECT_TRUE(GetDisplayIdFromEDID(
+ kInternalDisplay, charsize(kInternalDisplay), 0, &id));
+ EXPECT_NE(-1, id);
+}
+
+TEST(EdidParserX11Test, GetDisplayIdFailure) {
+ int64 id = -1;
+ EXPECT_FALSE(GetDisplayIdFromEDID(NULL, 0, 0, &id));
+ EXPECT_EQ(-1, id);
+}
+
+} // namespace base
diff --git a/build/linux/system.gyp b/build/linux/system.gyp
index 68e4d36..6a359db 100644
--- a/build/linux/system.gyp
+++ b/build/linux/system.gyp
@@ -681,6 +681,42 @@
],
},
{
+ 'target_name': 'xrandr',
+ 'type': 'none',
+ 'toolsets': ['host', 'target'],
+ 'conditions': [
+ ['_toolset=="target"', {
+ 'direct_dependent_settings': {
+ 'cflags': [
+ '<!@(<(pkg-config) --cflags xrandr)',
+ ],
+ },
+ 'link_settings': {
+ 'ldflags': [
+ '<!@(<(pkg-config) --libs-only-L --libs-only-other xrandr)',
+ ],
+ 'libraries': [
+ '<!@(<(pkg-config) --libs-only-l xrandr)',
+ ],
+ },
+ }, {
+ 'direct_dependent_settings': {
+ 'cflags': [
+ '<!@(pkg-config --cflags xrandr)',
+ ],
+ },
+ 'link_settings': {
+ 'ldflags': [
+ '<!@(pkg-config --libs-only-L --libs-only-other xrandr)',
+ ],
+ 'libraries': [
+ '<!@(pkg-config --libs-only-l xrandr)',
+ ],
+ },
+ }],
+ ],
+ },
+ {
'target_name': 'libgcrypt',
'type': 'none',
'conditions': [
diff --git a/chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.cc b/chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.cc
index 328b570..a63ebce 100644
--- a/chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.cc
+++ b/chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.cc
@@ -52,12 +52,7 @@ void ChromeBrowserMainExtraPartsAura::ToolkitInitialized() {
#if !defined(OS_CHROMEOS)
#if defined(USE_ASH)
active_desktop_monitor_.reset(new ActiveDesktopMonitor(GetInitialDesktop()));
- if (!chrome::ShouldOpenAshOnStartup())
#endif
- {
- gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE,
- views::CreateDesktopScreen());
- }
#endif
#if !defined(USE_ASH) && defined(OS_LINUX) && defined(USE_X11)
@@ -66,6 +61,18 @@ void ChromeBrowserMainExtraPartsAura::ToolkitInitialized() {
#endif
}
+void ChromeBrowserMainExtraPartsAura::PostMainMessageLoopStart() {
+#if !defined(OS_CHROMEOS)
+#if defined(USE_ASH)
+ if (!chrome::ShouldOpenAshOnStartup())
+#endif
+ {
+ gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE,
+ views::CreateDesktopScreen());
+ }
+#endif
+}
+
void ChromeBrowserMainExtraPartsAura::PostMainMessageLoopRun() {
active_desktop_monitor_.reset();
diff --git a/chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.h b/chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.h
index 8ba39ab..a199fe8 100644
--- a/chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.h
+++ b/chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.h
@@ -19,6 +19,7 @@ class ChromeBrowserMainExtraPartsAura : public ChromeBrowserMainExtraParts {
// Overridden from ChromeBrowserMainExtraParts:
virtual void ToolkitInitialized() OVERRIDE;
+ virtual void PostMainMessageLoopStart() OVERRIDE;
virtual void PostMainMessageLoopRun() OVERRIDE;
private:
diff --git a/chromeos/display/output_util.cc b/chromeos/display/output_util.cc
index 5e223c5..a1485b4 100644
--- a/chromeos/display/output_util.cc
+++ b/chromeos/display/output_util.cc
@@ -4,14 +4,12 @@
#include "chromeos/display/output_util.h"
-#include <X11/Xlib.h>
#include <X11/extensions/Xrandr.h>
#include <X11/Xatom.h>
+#include <X11/Xlib.h>
-#include "base/hash.h"
-#include "base/message_loop/message_loop.h"
#include "base/strings/string_util.h"
-#include "base/sys_byteorder.h"
+#include "base/x11/edid_parser_x11.h"
namespace chromeos {
namespace {
@@ -21,78 +19,6 @@ const char kInternal_LVDS[] = "LVDS";
const char kInternal_eDP[] = "eDP";
const char kInternal_DSI[] = "DSI";
-// Returns 64-bit persistent ID for the specified manufacturer's ID and
-// product_code_hash, 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,
- uint32 product_code_hash,
- uint8 output_index) {
- return ((static_cast<int64>(manufacturer_id) << 40) |
- (static_cast<int64>(product_code_hash) << 8) | output_index);
-}
-
-bool IsRandRAvailable() {
- int randr_version_major = 0;
- int randr_version_minor = 0;
- static bool is_randr_available = XRRQueryVersion(
- base::MessagePumpX11::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::MessagePumpX11::GetDefaultXDisplay();
-
- static Atom edid_property = XInternAtom(
- base::MessagePumpX11::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.
@@ -102,10 +28,10 @@ bool GetOutputDeviceData(XID output,
std::string* human_readable_name) {
unsigned long nitems = 0;
unsigned char *prop = NULL;
- if (!GetEDIDProperty(output, &nitems, &prop))
+ if (!base::GetEDIDProperty(output, &nitems, &prop))
return false;
- bool result = ParseOutputDeviceData(
+ bool result = base::ParseOutputDeviceData(
prop, nitems, manufacturer_id, human_readable_name);
XFree(prop);
return result;
@@ -119,117 +45,10 @@ std::string GetDisplayName(XID output_id) {
return display_name;
}
-bool GetDisplayId(XID output_id, size_t output_index, int64* display_id_out) {
- unsigned long nitems = 0;
- unsigned char* prop = NULL;
- if (!GetEDIDProperty(output_id, &nitems, &prop))
- return false;
-
- bool result =
- GetDisplayIdFromEDID(prop, nitems, output_index, display_id_out);
- XFree(prop);
- return result;
-}
-
-bool GetDisplayIdFromEDID(const unsigned char* prop,
- unsigned long nitems,
- size_t output_index,
- int64* display_id_out) {
- uint16 manufacturer_id = 0;
- std::string product_name;
-
- // ParseOutputDeviceData fails if it doesn't have product_name.
- ParseOutputDeviceData(prop, nitems, &manufacturer_id, &product_name);
-
- // Generates product specific value from product_name instead of product code.
- // See crbug.com/240341
- uint32 product_code_hash = product_name.empty() ?
- 0 : base::Hash(product_name);
- if (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_hash, output_index);
- return true;
- }
- return false;
-}
-
-bool ParseOutputDeviceData(const unsigned char* prop,
- unsigned long nitems,
- uint16* manufacturer_id,
- 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 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 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 (!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;
- }
- }
- }
-
- // 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))
+ if (!base::GetEDIDProperty(output, &nitems, &prop))
return false;
bool found = ParseOutputOverscanFlag(prop, nitems, flag);
diff --git a/chromeos/display/output_util.h b/chromeos/display/output_util.h
index d2f1f01..3c7a49f 100644
--- a/chromeos/display/output_util.h
+++ b/chromeos/display/output_util.h
@@ -23,19 +23,6 @@ typedef _XRROutputInfo XRROutputInfo;
namespace chromeos {
-// Gets the EDID data from |output| and generates the display id through
-// |GetDisplayIdFromEDID|.
-CHROMEOS_EXPORT bool GetDisplayId(XID output, size_t index,
- int64* display_id_out);
-
-// Generates the display id for the pair of |prop| with |nitems| length and
-// |index|, and store in |display_id_out|. Returns true if the display id is
-// successfully generated, or false otherwise.
-CHROMEOS_EXPORT bool GetDisplayIdFromEDID(const unsigned char* prop,
- unsigned long nitems,
- size_t index,
- int64* display_id_out);
-
// Generates the human readable string from EDID obtained for |output|.
CHROMEOS_EXPORT std::string GetDisplayName(XID output);
@@ -46,15 +33,6 @@ CHROMEOS_EXPORT std::string GetDisplayName(XID output);
// false.
CHROMEOS_EXPORT bool GetOutputOverscanFlag(XID output, bool* flag);
-// Parses |prop| as EDID data and stores extracted data into |manufacturer_id|
-// and |human_readable_name| and returns true. NULL can be passed for unwanted
-// output parameters. Some devices (especially internal displays) may not have
-// the field for |human_readable_name|, and it will return true in that case.
-CHROMEOS_EXPORT bool ParseOutputDeviceData(const unsigned char* prop,
- unsigned long nitems,
- uint16* manufacturer_id,
- 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,
diff --git a/chromeos/display/output_util_unittest.cc b/chromeos/display/output_util_unittest.cc
index 9c72d7b..1229b4f 100644
--- a/chromeos/display/output_util_unittest.cc
+++ b/chromeos/display/output_util_unittest.cc
@@ -75,90 +75,7 @@ const unsigned char kMisdetecedDisplay[] =
"\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";
-}
-
-const unsigned char kLP2565A[] =
- "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x76\x26\x01\x01\x01\x01"
- "\x02\x12\x01\x03\x80\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
- "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x81\x80\x81\x99\x71\x00\xA9\x00"
- "\xA9\x40\xB3\x00\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20"
- "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E"
- "\x5E\x11\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
- "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
- "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\xA4";
-
-const unsigned char kLP2565B[] =
- "\x00\xFF\xFF\xFF\xFF\xFF\xFF\x00\x22\xF0\x75\x26\x01\x01\x01\x01"
- "\x02\x12\x01\x03\x6E\x34\x21\x78\xEE\xEF\x95\xA3\x54\x4C\x9B\x26"
- "\x0F\x50\x54\xA5\x6B\x80\x81\x40\x71\x00\xA9\x00\xA9\x40\xA9\x4F"
- "\xB3\x00\xD1\xC0\xD1\x00\x28\x3C\x80\xA0\x70\xB0\x23\x40\x30\x20"
- "\x36\x00\x07\x44\x21\x00\x00\x1A\x00\x00\x00\xFD\x00\x30\x55\x1E"
- "\x5E\x15\x00\x0A\x20\x20\x20\x20\x20\x20\x00\x00\x00\xFC\x00\x48"
- "\x50\x20\x4C\x50\x32\x34\x36\x35\x0A\x20\x20\x20\x00\x00\x00\xFF"
- "\x00\x43\x4E\x4B\x38\x30\x32\x30\x34\x48\x4D\x0A\x20\x20\x00\x45";
-
-TEST(OutputUtilTest, ParseEDID) {
- uint16 manufacturer_id = 0;
- std::string human_readable_name;
- EXPECT_TRUE(ParseOutputDeviceData(
- kNormalDisplay, charsize(kNormalDisplay),
- &manufacturer_id, &human_readable_name));
- EXPECT_EQ(0x22f0u, manufacturer_id);
- EXPECT_EQ("HP ZR30w", human_readable_name);
-
- manufacturer_id = 0;
- human_readable_name.clear();
- EXPECT_TRUE(ParseOutputDeviceData(
- kInternalDisplay, charsize(kInternalDisplay),
- &manufacturer_id, NULL));
- EXPECT_EQ(0x4ca3u, manufacturer_id);
- EXPECT_EQ("", human_readable_name);
-
- // Internal display doesn't have name.
- EXPECT_TRUE(ParseOutputDeviceData(
- kInternalDisplay, charsize(kInternalDisplay),
- NULL, &human_readable_name));
- EXPECT_TRUE(human_readable_name.empty());
-
- manufacturer_id = 0;
- human_readable_name.clear();
- EXPECT_TRUE(ParseOutputDeviceData(
- kOverscanDisplay, charsize(kOverscanDisplay),
- &manufacturer_id, &human_readable_name));
- EXPECT_EQ(0x4c2du, manufacturer_id);
- EXPECT_EQ("SAMSUNG", human_readable_name);
-}
-
-TEST(OutputUtilTest, ParseBrokenEDID) {
- uint16 manufacturer_id = 0;
- std::string human_readable_name;
-
- // length == 0
- EXPECT_FALSE(ParseOutputDeviceData(
- kNormalDisplay, 0,
- &manufacturer_id, &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, &human_readable_name));
-
- // If |human_readable_name| isn't specified, it skips parsing the name.
- manufacturer_id = 0;
- EXPECT_TRUE(ParseOutputDeviceData(
- reinterpret_cast<const unsigned char*>(display_data.data()),
- display_data.size(),
- &manufacturer_id, NULL));
- EXPECT_EQ(0x22f0u, manufacturer_id);
-}
+} // namespace
TEST(OutputUtilTest, ParseOverscanFlag) {
bool flag = false;
@@ -223,27 +140,4 @@ TEST(OutputUtilTest, IsInternalOutputName) {
EXPECT_FALSE(IsInternalOutputName("DS"));
}
-TEST(OutputUtilTest, GetDisplayId) {
- // EDID of kLP2565A and B are slightly different but actually the same device.
- int64 id1 = -1;
- int64 id2 = -1;
- EXPECT_TRUE(GetDisplayIdFromEDID(kLP2565A, charsize(kLP2565A), 0, &id1));
- EXPECT_TRUE(GetDisplayIdFromEDID(kLP2565B, charsize(kLP2565B), 0, &id2));
- EXPECT_EQ(id1, id2);
- EXPECT_NE(-1, id1);
-}
-
-TEST(OutputUtilTest, GetDisplayIdFromInternal) {
- int64 id = -1;
- EXPECT_TRUE(GetDisplayIdFromEDID(
- kInternalDisplay, charsize(kInternalDisplay), 0, &id));
- EXPECT_NE(-1, id);
-}
-
-TEST(OutputUtilTest, GetDisplayIdFailure) {
- int64 id = -1;
- EXPECT_FALSE(GetDisplayIdFromEDID(NULL, 0, 0, &id));
- EXPECT_EQ(-1, id);
-}
-
} // namespace chromeos
diff --git a/chromeos/display/real_output_configurator_delegate.cc b/chromeos/display/real_output_configurator_delegate.cc
index 43b4cdb..fd7d7b0 100644
--- a/chromeos/display/real_output_configurator_delegate.cc
+++ b/chromeos/display/real_output_configurator_delegate.cc
@@ -17,6 +17,7 @@
#include "base/logging.h"
#include "base/message_loop/message_pump_x11.h"
+#include "base/x11/edid_parser_x11.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/power_manager_client.h"
#include "chromeos/display/output_util.h"
@@ -263,7 +264,7 @@ RealOutputConfiguratorDelegate::InitOutputSnapshot(
output.output = id;
output.width_mm = info->mm_width;
output.height_mm = info->mm_height;
- output.has_display_id = GetDisplayId(id, index, &output.display_id);
+ output.has_display_id = base::GetDisplayId(id, index, &output.display_id);
output.is_internal = IsInternalOutput(info);
output.index = index;
diff --git a/ui/views/views.gyp b/ui/views/views.gyp
index 8ba7958..30dd637 100644
--- a/ui/views/views.gyp
+++ b/ui/views/views.gyp
@@ -388,6 +388,7 @@
'widget/desktop_aura/desktop_screen_win.cc',
'widget/desktop_aura/desktop_screen_win.h',
'widget/desktop_aura/desktop_screen_x11.cc',
+ 'widget/desktop_aura/desktop_screen_x11.h',
'widget/desktop_aura/scoped_tooltip_client.cc',
'widget/desktop_aura/scoped_tooltip_client.h',
'widget/desktop_aura/x11_desktop_handler.cc',
@@ -750,6 +751,7 @@
'view_model_utils_unittest.cc',
'view_unittest.cc',
'widget/desktop_aura/desktop_native_widget_aura_unittest.cc',
+ 'widget/desktop_aura/desktop_screen_x11_unittest.cc',
'widget/desktop_aura/desktop_screen_position_client_unittest.cc',
'widget/native_widget_aura_unittest.cc',
'widget/native_widget_unittest.cc',
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.cc b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
index 880b3d0..821ae45 100644
--- a/ui/views/widget/desktop_aura/desktop_screen_x11.cc
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.cc
@@ -2,68 +2,125 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "ui/views/widget/desktop_aura/desktop_screen.h"
+#include "ui/views/widget/desktop_aura/desktop_screen_x11.h"
+#include <X11/extensions/Xrandr.h>
#include <X11/Xlib.h>
// It clashes with out RootWindow.
#undef RootWindow
#include "base/logging.h"
+#include "base/x11/edid_parser_x11.h"
#include "ui/aura/root_window.h"
#include "ui/aura/root_window_host.h"
+#include "ui/base/x/x11_util.h"
#include "ui/gfx/display.h"
+#include "ui/gfx/display_observer.h"
#include "ui/gfx/native_widget_types.h"
#include "ui/gfx/screen.h"
#include "ui/gfx/x/x11_types.h"
+#include "ui/views/widget/desktop_aura/desktop_screen.h"
namespace {
-// TODO(erg): This method is a temporary hack, until we can reliably extract
-// location data out of XRandR.
-gfx::Size GetPrimaryDisplaySize() {
+// The delay to perform configuration after RRNotify. See the comment
+// in |Dispatch()|.
+const int64 kConfigureDelayMs = 500;
+
+std::vector<gfx::Display> GetFallbackDisplayList() {
::XDisplay* display = gfx::GetXDisplay();
::Screen* screen = DefaultScreenOfDisplay(display);
int width = WidthOfScreen(screen);
int height = HeightOfScreen(screen);
- return gfx::Size(width, height);
-}
-
-class DesktopScreenX11 : public gfx::Screen {
- public:
- DesktopScreenX11();
- virtual ~DesktopScreenX11();
-
- // Overridden from gfx::Screen:
- virtual bool IsDIPEnabled() OVERRIDE;
- virtual gfx::Point GetCursorScreenPoint() OVERRIDE;
- virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE;
- virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
- OVERRIDE;
- virtual int GetNumDisplays() const OVERRIDE;
- virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE;
- virtual gfx::Display GetDisplayNearestWindow(
- gfx::NativeView window) const OVERRIDE;
- virtual gfx::Display GetDisplayNearestPoint(
- const gfx::Point& point) const OVERRIDE;
- virtual gfx::Display GetDisplayMatching(
- const gfx::Rect& match_rect) const OVERRIDE;
- virtual gfx::Display GetPrimaryDisplay() const OVERRIDE;
- virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE;
- virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE;
-
- private:
- DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11);
-};
+ return std::vector<gfx::Display>(
+ 1, gfx::Display(0, gfx::Rect(0, 0, width, height)));
+}
+
+} // namespace
+
+namespace views {
////////////////////////////////////////////////////////////////////////////////
// DesktopScreenX11, public:
-DesktopScreenX11::DesktopScreenX11() {
+DesktopScreenX11::DesktopScreenX11()
+ : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
+ x_root_window_(DefaultRootWindow(xdisplay_)),
+ has_xrandr_(false),
+ xrandr_event_base_(0) {
+ // We only support 1.3+. There were library changes before this and we should
+ // use the new interface instead of the 1.2 one.
+ int randr_version_major = 0;
+ int randr_version_minor = 0;
+ has_xrandr_ = XRRQueryVersion(
+ xdisplay_, &randr_version_major, &randr_version_minor) &&
+ randr_version_major == 1 &&
+ randr_version_minor >= 3;
+
+ if (has_xrandr_) {
+ int error_base_ignored = 0;
+ XRRQueryExtension(xdisplay_, &xrandr_event_base_, &error_base_ignored);
+
+ base::MessagePumpX11::Current()->AddDispatcherForRootWindow(this);
+ XRRSelectInput(xdisplay_,
+ x_root_window_,
+ RRScreenChangeNotifyMask | RROutputChangeNotifyMask);
+
+ displays_ = BuildDisplaysFromXRandRInfo();
+ } else {
+ displays_ = GetFallbackDisplayList();
+ }
}
DesktopScreenX11::~DesktopScreenX11() {
+ if (has_xrandr_)
+ base::MessagePumpX11::Current()->RemoveDispatcherForRootWindow(this);
+}
+
+void DesktopScreenX11::ProcessDisplayChange(
+ const std::vector<gfx::Display>& incoming) {
+ std::vector<gfx::Display>::const_iterator cur_it = displays_.begin();
+ for (; cur_it != displays_.end(); ++cur_it) {
+ bool found = false;
+ for (std::vector<gfx::Display>::const_iterator incoming_it =
+ incoming.begin(); incoming_it != incoming.end(); ++incoming_it) {
+ if (cur_it->id() == incoming_it->id()) {
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
+ OnDisplayRemoved(*cur_it));
+ }
+ }
+
+ std::vector<gfx::Display>::const_iterator incoming_it = incoming.begin();
+ for (; incoming_it != incoming.end(); ++incoming_it) {
+ bool found = false;
+ for (std::vector<gfx::Display>::const_iterator cur_it = displays_.begin();
+ cur_it != displays_.end(); ++cur_it) {
+ if (incoming_it->id() == cur_it->id()) {
+ if (incoming_it->bounds() != cur_it->bounds()) {
+ FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
+ OnDisplayBoundsChanged(*incoming_it));
+ }
+
+ found = true;
+ break;
+ }
+ }
+
+ if (!found) {
+ FOR_EACH_OBSERVER(gfx::DisplayObserver, observer_list_,
+ OnDisplayAdded(*incoming_it));
+ }
+ }
+
+ displays_ = incoming;
}
////////////////////////////////////////////////////////////////////////////////
@@ -93,65 +150,187 @@ gfx::Point DesktopScreenX11::GetCursorScreenPoint() {
}
gfx::NativeWindow DesktopScreenX11::GetWindowUnderCursor() {
- // TODO(erg): Implement using the discussion at
- // http://codereview.chromium.org/10279005/
- return NULL;
+ return GetWindowAtScreenPoint(GetCursorScreenPoint());
}
gfx::NativeWindow DesktopScreenX11::GetWindowAtScreenPoint(
const gfx::Point& point) {
+ // TODO(erg): Implement using the discussion at
+ // http://codereview.chromium.org/10279005/
NOTIMPLEMENTED();
return NULL;
}
int DesktopScreenX11::GetNumDisplays() const {
- // TODO(erg): Figure this out with oshima or piman because I have no clue
- // about the XRandR implications here.
- return 1;
+ return displays_.size();
}
std::vector<gfx::Display> DesktopScreenX11::GetAllDisplays() const {
- // TODO(erg): Do the right thing once we know what that is.
- return std::vector<gfx::Display>(1, GetPrimaryDisplay());
+ return displays_;
}
gfx::Display DesktopScreenX11::GetDisplayNearestWindow(
gfx::NativeView window) const {
- // TODO(erg): Do the right thing once we know what that is.
- return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize()));
+ // TODO(erg): This should theoretically be easy, but it isn't. At the time we
+ // get called here, our aura::Window has not been Init()ed, because this
+ // method is called to get the device scale factor as part of
+ // RootWindow::Init(), before Window::Init(). This seems very confused; we're
+ // trying to get a display nearest window even before we've allocated the
+ // root window. Once fixed, the correct implementation should probably be:
+ //
+ // return GetDisplayMatching(window->GetBoundsInScreen());
+ //
+ // But at least for now, we'll just fallback:
+ return GetPrimaryDisplay();
}
gfx::Display DesktopScreenX11::GetDisplayNearestPoint(
const gfx::Point& point) const {
- // TODO(erg): Do the right thing once we know what that is.
- return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize()));
+ for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
+ it != displays_.end(); ++it) {
+ if (it->bounds().Contains(point))
+ return *it;
+ }
+
+ return GetPrimaryDisplay();
}
gfx::Display DesktopScreenX11::GetDisplayMatching(
const gfx::Rect& match_rect) const {
- // TODO(erg): Do the right thing once we know what that is.
- return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize()));
+ int max_area = 0;
+ const gfx::Display* matching = NULL;
+ for (std::vector<gfx::Display>::const_iterator it = displays_.begin();
+ it != displays_.end(); ++it) {
+ gfx::Rect intersect = gfx::IntersectRects(it->bounds(), match_rect);
+ int area = intersect.width() * intersect.height();
+ if (area > max_area) {
+ max_area = area;
+ matching = &*it;
+ }
+ }
+ // Fallback to the primary display if there is no matching display.
+ return matching ? *matching : GetPrimaryDisplay();
}
gfx::Display DesktopScreenX11::GetPrimaryDisplay() const {
- // TODO(erg): Do the right thing once we know what that is.
- return gfx::Display(0, gfx::Rect(GetPrimaryDisplaySize()));
+ return displays_.front();
}
void DesktopScreenX11::AddObserver(gfx::DisplayObserver* observer) {
- // TODO(erg|oshima): Do the right thing once we know what that is.
- // crbug.com/122863
+ observer_list_.AddObserver(observer);
}
+
void DesktopScreenX11::RemoveObserver(gfx::DisplayObserver* observer) {
- // TODO(erg|oshima): Do the right thing once we know what that is.
- // crbug.com/122863
+ observer_list_.RemoveObserver(observer);
}
-} // namespace
+bool DesktopScreenX11::Dispatch(const base::NativeEvent& event) {
+ if (event->type - xrandr_event_base_ == RRScreenChangeNotify) {
+ // Pass the event through to xlib.
+ XRRUpdateConfiguration(event);
+ } else if (event->type - xrandr_event_base_ == RRNotify) {
+ // There's some sort of observer dispatch going on here, but I don't think
+ // it's the screen's?
+ DLOG(ERROR) << "DesktopScreenX11::Dispatch() -> RRNotify";
+
+ if (configure_timer_.get()) {
+ configure_timer_->Reset();
+ } else {
+ configure_timer_.reset(new base::OneShotTimer<DesktopScreenX11>());
+ configure_timer_->Start(
+ FROM_HERE,
+ base::TimeDelta::FromMilliseconds(kConfigureDelayMs),
+ this,
+ &DesktopScreenX11::ConfigureTimerFired);
+ }
+ }
+
+ return true;
+}
////////////////////////////////////////////////////////////////////////////////
+// DesktopScreenX11, private:
+
+DesktopScreenX11::DesktopScreenX11(
+ const std::vector<gfx::Display>& test_displays)
+ : xdisplay_(base::MessagePumpX11::GetDefaultXDisplay()),
+ x_root_window_(DefaultRootWindow(xdisplay_)),
+ has_xrandr_(false),
+ xrandr_event_base_(0),
+ displays_(test_displays) {
+}
-namespace views {
+std::vector<gfx::Display> DesktopScreenX11::BuildDisplaysFromXRandRInfo() {
+ std::vector<gfx::Display> displays;
+ XRRScreenResources* resources =
+ XRRGetScreenResourcesCurrent(xdisplay_, x_root_window_);
+ if (!resources) {
+ LOG(ERROR) << "XRandR returned no displays. Falling back to Root Window.";
+ return GetFallbackDisplayList();
+ }
+
+ bool has_work_area = false;
+ gfx::Rect work_area;
+ std::vector<int> value;
+ if (ui::GetIntArrayProperty(x_root_window_, "_NET_WORKAREA", &value) &&
+ value.size() >= 4) {
+ work_area = gfx::Rect(value[0], value[1], value[2], value[3]);
+ has_work_area = true;
+ }
+
+ for (int i = 0; i < resources->noutput; ++i) {
+ RROutput output_id = resources->outputs[i];
+ XRROutputInfo* output_info =
+ XRRGetOutputInfo(xdisplay_, resources, output_id);
+
+ bool is_connected = (output_info->connection == RR_Connected);
+ if (!is_connected) {
+ XRRFreeOutputInfo(output_info);
+ continue;
+ }
+
+ if (output_info->crtc) {
+ XRRCrtcInfo *crtc = XRRGetCrtcInfo(xdisplay_,
+ resources,
+ output_info->crtc);
+
+ int64 display_id = -1;
+ if (!base::GetDisplayId(output_id, i, &display_id)) {
+ // It isn't ideal, but if we can't parse the EDID data, fallback on the
+ // display number.
+ display_id = i;
+ }
+
+ gfx::Rect crtc_bounds(crtc->x, crtc->y, crtc->width, crtc->height);
+ gfx::Display display(display_id, crtc_bounds);
+ if (has_work_area) {
+ gfx::Rect intersection = crtc_bounds;
+ intersection.Intersect(work_area);
+ display.set_work_area(intersection);
+ }
+
+ displays.push_back(display);
+
+ XRRFreeCrtcInfo(crtc);
+ }
+
+ XRRFreeOutputInfo(output_info);
+ }
+
+ XRRFreeScreenResources(resources);
+
+ if (displays.empty())
+ return GetFallbackDisplayList();
+
+ return displays;
+}
+
+void DesktopScreenX11::ConfigureTimerFired() {
+ std::vector<gfx::Display> new_displays = BuildDisplaysFromXRandRInfo();
+ ProcessDisplayChange(new_displays);
+}
+
+////////////////////////////////////////////////////////////////////////////////
gfx::Screen* CreateDesktopScreen() {
return new DesktopScreenX11;
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11.h b/ui/views/widget/desktop_aura/desktop_screen_x11.h
new file mode 100644
index 0000000..1b9a832
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11.h
@@ -0,0 +1,95 @@
+// 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 UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_X11_H_
+#define UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_X11_H_
+
+#include "base/message_loop/message_pump_dispatcher.h"
+#include "base/observer_list.h"
+#include "base/timer/timer.h"
+#include "ui/gfx/screen.h"
+#include "ui/views/views_export.h"
+
+namespace gfx {
+class Display;
+}
+
+typedef unsigned long XID;
+typedef XID Window;
+typedef struct _XDisplay Display;
+
+namespace views {
+class DesktopScreenX11Test;
+
+// Our singleton screen implementation that talks to xrandr.
+class VIEWS_EXPORT DesktopScreenX11 : public gfx::Screen,
+ public base::MessagePumpDispatcher {
+ public:
+ DesktopScreenX11();
+
+ virtual ~DesktopScreenX11();
+
+ // Takes a set of displays and dispatches the screen change events to
+ // listeners. Exposed for testing.
+ void ProcessDisplayChange(const std::vector<gfx::Display>& displays);
+
+ // Overridden from gfx::Screen:
+ virtual bool IsDIPEnabled() OVERRIDE;
+ virtual gfx::Point GetCursorScreenPoint() OVERRIDE;
+ virtual gfx::NativeWindow GetWindowUnderCursor() OVERRIDE;
+ virtual gfx::NativeWindow GetWindowAtScreenPoint(const gfx::Point& point)
+ OVERRIDE;
+ virtual int GetNumDisplays() const OVERRIDE;
+ virtual std::vector<gfx::Display> GetAllDisplays() const OVERRIDE;
+ virtual gfx::Display GetDisplayNearestWindow(
+ gfx::NativeView window) const OVERRIDE;
+ virtual gfx::Display GetDisplayNearestPoint(
+ const gfx::Point& point) const OVERRIDE;
+ virtual gfx::Display GetDisplayMatching(
+ const gfx::Rect& match_rect) const OVERRIDE;
+ virtual gfx::Display GetPrimaryDisplay() const OVERRIDE;
+ virtual void AddObserver(gfx::DisplayObserver* observer) OVERRIDE;
+ virtual void RemoveObserver(gfx::DisplayObserver* observer) OVERRIDE;
+
+ // Overridden from MessagePumpDispatcher:
+ virtual bool Dispatch(const base::NativeEvent& event) OVERRIDE;
+
+ private:
+ friend class DesktopScreenX11Test;
+
+ // Constructor used in tests.
+ DesktopScreenX11(const std::vector<gfx::Display>& test_displays);
+
+ // Builds a list of displays from the current screen information offered by
+ // the X server.
+ std::vector<gfx::Display> BuildDisplaysFromXRandRInfo();
+
+ // We delay updating the display so we can coalesce events.
+ void ConfigureTimerFired();
+
+ Display* xdisplay_;
+ ::Window x_root_window_;
+
+ // Whether the x server supports the XRandR extension.
+ bool has_xrandr_;
+
+ // The base of the event numbers used to represent XRandr events used in
+ // decoding events regarding output add/remove.
+ int xrandr_event_base_;
+
+ // The display objects we present to chrome.
+ std::vector<gfx::Display> displays_;
+
+ // The timer to delay configuring outputs. See also the comments in
+ // Dispatch().
+ scoped_ptr<base::OneShotTimer<DesktopScreenX11> > configure_timer_;
+
+ ObserverList<gfx::DisplayObserver> observer_list_;
+
+ DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11);
+};
+
+} // namespace views
+
+#endif // UI_VIEWS_WIDGET_DESKTOP_AURA_DESKTOP_SCREEN_X11_H_
diff --git a/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc b/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc
new file mode 100644
index 0000000..45a2edf
--- /dev/null
+++ b/ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc
@@ -0,0 +1,186 @@
+// 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 "ui/views/widget/desktop_aura/desktop_screen_x11.h"
+
+#include "base/message_loop/message_loop.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "ui/gfx/display_observer.h"
+
+namespace views {
+
+const int64 kFirstDisplay = 5321829;
+const int64 kSecondDisplay = 928310;
+
+class DesktopScreenX11Test : public testing::Test,
+ public gfx::DisplayObserver {
+ public:
+ DesktopScreenX11Test() {}
+ virtual ~DesktopScreenX11Test() {}
+
+ // Overridden from testing::Test:
+ virtual void SetUp() OVERRIDE {
+ // Initialize the world to the single monitor case.
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
+ screen_.reset(new DesktopScreenX11(displays));
+ screen_->AddObserver(this);
+ }
+ virtual void TearDown() OVERRIDE {
+ screen_.reset();
+ message_loop_.RunUntilIdle();
+ }
+
+ protected:
+ std::vector<gfx::Display> changed_display_;
+ std::vector<gfx::Display> added_display_;
+ std::vector<gfx::Display> removed_display_;
+
+ DesktopScreenX11* screen() { return screen_.get(); }
+
+ void ResetDisplayChanges() {
+ changed_display_.clear();
+ added_display_.clear();
+ removed_display_.clear();
+ }
+
+ private:
+ // Overridden from gfx::DisplayObserver:
+ virtual void OnDisplayBoundsChanged(const gfx::Display& display) OVERRIDE {
+ changed_display_.push_back(display);
+ }
+
+ virtual void OnDisplayAdded(const gfx::Display& new_display) OVERRIDE {
+ added_display_.push_back(new_display);
+ }
+
+ virtual void OnDisplayRemoved(const gfx::Display& old_display) OVERRIDE {
+ removed_display_.push_back(old_display);
+ }
+
+ base::MessageLoopForUI message_loop_;
+ scoped_ptr<DesktopScreenX11> screen_;
+
+ DISALLOW_COPY_AND_ASSIGN(DesktopScreenX11Test);
+};
+
+TEST_F(DesktopScreenX11Test, BoundsChangeSingleMonitor) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 1024, 768)));
+ screen()->ProcessDisplayChange(displays);
+
+ EXPECT_EQ(1u, changed_display_.size());
+ EXPECT_EQ(0u, added_display_.size());
+ EXPECT_EQ(0u, removed_display_.size());
+}
+
+TEST_F(DesktopScreenX11Test, AddMonitorToTheRight) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
+ displays.push_back(gfx::Display(kSecondDisplay,
+ gfx::Rect(640, 0, 1024, 768)));
+ screen()->ProcessDisplayChange(displays);
+
+ EXPECT_EQ(0u, changed_display_.size());
+ EXPECT_EQ(1u, added_display_.size());
+ EXPECT_EQ(0u, removed_display_.size());
+}
+
+TEST_F(DesktopScreenX11Test, AddMonitorToTheLeft) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 1024, 768)));
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(1024, 0, 640, 480)));
+ screen()->ProcessDisplayChange(displays);
+
+ EXPECT_EQ(1u, changed_display_.size());
+ EXPECT_EQ(1u, added_display_.size());
+ EXPECT_EQ(0u, removed_display_.size());
+}
+
+TEST_F(DesktopScreenX11Test, RemoveMonitorOnRight) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
+ displays.push_back(gfx::Display(kSecondDisplay,
+ gfx::Rect(640, 0, 1024, 768)));
+ screen()->ProcessDisplayChange(displays);
+
+ ResetDisplayChanges();
+
+ displays.clear();
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
+ screen()->ProcessDisplayChange(displays);
+
+ EXPECT_EQ(0u, changed_display_.size());
+ EXPECT_EQ(0u, added_display_.size());
+ EXPECT_EQ(1u, removed_display_.size());
+}
+
+TEST_F(DesktopScreenX11Test, RemoveMonitorOnLeft) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
+ displays.push_back(gfx::Display(kSecondDisplay,
+ gfx::Rect(640, 0, 1024, 768)));
+ screen()->ProcessDisplayChange(displays);
+
+ ResetDisplayChanges();
+
+ displays.clear();
+ displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 1024, 768)));
+ screen()->ProcessDisplayChange(displays);
+
+ EXPECT_EQ(1u, changed_display_.size());
+ EXPECT_EQ(0u, added_display_.size());
+ EXPECT_EQ(1u, removed_display_.size());
+}
+
+TEST_F(DesktopScreenX11Test, GetDisplayNearestPoint) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
+ displays.push_back(gfx::Display(kSecondDisplay,
+ gfx::Rect(640, 0, 1024, 768)));
+ screen()->ProcessDisplayChange(displays);
+
+ EXPECT_EQ(kSecondDisplay,
+ screen()->GetDisplayNearestPoint(gfx::Point(650, 10)).id());
+ EXPECT_EQ(kFirstDisplay,
+ screen()->GetDisplayNearestPoint(gfx::Point(10, 10)).id());
+ EXPECT_EQ(kFirstDisplay,
+ screen()->GetDisplayNearestPoint(gfx::Point(10000, 10000)).id());
+}
+
+TEST_F(DesktopScreenX11Test, GetDisplayMatchingBasic) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
+ displays.push_back(gfx::Display(kSecondDisplay,
+ gfx::Rect(640, 0, 1024, 768)));
+ screen()->ProcessDisplayChange(displays);
+
+ EXPECT_EQ(kSecondDisplay,
+ screen()->GetDisplayMatching(gfx::Rect(700, 20, 100, 100)).id());
+}
+
+TEST_F(DesktopScreenX11Test, GetDisplayMatchingOverlap) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay, gfx::Rect(0, 0, 640, 480)));
+ displays.push_back(gfx::Display(kSecondDisplay,
+ gfx::Rect(640, 0, 1024, 768)));
+ screen()->ProcessDisplayChange(displays);
+
+ EXPECT_EQ(kSecondDisplay,
+ screen()->GetDisplayMatching(gfx::Rect(630, 20, 100, 100)).id());
+}
+
+TEST_F(DesktopScreenX11Test, GetPrimaryDisplay) {
+ std::vector<gfx::Display> displays;
+ displays.push_back(gfx::Display(kFirstDisplay,
+ gfx::Rect(640, 0, 1024, 768)));
+ displays.push_back(gfx::Display(kSecondDisplay, gfx::Rect(0, 0, 640, 480)));
+ screen()->ProcessDisplayChange(displays);
+
+ // The first display in the list is always the primary, even if other
+ // displays are to the left in screen layout.
+ EXPECT_EQ(kFirstDisplay, screen()->GetPrimaryDisplay().id());
+}
+
+} // namespace views