diff options
author | erg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-25 21:32:21 +0000 |
---|---|---|
committer | erg@chromium.org <erg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-09-25 21:32:21 +0000 |
commit | c68f03565460122348c5a437b814f85f58ee04d5 (patch) | |
tree | e241a28fe68483d864277663c6dfd901a4ec9bab | |
parent | 769e94ab4f20554bd0ae0c2be5ad2654a62ce16b (diff) | |
download | chromium_src-c68f03565460122348c5a437b814f85f58ee04d5.zip chromium_src-c68f03565460122348c5a437b814f85f58ee04d5.tar.gz chromium_src-c68f03565460122348c5a437b814f85f58ee04d5.tar.bz2 |
Reland linux_aura: Implement most of DesktopScreenX11.
[[Making changes to '_toolset=="target"' section in system.gyp to fix
cros compiling.]]
The linux_aura port didn't deal with multiple monitors very well because
it was treating the X root window as one big display. When xrandr is
present, get the screen areas from it, and exposes this data back to
chrome.
This patch also factors out the EDID parser than chromeos was using into
a common directory. Like chromeos, we use it to assign stable display
IDs.
BUG=287972
TBR=brettw@chromium.org, derat@chromium.org, sky@chromium.org
First Review URL: https://codereview.chromium.org/23536057
Review URL: https://codereview.chromium.org/24459002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@225254 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | base/base.gyp | 13 | ||||
-rw-r--r-- | base/base.gypi | 6 | ||||
-rw-r--r-- | base/x11/edid_parser_x11.cc | 196 | ||||
-rw-r--r-- | base/x11/edid_parser_x11.h | 54 | ||||
-rw-r--r-- | base/x11/edid_parser_x11_unittest.cc | 167 | ||||
-rw-r--r-- | build/linux/system.gyp | 36 | ||||
-rw-r--r-- | chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.cc | 17 | ||||
-rw-r--r-- | chrome/browser/ui/aura/chrome_browser_main_extra_parts_aura.h | 1 | ||||
-rw-r--r-- | chromeos/display/output_util.cc | 191 | ||||
-rw-r--r-- | chromeos/display/output_util.h | 22 | ||||
-rw-r--r-- | chromeos/display/output_util_unittest.cc | 108 | ||||
-rw-r--r-- | chromeos/display/real_output_configurator_delegate.cc | 3 | ||||
-rw-r--r-- | ui/views/views.gyp | 2 | ||||
-rw-r--r-- | ui/views/widget/desktop_aura/desktop_screen_x11.cc | 291 | ||||
-rw-r--r-- | ui/views/widget/desktop_aura/desktop_screen_x11.h | 95 | ||||
-rw-r--r-- | ui/views/widget/desktop_aura/desktop_screen_x11_unittest.cc | 186 |
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 |