diff options
-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 |