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. 4 5 #include "chromeos/display/output_util.h" 6 7 #include <X11/Xlib.h> 8 #include <X11/extensions/Xrandr.h> 9 #include <X11/Xatom.h> 10 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" 15 16 namespace chromeos { 17 namespace { 18 19 // Prefixes for the built-in displays. 20 const char kInternal_LVDS[] = "LVDS"; 21 const char kInternal_eDP[] = "eDP"; 22 const char kInternal_DSI[] = "DSI"; 23 24 // Returns 64-bit persistent ID for the specified manufacturer's ID and 25 // product_code_hash, and the index of the output it is connected to. 26 // |output_index| is used to distinguish the displays of the same type. For 27 // example, swapping two identical display between two outputs will not be 28 // treated as swap. The 'serial number' field in EDID isn't used here because 29 // it is not guaranteed to have unique number and it may have the same fixed 30 // value (like 0). 31 int64 GetID(uint16 manufacturer_id, 32 uint32 product_code_hash, 33 uint8 output_index) { 34 return ((static_cast<int64>(manufacturer_id) << 40) | 35 (static_cast<int64>(product_code_hash) << 8) | output_index); 36 } 37 38 bool IsRandRAvailable() { 39 int randr_version_major = 0; 40 int randr_version_minor = 0; 41 static bool is_randr_available = XRRQueryVersion( 42 base::MessagePumpAuraX11::GetDefaultXDisplay(), 43 &randr_version_major, &randr_version_minor); 44 return is_randr_available; 45 } 46 47 // Get the EDID data from the |output| and stores to |prop|. |nitem| will store 48 // the number of characters |prop| will have. It doesn't take the ownership of 49 // |prop|, so caller must release it by XFree(). 50 // Returns true if EDID property is successfully obtained. Otherwise returns 51 // false and does not touch |prop| and |nitems|. 52 bool GetEDIDProperty(XID output, unsigned long* nitems, unsigned char** prop) { 53 if (!IsRandRAvailable()) 54 return false; 55 56 Display* display = base::MessagePumpAuraX11::GetDefaultXDisplay(); 57 58 static Atom edid_property = XInternAtom( 59 base::MessagePumpAuraX11::GetDefaultXDisplay(), 60 RR_PROPERTY_RANDR_EDID, false); 61 62 bool has_edid_property = false; 63 int num_properties = 0; 64 Atom* properties = XRRListOutputProperties(display, output, &num_properties); 65 for (int i = 0; i < num_properties; ++i) { 66 if (properties[i] == edid_property) { 67 has_edid_property = true; 68 break; 69 } 70 } 71 XFree(properties); 72 if (!has_edid_property) 73 return false; 74 75 Atom actual_type; 76 int actual_format; 77 unsigned long bytes_after; 78 XRRGetOutputProperty(display, 79 output, 80 edid_property, 81 0, // offset 82 128, // length 83 false, // _delete 84 false, // pending 85 AnyPropertyType, // req_type 86 &actual_type, 87 &actual_format, 88 nitems, 89 &bytes_after, 90 prop); 91 DCHECK_EQ(XA_INTEGER, actual_type); 92 DCHECK_EQ(8, actual_format); 93 return true; 94 } 95 96 // Gets some useful data from the specified output device, such like 97 // manufacturer's ID, product code, and human readable name. Returns false if it 98 // fails to get those data and doesn't touch manufacturer ID/product code/name. 99 // NULL can be passed for unwanted output parameters. 100 bool GetOutputDeviceData(XID output, 101 uint16* manufacturer_id, 102 std::string* human_readable_name) { 103 unsigned long nitems = 0; 104 unsigned char *prop = NULL; 105 if (!GetEDIDProperty(output, &nitems, &prop)) 106 return false; 107 108 bool result = ParseOutputDeviceData( 109 prop, nitems, manufacturer_id, human_readable_name); 110 XFree(prop); 111 return result; 112 } 113 114 float GetRefreshRate(const XRRModeInfo* mode_info) { 115 if (mode_info->hTotal && mode_info->vTotal) { 116 return static_cast<float>(mode_info->dotClock) / 117 (static_cast<float>(mode_info->hTotal) * 118 static_cast<float>(mode_info->vTotal)); 119 } else { 120 return 0.0f; 121 } 122 } 123 124 } // namespace 125 126 std::string GetDisplayName(XID output_id) { 127 std::string display_name; 128 GetOutputDeviceData(output_id, NULL, &display_name); 129 return display_name; 130 } 131 132 bool GetDisplayId(XID output_id, size_t output_index, int64* display_id_out) { 133 unsigned long nitems = 0; 134 unsigned char* prop = NULL; 135 if (!GetEDIDProperty(output_id, &nitems, &prop)) 136 return false; 137 138 bool result = 139 GetDisplayIdFromEDID(prop, nitems, output_index, display_id_out); 140 XFree(prop); 141 return result; 142 } 143 144 bool GetDisplayIdFromEDID(const unsigned char* prop, 145 unsigned long nitems, 146 size_t output_index, 147 int64* display_id_out) { 148 uint16 manufacturer_id = 0; 149 std::string product_name; 150 151 // ParseOutputDeviceData fails if it doesn't have product_name. 152 ParseOutputDeviceData(prop, nitems, &manufacturer_id, &product_name); 153 154 // Generates product specific value from product_name instead of product code. 155 // See crbug.com/240341 156 uint32 product_code_hash = product_name.empty() ? 157 0 : base::Hash(product_name); 158 if (manufacturer_id != 0) { 159 // An ID based on display's index will be assigned later if this call 160 // fails. 161 *display_id_out = GetID( 162 manufacturer_id, product_code_hash, output_index); 163 return true; 164 } 165 return false; 166 } 167 168 bool ParseOutputDeviceData(const unsigned char* prop, 169 unsigned long nitems, 170 uint16* manufacturer_id, 171 std::string* human_readable_name) { 172 // See http://en.wikipedia.org/wiki/Extended_display_identification_data 173 // for the details of EDID data format. We use the following data: 174 // bytes 8-9: manufacturer EISA ID, in big-endian 175 // bytes 54-125: four descriptors (18-bytes each) which may contain 176 // the display name. 177 const unsigned int kManufacturerOffset = 8; 178 const unsigned int kManufacturerLength = 2; 179 const unsigned int kDescriptorOffset = 54; 180 const unsigned int kNumDescriptors = 4; 181 const unsigned int kDescriptorLength = 18; 182 // The specifier types. 183 const unsigned char kMonitorNameDescriptor = 0xfc; 184 185 if (manufacturer_id) { 186 if (nitems < kManufacturerOffset + kManufacturerLength) { 187 LOG(ERROR) << "too short EDID data: manifacturer id"; 188 return false; 189 } 190 191 *manufacturer_id = 192 *reinterpret_cast<const uint16*>(prop + kManufacturerOffset); 193 #if defined(ARCH_CPU_LITTLE_ENDIAN) 194 *manufacturer_id = base::ByteSwap(*manufacturer_id); 195 #endif 196 } 197 198 if (!human_readable_name) 199 return true; 200 201 human_readable_name->clear(); 202 for (unsigned int i = 0; i < kNumDescriptors; ++i) { 203 if (nitems < kDescriptorOffset + (i + 1) * kDescriptorLength) 204 break; 205 206 const unsigned char* desc_buf = 207 prop + kDescriptorOffset + i * kDescriptorLength; 208 // If the descriptor contains the display name, it has the following 209 // structure: 210 // bytes 0-2, 4: \0 211 // byte 3: descriptor type, defined above. 212 // bytes 5-17: text data, ending with \r, padding with spaces 213 // we should check bytes 0-2 and 4, since it may have other values in 214 // case that the descriptor contains other type of data. 215 if (desc_buf[0] == 0 && desc_buf[1] == 0 && desc_buf[2] == 0 && 216 desc_buf[4] == 0) { 217 if (desc_buf[3] == kMonitorNameDescriptor) { 218 std::string found_name( 219 reinterpret_cast<const char*>(desc_buf + 5), kDescriptorLength - 5); 220 TrimWhitespaceASCII(found_name, TRIM_TRAILING, human_readable_name); 221 break; 222 } 223 } 224 } 225 226 // Verify if the |human_readable_name| consists of printable characters only. 227 for (size_t i = 0; i < human_readable_name->size(); ++i) { 228 char c = (*human_readable_name)[i]; 229 if (!isascii(c) || !isprint(c)) { 230 human_readable_name->clear(); 231 LOG(ERROR) << "invalid EDID: human unreadable char in name"; 232 return false; 233 } 234 } 235 236 return true; 237 } 238 239 bool GetOutputOverscanFlag(XID output, bool* flag) { 240 unsigned long nitems = 0; 241 unsigned char *prop = NULL; 242 if (!GetEDIDProperty(output, &nitems, &prop)) 243 return false; 244 245 bool found = ParseOutputOverscanFlag(prop, nitems, flag); 246 XFree(prop); 247 return found; 248 } 249 250 bool ParseOutputOverscanFlag(const unsigned char* prop, 251 unsigned long nitems, 252 bool *flag) { 253 // See http://en.wikipedia.org/wiki/Extended_display_identification_data 254 // for the extension format of EDID. Also see EIA/CEA-861 spec for 255 // the format of the extensions and how video capability is encoded. 256 // - byte 0: tag. should be 02h. 257 // - byte 1: revision. only cares revision 3 (03h). 258 // - byte 4-: data block. 259 const unsigned int kExtensionBase = 128; 260 const unsigned int kExtensionSize = 128; 261 const unsigned int kNumExtensionsOffset = 126; 262 const unsigned int kDataBlockOffset = 4; 263 const unsigned char kCEAExtensionTag = '\x02'; 264 const unsigned char kExpectedExtensionRevision = '\x03'; 265 const unsigned char kExtendedTag = 7; 266 const unsigned char kExtendedVideoCapabilityTag = 0; 267 const unsigned int kPTOverscan = 4; 268 const unsigned int kITOverscan = 2; 269 const unsigned int kCEOverscan = 0; 270 271 if (nitems <= kNumExtensionsOffset) 272 return false; 273 274 unsigned char num_extensions = prop[kNumExtensionsOffset]; 275 276 for (size_t i = 0; i < num_extensions; ++i) { 277 // Skip parsing the whole extension if size is not enough. 278 if (nitems < kExtensionBase + (i + 1) * kExtensionSize) 279 break; 280 281 const unsigned char* extension = prop + kExtensionBase + i * kExtensionSize; 282 unsigned char tag = extension[0]; 283 unsigned char revision = extension[1]; 284 if (tag != kCEAExtensionTag || revision != kExpectedExtensionRevision) 285 continue; 286 287 unsigned char timing_descriptors_start = 288 std::min(extension[2], static_cast<unsigned char>(kExtensionSize)); 289 const unsigned char* data_block = extension + kDataBlockOffset; 290 while (data_block < extension + timing_descriptors_start) { 291 // A data block is encoded as: 292 // - byte 1 high 3 bits: tag. '07' for extended tags. 293 // - byte 1 remaining bits: the length of data block. 294 // - byte 2: the extended tag. '0' for video capability. 295 // - byte 3: the capability. 296 unsigned char tag = data_block[0] >> 5; 297 unsigned char payload_length = data_block[0] & 0x1f; 298 if (static_cast<unsigned long>(data_block + payload_length - prop) > 299 nitems) 300 break; 301 302 if (tag != kExtendedTag || payload_length < 2) { 303 data_block += payload_length + 1; 304 continue; 305 } 306 307 unsigned char extended_tag_code = data_block[1]; 308 if (extended_tag_code != kExtendedVideoCapabilityTag) { 309 data_block += payload_length + 1; 310 continue; 311 } 312 313 // The difference between preferred, IT, and CE video formats 314 // doesn't matter. Sets |flag| to true if any of these flags are true. 315 if ((data_block[2] & (1 << kPTOverscan)) || 316 (data_block[2] & (1 << kITOverscan)) || 317 (data_block[2] & (1 << kCEOverscan))) { 318 *flag = true; 319 } else { 320 *flag = false; 321 } 322 return true; 323 } 324 } 325 326 return false; 327 } 328 329 bool IsInternalOutputName(const std::string& name) { 330 return name.find(kInternal_LVDS) == 0 || name.find(kInternal_eDP) == 0 || 331 name.find(kInternal_DSI) == 0; 332 } 333 334 const XRRModeInfo* FindModeInfo(const XRRScreenResources* screen_resources, 335 XID current_mode) { 336 for (int m = 0; m < screen_resources->nmode; m++) { 337 XRRModeInfo *mode = &screen_resources->modes[m]; 338 if (mode->id == current_mode) 339 return mode; 340 } 341 return NULL; 342 } 343 344 // Find a mode that matches the given size with highest 345 // reflesh rate. 346 RRMode FindOutputModeMatchingSize( 347 const XRRScreenResources* screen_resources, 348 const XRROutputInfo* output_info, 349 size_t width, 350 size_t height) { 351 RRMode found = None; 352 float best_rate = 0; 353 bool non_interlaced_found = false; 354 for (int i = 0; i < output_info->nmode; ++i) { 355 RRMode mode = output_info->modes[i]; 356 const XRRModeInfo* info = FindModeInfo(screen_resources, mode); 357 358 if (info->width == width && info->height == height) { 359 float rate = GetRefreshRate(info); 360 361 if (info->modeFlags & RR_Interlace) { 362 if (non_interlaced_found) 363 continue; 364 } else { 365 // Reset the best rate if the non interlaced is 366 // found the first time. 367 if (!non_interlaced_found) { 368 best_rate = rate; 369 } 370 non_interlaced_found = true; 371 } 372 if (rate < best_rate) 373 continue; 374 375 found = mode; 376 best_rate = rate; 377 } 378 } 379 return found; 380 } 381 382 namespace test { 383 384 XRRModeInfo CreateModeInfo(int id, 385 int width, 386 int height, 387 bool interlaced, 388 float refresh_rate) { 389 XRRModeInfo mode_info = {0}; 390 mode_info.id = id; 391 mode_info.width = width; 392 mode_info.height = height; 393 if (interlaced) 394 mode_info.modeFlags = RR_Interlace; 395 if (refresh_rate != 0.0f) { 396 mode_info.hTotal = 1; 397 mode_info.vTotal = 1; 398 mode_info.dotClock = refresh_rate; 399 } 400 return mode_info; 401 } 402 403 } // namespace test 404 405 } // namespace chromeos 406