Changeset View
Changeset View
Standalone View
Standalone View
plugins/platforms/drm/drm_output.cpp
Show First 20 Lines • Show All 260 Lines • ▼ Show 20 Line(s) | 246 | { | |||
---|---|---|---|---|---|
261 | if (isInternal()) { | 261 | if (isInternal()) { | ||
262 | connect(kwinApp(), &Application::screensCreated, this, | 262 | connect(kwinApp(), &Application::screensCreated, this, | ||
263 | [this] { | 263 | [this] { | ||
264 | connect(screens()->orientationSensor(), &OrientationSensor::orientationChanged, this, &DrmOutput::automaticRotation); | 264 | connect(screens()->orientationSensor(), &OrientationSensor::orientationChanged, this, &DrmOutput::automaticRotation); | ||
265 | } | 265 | } | ||
266 | ); | 266 | ); | ||
267 | } | 267 | } | ||
268 | 268 | | |||
269 | QSize physicalSize = !m_edid.physicalSize.isEmpty() ? m_edid.physicalSize : QSize(connector->mmWidth, connector->mmHeight); | 269 | QSize physicalSize = !m_edid.physicalSize().isEmpty() ? m_edid.physicalSize() : QSize(connector->mmWidth, connector->mmHeight); | ||
270 | // the size might be completely borked. E.g. Samsung SyncMaster 2494HS reports 160x90 while in truth it's 520x292 | 270 | // the size might be completely borked. E.g. Samsung SyncMaster 2494HS reports 160x90 while in truth it's 520x292 | ||
271 | // as this information is used to calculate DPI info, it's going to result in everything being huge | 271 | // as this information is used to calculate DPI info, it's going to result in everything being huge | ||
272 | const QByteArray unknown = QByteArrayLiteral("unknown"); | 272 | const QByteArray unknown = QByteArrayLiteral("unknown"); | ||
273 | KConfigGroup group = kwinApp()->config()->group("EdidOverwrite").group(m_edid.eisaId.isEmpty() ? unknown : m_edid.eisaId) | 273 | KConfigGroup group = kwinApp()->config()->group("EdidOverwrite").group(m_edid.eisaId().isEmpty() ? unknown : m_edid.eisaId()) | ||
274 | .group(m_edid.monitorName.isEmpty() ? unknown : m_edid.monitorName) | 274 | .group(m_edid.monitorName().isEmpty() ? unknown : m_edid.monitorName()) | ||
275 | .group(m_edid.serialNumber.isEmpty() ? unknown : m_edid.serialNumber); | 275 | .group(m_edid.serialNumber().isEmpty() ? unknown : m_edid.serialNumber()); | ||
276 | if (group.hasKey("PhysicalSize")) { | 276 | if (group.hasKey("PhysicalSize")) { | ||
277 | const QSize overwriteSize = group.readEntry("PhysicalSize", physicalSize); | 277 | const QSize overwriteSize = group.readEntry("PhysicalSize", physicalSize); | ||
278 | qCWarning(KWIN_DRM) << "Overwriting monitor physical size for" << m_edid.eisaId << "/" << m_edid.monitorName << "/" << m_edid.serialNumber << " from " << physicalSize << "to " << overwriteSize; | 278 | qCWarning(KWIN_DRM) << "Overwriting monitor physical size for" << m_edid.eisaId() << "/" << m_edid.monitorName() << "/" << m_edid.serialNumber() << " from " << physicalSize << "to " << overwriteSize; | ||
279 | physicalSize = overwriteSize; | 279 | physicalSize = overwriteSize; | ||
280 | } | 280 | } | ||
281 | setRawPhysicalSize(physicalSize); | 281 | setRawPhysicalSize(physicalSize); | ||
282 | 282 | | |||
283 | initOutputDevice(connector); | 283 | initOutputDevice(connector); | ||
284 | 284 | | |||
285 | setEnabled(true); | 285 | setEnabled(true); | ||
286 | return true; | 286 | return true; | ||
287 | } | 287 | } | ||
288 | 288 | | |||
289 | void DrmOutput::initUuid() | 289 | void DrmOutput::initUuid() | ||
290 | { | 290 | { | ||
291 | QCryptographicHash hash(QCryptographicHash::Md5); | 291 | QCryptographicHash hash(QCryptographicHash::Md5); | ||
292 | hash.addData(QByteArray::number(m_conn->id())); | 292 | hash.addData(QByteArray::number(m_conn->id())); | ||
293 | hash.addData(m_edid.eisaId); | 293 | hash.addData(m_edid.eisaId()); | ||
294 | hash.addData(m_edid.monitorName); | 294 | hash.addData(m_edid.monitorName()); | ||
295 | hash.addData(m_edid.serialNumber); | 295 | hash.addData(m_edid.serialNumber()); | ||
296 | m_uuid = hash.result().toHex().left(10); | 296 | m_uuid = hash.result().toHex().left(10); | ||
297 | } | 297 | } | ||
298 | 298 | | |||
299 | void DrmOutput::initOutputDevice(drmModeConnector *connector) | 299 | void DrmOutput::initOutputDevice(drmModeConnector *connector) | ||
300 | { | 300 | { | ||
301 | QString manufacturer; | 301 | QString manufacturer; | ||
302 | if (!m_edid.eisaId.isEmpty()) { | 302 | if (!m_edid.eisaId().isEmpty()) { | ||
303 | manufacturer = QString::fromLatin1(m_edid.eisaId); | 303 | manufacturer = QString::fromLatin1(m_edid.eisaId()); | ||
304 | } | 304 | } | ||
305 | 305 | | |||
306 | QString connectorName = s_connectorNames.value(connector->connector_type, QByteArrayLiteral("Unknown")); | 306 | QString connectorName = s_connectorNames.value(connector->connector_type, QByteArrayLiteral("Unknown")); | ||
307 | QString modelName; | 307 | QString modelName; | ||
308 | 308 | | |||
309 | if (!m_edid.monitorName.isEmpty()) { | 309 | if (!m_edid.monitorName().isEmpty()) { | ||
310 | QString m = QString::fromLatin1(m_edid.monitorName); | 310 | QString m = QString::fromLatin1(m_edid.monitorName()); | ||
311 | if (!m_edid.serialNumber.isEmpty()) { | 311 | if (!m_edid.serialNumber().isEmpty()) { | ||
312 | m.append('/'); | 312 | m.append('/'); | ||
313 | m.append(QString::fromLatin1(m_edid.serialNumber)); | 313 | m.append(QString::fromLatin1(m_edid.serialNumber())); | ||
314 | } | 314 | } | ||
315 | modelName = m; | 315 | modelName = m; | ||
316 | } else if (!m_edid.serialNumber.isEmpty()) { | 316 | } else if (!m_edid.serialNumber().isEmpty()) { | ||
317 | modelName = QString::fromLatin1(m_edid.serialNumber); | 317 | modelName = QString::fromLatin1(m_edid.serialNumber()); | ||
318 | } else { | 318 | } else { | ||
319 | modelName = i18n("unknown"); | 319 | modelName = i18n("unknown"); | ||
320 | } | 320 | } | ||
321 | 321 | | |||
322 | const QString model = connectorName + QStringLiteral("-") + QString::number(connector->connector_type_id) + QStringLiteral("-") + modelName; | 322 | const QString model = connectorName + QStringLiteral("-") + QString::number(connector->connector_type_id) + QStringLiteral("-") + modelName; | ||
323 | 323 | | |||
324 | // read in mode information | 324 | // read in mode information | ||
325 | QVector<KWayland::Server::OutputDeviceInterface::Mode> modes; | 325 | QVector<KWayland::Server::OutputDeviceInterface::Mode> modes; | ||
Show All 34 Lines | 351 | return mode->clock == m_mode.clock | |||
360 | && mode->vtotal == m_mode.vtotal | 360 | && mode->vtotal == m_mode.vtotal | ||
361 | && mode->vscan == m_mode.vscan | 361 | && mode->vscan == m_mode.vscan | ||
362 | && mode->vrefresh == m_mode.vrefresh | 362 | && mode->vrefresh == m_mode.vrefresh | ||
363 | && mode->flags == m_mode.flags | 363 | && mode->flags == m_mode.flags | ||
364 | && mode->type == m_mode.type | 364 | && mode->type == m_mode.type | ||
365 | && qstrcmp(mode->name, m_mode.name) == 0; | 365 | && qstrcmp(mode->name, m_mode.name) == 0; | ||
366 | } | 366 | } | ||
367 | 367 | | |||
368 | static bool verifyEdidHeader(drmModePropertyBlobPtr edid) | | |||
369 | { | | |||
370 | const uint8_t *data = reinterpret_cast<uint8_t*>(edid->data); | | |||
371 | if (data[0] != 0x00) { | | |||
372 | return false; | | |||
373 | } | | |||
374 | for (int i = 1; i < 7; ++i) { | | |||
375 | if (data[i] != 0xFF) { | | |||
376 | return false; | | |||
377 | } | | |||
378 | } | | |||
379 | if (data[7] != 0x00) { | | |||
380 | return false; | | |||
381 | } | | |||
382 | return true; | | |||
383 | } | | |||
384 | | ||||
385 | static QByteArray extractEisaId(drmModePropertyBlobPtr edid) | | |||
386 | { | | |||
387 | /* | | |||
388 | * From EDID standard section 3.4: | | |||
389 | * The ID Manufacturer Name field, shown in Table 3.5, contains a 2-byte representation of the monitor's | | |||
390 | * manufacturer. This is the same as the EISA ID. It is based on compressed ASCII, “0001=A” ... “11010=Z”. | | |||
391 | * | | |||
392 | * The table: | | |||
393 | * | Byte | Bit | | | |||
394 | * | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | | | |||
395 | * ---------------------------------------- | | |||
396 | * | 1 | 0)| (4| 3 | 2 | 1 | 0)| (4| 3 | | | |||
397 | * | | * | Character 1 | Char 2| | | |||
398 | * ---------------------------------------- | | |||
399 | * | 2 | 2 | 1 | 0)| (4| 3 | 2 | 1 | 0)| | | |||
400 | * | | Character2| Character 3 | | | |||
401 | * ---------------------------------------- | | |||
402 | **/ | | |||
403 | const uint8_t *data = reinterpret_cast<uint8_t*>(edid->data); | | |||
404 | static const uint offset = 0x8; | | |||
405 | char id[4]; | | |||
406 | if (data[offset] >> 7) { | | |||
407 | // bit at position 7 is not a 0 | | |||
408 | return QByteArray(); | | |||
409 | } | | |||
410 | // shift two bits to right, and with 7 right most bits | | |||
411 | id[0] = 'A' + ((data[offset] >> 2) & 0x1f) -1; | | |||
412 | // for first byte: take last two bits and shift them 3 to left (000xx000) | | |||
413 | // for second byte: shift 5 bits to right and take 3 right most bits (00000xxx) | | |||
414 | // or both together | | |||
415 | id[1] = 'A' + (((data[offset] & 0x3) << 3) | ((data[offset + 1] >> 5) & 0x7)) - 1; | | |||
416 | // take five right most bits | | |||
417 | id[2] = 'A' + (data[offset + 1] & 0x1f) - 1; | | |||
418 | id[3] = '\0'; | | |||
419 | return QByteArray(id); | | |||
420 | } | | |||
421 | | ||||
422 | static void extractMonitorDescriptorDescription(drmModePropertyBlobPtr blob, DrmOutput::Edid &edid) | | |||
423 | { | | |||
424 | // see section 3.10.3 | | |||
425 | const uint8_t *data = reinterpret_cast<uint8_t*>(blob->data); | | |||
426 | static const uint offset = 0x36; | | |||
427 | static const uint blockLength = 18; | | |||
428 | for (int i = 0; i < 5; ++i) { | | |||
429 | const uint co = offset + i * blockLength; | | |||
430 | // Flag = 0000h when block used as descriptor | | |||
431 | if (data[co] != 0) { | | |||
432 | continue; | | |||
433 | } | | |||
434 | if (data[co + 1] != 0) { | | |||
435 | continue; | | |||
436 | } | | |||
437 | // Reserved = 00h when block used as descriptor | | |||
438 | if (data[co + 2] != 0) { | | |||
439 | continue; | | |||
440 | } | | |||
441 | /* | | |||
442 | * FFh: Monitor Serial Number - Stored as ASCII, code page # 437, ≤ 13 bytes. | | |||
443 | * FEh: ASCII String - Stored as ASCII, code page # 437, ≤ 13 bytes. | | |||
444 | * FDh: Monitor range limits, binary coded | | |||
445 | * FCh: Monitor name, stored as ASCII, code page # 437 | | |||
446 | * FBh: Descriptor contains additional color point data | | |||
447 | * FAh: Descriptor contains additional Standard Timing Identifications | | |||
448 | * F9h - 11h: Currently undefined | | |||
449 | * 10h: Dummy descriptor, used to indicate that the descriptor space is unused | | |||
450 | * 0Fh - 00h: Descriptor defined by manufacturer. | | |||
451 | */ | | |||
452 | if (data[co + 3] == 0xfc && edid.monitorName.isEmpty()) { | | |||
453 | edid.monitorName = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); | | |||
454 | } | | |||
455 | if (data[co + 3] == 0xfe) { | | |||
456 | const QByteArray id = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); | | |||
457 | if (!id.isEmpty()) { | | |||
458 | edid.eisaId = id; | | |||
459 | } | | |||
460 | } | | |||
461 | if (data[co + 3] == 0xff) { | | |||
462 | edid.serialNumber = QByteArray((const char *)(&data[co + 5]), 12).trimmed(); | | |||
463 | } | | |||
464 | } | | |||
465 | } | | |||
466 | | ||||
467 | static QByteArray extractSerialNumber(drmModePropertyBlobPtr edid) | | |||
468 | { | | |||
469 | // see section 3.4 | | |||
470 | const uint8_t *data = reinterpret_cast<uint8_t*>(edid->data); | | |||
471 | static const uint offset = 0x0C; | | |||
472 | /* | | |||
473 | * The ID serial number is a 32-bit serial number used to differentiate between individual instances of the same model | | |||
474 | * of monitor. Its use is optional. When used, the bit order for this field follows that shown in Table 3.6. The EDID | | |||
475 | * structure Version 1 Revision 1 and later offer a way to represent the serial number of the monitor as an ASCII string | | |||
476 | * in a separate descriptor block. | | |||
477 | */ | | |||
478 | uint32_t serialNumber = 0; | | |||
479 | serialNumber = (uint32_t) data[offset + 0]; | | |||
480 | serialNumber |= (uint32_t) data[offset + 1] << 8; | | |||
481 | serialNumber |= (uint32_t) data[offset + 2] << 16; | | |||
482 | serialNumber |= (uint32_t) data[offset + 3] << 24; | | |||
483 | if (serialNumber == 0) { | | |||
484 | return QByteArray(); | | |||
485 | } | | |||
486 | return QByteArray::number(serialNumber); | | |||
487 | } | | |||
488 | | ||||
489 | static QSize extractPhysicalSize(drmModePropertyBlobPtr edid) | | |||
490 | { | | |||
491 | const uint8_t *data = reinterpret_cast<uint8_t*>(edid->data); | | |||
492 | return QSize(data[0x15], data[0x16]) * 10; | | |||
493 | } | | |||
494 | | ||||
495 | void DrmOutput::initEdid(drmModeConnector *connector) | 368 | void DrmOutput::initEdid(drmModeConnector *connector) | ||
496 | { | 369 | { | ||
497 | DrmScopedPointer<drmModePropertyBlobRes> edid; | 370 | DrmScopedPointer<drmModePropertyBlobRes> edid; | ||
498 | for (int i = 0; i < connector->count_props; ++i) { | 371 | for (int i = 0; i < connector->count_props; ++i) { | ||
499 | DrmScopedPointer<drmModePropertyRes> property(drmModeGetProperty(m_backend->fd(), connector->props[i])); | 372 | DrmScopedPointer<drmModePropertyRes> property(drmModeGetProperty(m_backend->fd(), connector->props[i])); | ||
500 | if (!property) { | 373 | if (!property) { | ||
501 | continue; | 374 | continue; | ||
502 | } | 375 | } | ||
503 | if ((property->flags & DRM_MODE_PROP_BLOB) && qstrcmp(property->name, "EDID") == 0) { | 376 | if ((property->flags & DRM_MODE_PROP_BLOB) && qstrcmp(property->name, "EDID") == 0) { | ||
504 | edid.reset(drmModeGetPropertyBlob(m_backend->fd(), connector->prop_values[i])); | 377 | edid.reset(drmModeGetPropertyBlob(m_backend->fd(), connector->prop_values[i])); | ||
505 | } | 378 | } | ||
506 | } | 379 | } | ||
507 | if (!edid) { | 380 | if (!edid) { | ||
508 | return; | 381 | return; | ||
davidedmundson: If we hit this return with this patch m_edid changes from being merely invalid to being null. | |||||
Yeah... Are there actually drivers that do not expose EDID property? Just to be sure I'll add default constructor to EDID class. zzag: Yeah... Are there actually drivers that do not expose EDID property?
Just to be sure I'll add… | |||||
509 | } | 382 | } | ||
510 | 383 | | |||
511 | // for documentation see: http://read.pudn.com/downloads110/ebook/456020/E-EDID%20Standard.pdf | 384 | m_edid = Edid(edid->data, edid->length); | ||
512 | if (edid->length < 128) { | 385 | if (!m_edid.isValid()) { | ||
513 | return; | 386 | qCWarning(KWIN_DRM, "Couldn't parse EDID for connector with id %d", m_conn->id()); | ||
514 | } | 387 | } | ||
515 | if (!verifyEdidHeader(edid.data())) { | | |||
516 | return; | | |||
517 | } | | |||
518 | m_edid.eisaId = extractEisaId(edid.data()); | | |||
519 | m_edid.serialNumber = extractSerialNumber(edid.data()); | | |||
520 | | ||||
521 | // parse monitor descriptor description | | |||
522 | extractMonitorDescriptorDescription(edid.data(), m_edid); | | |||
523 | | ||||
524 | m_edid.physicalSize = extractPhysicalSize(edid.data()); | | |||
525 | } | 388 | } | ||
526 | 389 | | |||
527 | bool DrmOutput::initPrimaryPlane() | 390 | bool DrmOutput::initPrimaryPlane() | ||
528 | { | 391 | { | ||
529 | for (int i = 0; i < m_backend->planes().size(); ++i) { | 392 | for (int i = 0; i < m_backend->planes().size(); ++i) { | ||
530 | DrmPlane* p = m_backend->planes()[i]; | 393 | DrmPlane* p = m_backend->planes()[i]; | ||
531 | if (!p) { | 394 | if (!p) { | ||
532 | continue; | 395 | continue; | ||
▲ Show 20 Lines • Show All 682 Lines • Show Last 20 Lines |
If we hit this return with this patch m_edid changes from being merely invalid to being null. Something we don't guard for.