1 // Copyright (c) 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "base/x11/edid_parser_x11.h"
7 #include <X11/extensions/Xrandr.h>
11 #include "base/hash.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/strings/string_util.h"
14 #include "base/sys_byteorder.h"
18 // Returns 64-bit persistent ID for the specified manufacturer's ID and
19 // product_code_hash, and the index of the output it is connected to.
20 // |output_index| is used to distinguish the displays of the same type. For
21 // example, swapping two identical display between two outputs will not be
22 // treated as swap. The 'serial number' field in EDID isn't used here because
23 // it is not guaranteed to have unique number and it may have the same fixed
25 int64
GetID(uint16 manufacturer_id
,
26 uint32 product_code_hash
,
28 return ((static_cast<int64
>(manufacturer_id
) << 40) |
29 (static_cast<int64
>(product_code_hash
) << 8) | output_index
);
32 bool IsRandRAvailable() {
33 int randr_version_major
= 0;
34 int randr_version_minor
= 0;
35 static bool is_randr_available
= XRRQueryVersion(
36 base::MessagePumpX11::GetDefaultXDisplay(),
37 &randr_version_major
, &randr_version_minor
);
38 return is_randr_available
;
45 bool GetEDIDProperty(XID output
, unsigned long* nitems
, unsigned char** prop
) {
46 if (!IsRandRAvailable())
49 Display
* display
= base::MessagePumpX11::GetDefaultXDisplay();
51 static Atom edid_property
= XInternAtom(
52 base::MessagePumpX11::GetDefaultXDisplay(),
53 RR_PROPERTY_RANDR_EDID
, false);
55 bool has_edid_property
= false;
56 int num_properties
= 0;
57 Atom
* properties
= XRRListOutputProperties(display
, output
, &num_properties
);
58 for (int i
= 0; i
< num_properties
; ++i
) {
59 if (properties
[i
] == edid_property
) {
60 has_edid_property
= true;
65 if (!has_edid_property
)
70 unsigned long bytes_after
;
71 XRRGetOutputProperty(display
,
78 AnyPropertyType
, // req_type
84 DCHECK_EQ(XA_INTEGER
, actual_type
);
85 DCHECK_EQ(8, actual_format
);
89 bool GetDisplayId(XID output_id
, size_t output_index
, int64
* display_id_out
) {
90 unsigned long nitems
= 0;
91 unsigned char* prop
= NULL
;
92 if (!GetEDIDProperty(output_id
, &nitems
, &prop
))
96 GetDisplayIdFromEDID(prop
, nitems
, output_index
, display_id_out
);
101 bool GetDisplayIdFromEDID(const unsigned char* prop
,
102 unsigned long nitems
,
104 int64
* display_id_out
) {
105 uint16 manufacturer_id
= 0;
106 std::string product_name
;
108 // ParseOutputDeviceData fails if it doesn't have product_name.
109 ParseOutputDeviceData(prop
, nitems
, &manufacturer_id
, &product_name
);
111 // Generates product specific value from product_name instead of product code.
112 // See crbug.com/240341
113 uint32 product_code_hash
= product_name
.empty() ?
114 0 : base::Hash(product_name
);
115 if (manufacturer_id
!= 0) {
116 // An ID based on display's index will be assigned later if this call
118 *display_id_out
= GetID(
119 manufacturer_id
, product_code_hash
, output_index
);
125 bool ParseOutputDeviceData(const unsigned char* prop
,
126 unsigned long nitems
,
127 uint16
* manufacturer_id
,
128 std::string
* human_readable_name
) {
129 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
130 // for the details of EDID data format. We use the following data:
131 // bytes 8-9: manufacturer EISA ID, in big-endian
132 // bytes 54-125: four descriptors (18-bytes each) which may contain
134 const unsigned int kManufacturerOffset
= 8;
135 const unsigned int kManufacturerLength
= 2;
136 const unsigned int kDescriptorOffset
= 54;
137 const unsigned int kNumDescriptors
= 4;
138 const unsigned int kDescriptorLength
= 18;
139 // The specifier types.
140 const unsigned char kMonitorNameDescriptor
= 0xfc;
142 if (manufacturer_id
) {
143 if (nitems
< kManufacturerOffset
+ kManufacturerLength
) {
144 LOG(ERROR
) << "too short EDID data: manifacturer id";
149 *reinterpret_cast<const uint16
*>(prop
+ kManufacturerOffset
);
150 #if defined(ARCH_CPU_LITTLE_ENDIAN)
151 *manufacturer_id
= base::ByteSwap(*manufacturer_id
);
155 if (!human_readable_name
)
158 human_readable_name
->clear();
159 for (unsigned int i
= 0; i
< kNumDescriptors
; ++i
) {
160 if (nitems
< kDescriptorOffset
+ (i
+ 1) * kDescriptorLength
)
163 const unsigned char* desc_buf
=
164 prop
+ kDescriptorOffset
+ i
* kDescriptorLength
;
165 // If the descriptor contains the display name, it has the following
168 // byte 3: descriptor type, defined above.
169 // bytes 5-17: text data, ending with \r, padding with spaces
170 // we should check bytes 0-2 and 4, since it may have other values in
171 // case that the descriptor contains other type of data.
172 if (desc_buf
[0] == 0 && desc_buf
[1] == 0 && desc_buf
[2] == 0 &&
174 if (desc_buf
[3] == kMonitorNameDescriptor
) {
175 std::string
found_name(
176 reinterpret_cast<const char*>(desc_buf
+ 5), kDescriptorLength
- 5);
177 TrimWhitespaceASCII(found_name
, TRIM_TRAILING
, human_readable_name
);
183 // Verify if the |human_readable_name| consists of printable characters only.
184 for (size_t i
= 0; i
< human_readable_name
->size(); ++i
) {
185 char c
= (*human_readable_name
)[i
];
186 if (!isascii(c
) || !isprint(c
)) {
187 human_readable_name
->clear();
188 LOG(ERROR
) << "invalid EDID: human unreadable char in name";