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 "chromeos/display/output_util.h"
8 #include <X11/extensions/Xrandr.h>
11 #include "base/hash.h"
12 #include "base/message_loop.h"
13 #include "base/strings/string_util.h"
14 #include "base/sys_byteorder.h"
19 // Prefixes for the built-in displays.
20 const char kInternal_LVDS
[] = "LVDS";
21 const char kInternal_eDP
[] = "eDP";
23 // Returns 64-bit persistent ID for the specified manufacturer's ID and
24 // product_code_hash, and the index of the output it is connected to.
25 // |output_index| is used to distinguish the displays of the same type. For
26 // example, swapping two identical display between two outputs will not be
27 // treated as swap. The 'serial number' field in EDID isn't used here because
28 // it is not guaranteed to have unique number and it may have the same fixed
30 int64
GetID(uint16 manufacturer_id
,
31 uint32 product_code_hash
,
33 return ((static_cast<int64
>(manufacturer_id
) << 40) |
34 (static_cast<int64
>(product_code_hash
) << 8) | output_index
);
37 bool IsRandRAvailable() {
38 int randr_version_major
= 0;
39 int randr_version_minor
= 0;
40 static bool is_randr_available
= XRRQueryVersion(
41 base::MessagePumpAuraX11::GetDefaultXDisplay(),
42 &randr_version_major
, &randr_version_minor
);
43 return is_randr_available
;
46 // Get the EDID data from the |output| and stores to |prop|. |nitem| will store
47 // the number of characters |prop| will have. It doesn't take the ownership of
48 // |prop|, so caller must release it by XFree().
49 // Returns true if EDID property is successfully obtained. Otherwise returns
50 // false and does not touch |prop| and |nitems|.
51 bool GetEDIDProperty(XID output
, unsigned long* nitems
, unsigned char** prop
) {
52 if (!IsRandRAvailable())
55 Display
* display
= base::MessagePumpAuraX11::GetDefaultXDisplay();
57 static Atom edid_property
= XInternAtom(
58 base::MessagePumpAuraX11::GetDefaultXDisplay(),
59 RR_PROPERTY_RANDR_EDID
, false);
61 bool has_edid_property
= false;
62 int num_properties
= 0;
63 Atom
* properties
= XRRListOutputProperties(display
, output
, &num_properties
);
64 for (int i
= 0; i
< num_properties
; ++i
) {
65 if (properties
[i
] == edid_property
) {
66 has_edid_property
= true;
71 if (!has_edid_property
)
76 unsigned long bytes_after
;
77 XRRGetOutputProperty(display
,
84 AnyPropertyType
, // req_type
90 DCHECK_EQ(XA_INTEGER
, actual_type
);
91 DCHECK_EQ(8, actual_format
);
95 // Gets some useful data from the specified output device, such like
96 // manufacturer's ID, product code, and human readable name. Returns false if it
97 // fails to get those data and doesn't touch manufacturer ID/product code/name.
98 // NULL can be passed for unwanted output parameters.
99 bool GetOutputDeviceData(XID output
,
100 uint16
* manufacturer_id
,
101 std::string
* human_readable_name
) {
102 unsigned long nitems
= 0;
103 unsigned char *prop
= NULL
;
104 if (!GetEDIDProperty(output
, &nitems
, &prop
))
107 bool result
= ParseOutputDeviceData(
108 prop
, nitems
, manufacturer_id
, human_readable_name
);
115 std::string
GetDisplayName(XID output_id
) {
116 std::string display_name
;
117 GetOutputDeviceData(output_id
, NULL
, &display_name
);
121 bool GetDisplayId(XID output_id
, size_t output_index
, int64
* display_id_out
) {
122 unsigned long nitems
= 0;
123 unsigned char* prop
= NULL
;
124 if (!GetEDIDProperty(output_id
, &nitems
, &prop
))
128 GetDisplayIdFromEDID(prop
, nitems
, output_index
, display_id_out
);
133 bool GetDisplayIdFromEDID(const unsigned char* prop
,
134 unsigned long nitems
,
136 int64
* display_id_out
) {
137 uint16 manufacturer_id
= 0;
138 std::string product_name
;
140 // ParseOutputDeviceData fails if it doesn't have product_name.
141 ParseOutputDeviceData(prop
, nitems
, &manufacturer_id
, &product_name
);
143 // Generates product specific value from product_name instead of product code.
144 // See crbug.com/240341
145 uint32 product_code_hash
= product_name
.empty() ?
146 0 : base::Hash(product_name
);
147 if (manufacturer_id
!= 0) {
148 // An ID based on display's index will be assigned later if this call
150 *display_id_out
= GetID(
151 manufacturer_id
, product_code_hash
, output_index
);
157 bool ParseOutputDeviceData(const unsigned char* prop
,
158 unsigned long nitems
,
159 uint16
* manufacturer_id
,
160 std::string
* human_readable_name
) {
161 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
162 // for the details of EDID data format. We use the following data:
163 // bytes 8-9: manufacturer EISA ID, in big-endian
164 // bytes 54-125: four descriptors (18-bytes each) which may contain
166 const unsigned int kManufacturerOffset
= 8;
167 const unsigned int kManufacturerLength
= 2;
168 const unsigned int kDescriptorOffset
= 54;
169 const unsigned int kNumDescriptors
= 4;
170 const unsigned int kDescriptorLength
= 18;
171 // The specifier types.
172 const unsigned char kMonitorNameDescriptor
= 0xfc;
174 if (manufacturer_id
) {
175 if (nitems
< kManufacturerOffset
+ kManufacturerLength
) {
176 LOG(ERROR
) << "too short EDID data: manifacturer id";
181 *reinterpret_cast<const uint16
*>(prop
+ kManufacturerOffset
);
182 #if defined(ARCH_CPU_LITTLE_ENDIAN)
183 *manufacturer_id
= base::ByteSwap(*manufacturer_id
);
187 if (!human_readable_name
)
190 human_readable_name
->clear();
191 for (unsigned int i
= 0; i
< kNumDescriptors
; ++i
) {
192 if (nitems
< kDescriptorOffset
+ (i
+ 1) * kDescriptorLength
)
195 const unsigned char* desc_buf
=
196 prop
+ kDescriptorOffset
+ i
* kDescriptorLength
;
197 // If the descriptor contains the display name, it has the following
200 // byte 3: descriptor type, defined above.
201 // bytes 5-17: text data, ending with \r, padding with spaces
202 // we should check bytes 0-2 and 4, since it may have other values in
203 // case that the descriptor contains other type of data.
204 if (desc_buf
[0] == 0 && desc_buf
[1] == 0 && desc_buf
[2] == 0 &&
206 if (desc_buf
[3] == kMonitorNameDescriptor
) {
207 std::string
found_name(
208 reinterpret_cast<const char*>(desc_buf
+ 5), kDescriptorLength
- 5);
209 TrimWhitespaceASCII(found_name
, TRIM_TRAILING
, human_readable_name
);
215 // Verify if the |human_readable_name| consists of printable characters only.
216 for (size_t i
= 0; i
< human_readable_name
->size(); ++i
) {
217 char c
= (*human_readable_name
)[i
];
218 if (!isascii(c
) || !isprint(c
)) {
219 human_readable_name
->clear();
220 LOG(ERROR
) << "invalid EDID: human unreadable char in name";
228 bool GetOutputOverscanFlag(XID output
, bool* flag
) {
229 unsigned long nitems
= 0;
230 unsigned char *prop
= NULL
;
231 if (!GetEDIDProperty(output
, &nitems
, &prop
))
234 bool found
= ParseOutputOverscanFlag(prop
, nitems
, flag
);
239 bool ParseOutputOverscanFlag(const unsigned char* prop
,
240 unsigned long nitems
,
242 // See http://en.wikipedia.org/wiki/Extended_display_identification_data
243 // for the extension format of EDID. Also see EIA/CEA-861 spec for
244 // the format of the extensions and how video capability is encoded.
245 // - byte 0: tag. should be 02h.
246 // - byte 1: revision. only cares revision 3 (03h).
247 // - byte 4-: data block.
248 const unsigned int kExtensionBase
= 128;
249 const unsigned int kExtensionSize
= 128;
250 const unsigned int kNumExtensionsOffset
= 126;
251 const unsigned int kDataBlockOffset
= 4;
252 const unsigned char kCEAExtensionTag
= '\x02';
253 const unsigned char kExpectedExtensionRevision
= '\x03';
254 const unsigned char kExtendedTag
= 7;
255 const unsigned char kExtendedVideoCapabilityTag
= 0;
256 const unsigned int kPTOverscan
= 4;
257 const unsigned int kITOverscan
= 2;
258 const unsigned int kCEOverscan
= 0;
260 if (nitems
<= kNumExtensionsOffset
)
263 unsigned char num_extensions
= prop
[kNumExtensionsOffset
];
265 for (size_t i
= 0; i
< num_extensions
; ++i
) {
266 // Skip parsing the whole extension if size is not enough.
267 if (nitems
< kExtensionBase
+ (i
+ 1) * kExtensionSize
)
270 const unsigned char* extension
= prop
+ kExtensionBase
+ i
* kExtensionSize
;
271 unsigned char tag
= extension
[0];
272 unsigned char revision
= extension
[1];
273 if (tag
!= kCEAExtensionTag
|| revision
!= kExpectedExtensionRevision
)
276 unsigned char timing_descriptors_start
=
277 std::min(extension
[2], static_cast<unsigned char>(kExtensionSize
));
278 const unsigned char* data_block
= extension
+ kDataBlockOffset
;
279 while (data_block
< extension
+ timing_descriptors_start
) {
280 // A data block is encoded as:
281 // - byte 1 high 3 bits: tag. '07' for extended tags.
282 // - byte 1 remaining bits: the length of data block.
283 // - byte 2: the extended tag. '0' for video capability.
284 // - byte 3: the capability.
285 unsigned char tag
= data_block
[0] >> 5;
286 unsigned char payload_length
= data_block
[0] & 0x1f;
287 if (static_cast<unsigned long>(data_block
+ payload_length
- prop
) >
291 if (tag
!= kExtendedTag
|| payload_length
< 2) {
292 data_block
+= payload_length
+ 1;
296 unsigned char extended_tag_code
= data_block
[1];
297 if (extended_tag_code
!= kExtendedVideoCapabilityTag
) {
298 data_block
+= payload_length
+ 1;
302 // The difference between preferred, IT, and CE video formats
303 // doesn't matter. Sets |flag| to true if any of these flags are true.
304 if ((data_block
[2] & (1 << kPTOverscan
)) ||
305 (data_block
[2] & (1 << kITOverscan
)) ||
306 (data_block
[2] & (1 << kCEOverscan
))) {
318 bool IsInternalOutputName(const std::string
& name
) {
319 return name
.find(kInternal_LVDS
) == 0 || name
.find(kInternal_eDP
) == 0;
322 } // namespace chromeos