summaryrefslogtreecommitdiffstats
path: root/src/gui/util/qedidparser.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui/util/qedidparser.cpp')
-rw-r--r--src/gui/util/qedidparser.cpp232
1 files changed, 157 insertions, 75 deletions
diff --git a/src/gui/util/qedidparser.cpp b/src/gui/util/qedidparser.cpp
index 7ca514e7c6..4dae151e6a 100644
--- a/src/gui/util/qedidparser.cpp
+++ b/src/gui/util/qedidparser.cpp
@@ -1,43 +1,9 @@
-/****************************************************************************
-**
-** Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
-** Contact: https://www.qt.io/licensing/
-**
-** This file is part of the QtGui module of the Qt Toolkit.
-**
-** $QT_BEGIN_LICENSE:LGPL$
-** Commercial License Usage
-** Licensees holding valid commercial Qt licenses may use this file in
-** accordance with the commercial license agreement provided with the
-** Software or, alternatively, in accordance with the terms contained in
-** a written agreement between you and The Qt Company. For licensing terms
-** and conditions see https://www.qt.io/terms-conditions. For further
-** information use the contact form at https://www.qt.io/contact-us.
-**
-** GNU Lesser General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU Lesser
-** General Public License version 3 as published by the Free Software
-** Foundation and appearing in the file LICENSE.LGPL3 included in the
-** packaging of this file. Please review the following information to
-** ensure the GNU Lesser General Public License version 3 requirements
-** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
-**
-** GNU General Public License Usage
-** Alternatively, this file may be used under the terms of the GNU
-** General Public License version 2.0 or (at your option) the GNU General
-** Public license version 3 or any later version approved by the KDE Free
-** Qt Foundation. The licenses are as published by the Free Software
-** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
-** included in the packaging of this file. Please review the following
-** information to ensure the GNU General Public License requirements will
-** be met: https://www.gnu.org/licenses/gpl-2.0.html and
-** https://www.gnu.org/licenses/gpl-3.0.html.
-**
-** $QT_END_LICENSE$
-**
-****************************************************************************/
+// Copyright (C) 2017 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
+// Copyright (C) 2021 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author Giuseppe D'Angelo <giuseppe.dangelo@kdab.com>
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
#include <QtCore/QFile>
+#include <QtCore/QByteArrayView>
#include "qedidparser_p.h"
#include "qedidvendortable_p.h"
@@ -46,46 +12,67 @@
#define EDID_DESCRIPTOR_PRODUCT_NAME 0xfc
#define EDID_DESCRIPTOR_SERIAL_NUMBER 0xff
+#define EDID_DATA_BLOCK_COUNT 4
#define EDID_OFFSET_DATA_BLOCKS 0x36
#define EDID_OFFSET_LAST_BLOCK 0x6c
#define EDID_OFFSET_PNP_ID 0x08
#define EDID_OFFSET_SERIAL 0x0c
#define EDID_PHYSICAL_WIDTH 0x15
#define EDID_OFFSET_PHYSICAL_HEIGHT 0x16
+#define EDID_TRANSFER_FUNCTION 0x17
+#define EDID_FEATURE_SUPPORT 0x18
+#define EDID_CHROMATICITIES_BLOCK 0x19
QT_BEGIN_NAMESPACE
-QEdidParser::QEdidParser()
+using namespace Qt::StringLiterals;
+
+static QString lookupVendorIdInSystemDatabase(QByteArrayView id)
{
- // Cache vendors list from pnp.ids
- const QString fileName = QLatin1String("/usr/share/hwdata/pnp.ids");
- if (QFile::exists(fileName)) {
- QFile file(fileName);
-
- if (file.open(QFile::ReadOnly)) {
- while (!file.atEnd()) {
- QString line = QString::fromUtf8(file.readLine()).trimmed();
-
- if (line.startsWith(QLatin1Char('#')))
- continue;
-
- QStringList parts = line.split(QLatin1Char('\t'));
- if (parts.count() > 1) {
- QString pnpId = parts.at(0);
- parts.removeFirst();
- m_vendorCache[pnpId] = parts.join(QLatin1Char(' '));
- }
- }
+ QString result;
+
+ const QString fileName = "/usr/share/hwdata/pnp.ids"_L1;
+ QFile file(fileName);
+ if (!file.open(QFile::ReadOnly))
+ return result;
+
+ // On Ubuntu 20.04 the longest line in the file is 85 bytes, so this
+ // leaves plenty of room...
+ constexpr int MaxLineSize = 512;
+ char buf[MaxLineSize];
+
+ while (!file.atEnd()) {
+ auto read = file.readLine(buf, MaxLineSize);
+ if (read < 0 || read == MaxLineSize) // read error
+ break;
+
+ QByteArrayView line(buf, read - 1); // -1 to remove the trailing newline
+ if (line.isEmpty())
+ continue;
- file.close();
+ if (line.startsWith('#'))
+ continue;
+
+ auto tabPosition = line.indexOf('\t');
+ if (tabPosition <= 0) // no vendor id
+ continue;
+ if (tabPosition + 1 == line.size()) // no vendor name
+ continue;
+
+ if (line.first(tabPosition) == id) {
+ auto vendor = line.sliced(tabPosition + 1);
+ result = QString::fromUtf8(vendor.data(), vendor.size());
+ break;
}
}
+
+ return result;
}
bool QEdidParser::parse(const QByteArray &blob)
{
const quint8 *data = reinterpret_cast<const quint8 *>(blob.constData());
- const size_t length = blob.length();
+ const size_t length = blob.size();
// Verify header
if (length < 128)
@@ -102,7 +89,6 @@ bool QEdidParser::parse(const QByteArray &blob)
pnpId[0] = 'A' + ((data[EDID_OFFSET_PNP_ID] & 0x7c) / 4) - 1;
pnpId[1] = 'A' + ((data[EDID_OFFSET_PNP_ID] & 0x3) * 8) + ((data[EDID_OFFSET_PNP_ID + 1] & 0xe0) / 32) - 1;
pnpId[2] = 'A' + (data[EDID_OFFSET_PNP_ID + 1] & 0x1f) - 1;
- QString pnpIdString = QString::fromLatin1(pnpId, 3);
// Clear manufacturer
manufacturer = QString();
@@ -119,7 +105,7 @@ bool QEdidParser::parse(const QByteArray &blob)
serialNumber = QString();
// Parse EDID data
- for (int i = 0; i < 5; ++i) {
+ for (int i = 0; i < EDID_DATA_BLOCK_COUNT; ++i) {
const uint offset = EDID_OFFSET_DATA_BLOCKS + i * 18;
if (data[offset] != 0 || data[offset + 1] != 0 || data[offset + 2] != 0)
@@ -134,24 +120,114 @@ bool QEdidParser::parse(const QByteArray &blob)
}
// Try to use cache first because it is potentially more updated
- manufacturer = m_vendorCache.value(pnpIdString);
+ manufacturer = lookupVendorIdInSystemDatabase(pnpId);
+
if (manufacturer.isEmpty()) {
// Find the manufacturer from the vendor lookup table
- for (const auto &vendor : q_edidVendorTable) {
- if (strncmp(vendor.id, pnpId, 3) == 0) {
- manufacturer = QString::fromUtf8(vendor.name);
- break;
- }
- }
+ const auto compareVendorId = [](const VendorTable &vendor, const char *str)
+ {
+ return strncmp(vendor.id, str, 3) < 0;
+ };
+
+ const auto b = std::begin(q_edidVendorTable);
+ const auto e = std::end(q_edidVendorTable);
+ auto it = std::lower_bound(b,
+ e,
+ pnpId,
+ compareVendorId);
+
+ if (it != e && strncmp(it->id, pnpId, 3) == 0)
+ manufacturer = QString::fromUtf8(it->name);
}
// If we don't know the manufacturer, fallback to PNP ID
if (manufacturer.isEmpty())
- manufacturer = pnpIdString;
+ manufacturer = QString::fromUtf8(pnpId, std::size(pnpId));
// Physical size
physicalSize = QSizeF(data[EDID_PHYSICAL_WIDTH], data[EDID_OFFSET_PHYSICAL_HEIGHT]) * 10;
+ // Gamma and transfer function
+ const uint igamma = data[EDID_TRANSFER_FUNCTION];
+ if (igamma != 0xff) {
+ gamma = 1.0 + (igamma / 100.0f);
+ useTables = false;
+ } else {
+ gamma = 0.0; // Defined in DI-EXT
+ useTables = true;
+ }
+ sRgb = data[EDID_FEATURE_SUPPORT] & 0x04;
+
+ // Chromaticities
+ int rx = (data[EDID_CHROMATICITIES_BLOCK] >> 6) & 0x03;
+ int ry = (data[EDID_CHROMATICITIES_BLOCK] >> 4) & 0x03;
+ int gx = (data[EDID_CHROMATICITIES_BLOCK] >> 2) & 0x03;
+ int gy = (data[EDID_CHROMATICITIES_BLOCK] >> 0) & 0x03;
+ int bx = (data[EDID_CHROMATICITIES_BLOCK + 1] >> 6) & 0x03;
+ int by = (data[EDID_CHROMATICITIES_BLOCK + 1] >> 4) & 0x03;
+ int wx = (data[EDID_CHROMATICITIES_BLOCK + 1] >> 2) & 0x03;
+ int wy = (data[EDID_CHROMATICITIES_BLOCK + 1] >> 0) & 0x03;
+ rx |= data[EDID_CHROMATICITIES_BLOCK + 2] << 2;
+ ry |= data[EDID_CHROMATICITIES_BLOCK + 3] << 2;
+ gx |= data[EDID_CHROMATICITIES_BLOCK + 4] << 2;
+ gy |= data[EDID_CHROMATICITIES_BLOCK + 5] << 2;
+ bx |= data[EDID_CHROMATICITIES_BLOCK + 6] << 2;
+ by |= data[EDID_CHROMATICITIES_BLOCK + 7] << 2;
+ wx |= data[EDID_CHROMATICITIES_BLOCK + 8] << 2;
+ wy |= data[EDID_CHROMATICITIES_BLOCK + 9] << 2;
+
+ redChromaticity.setX(rx * (1.0f / 1024.0f));
+ redChromaticity.setY(ry * (1.0f / 1024.0f));
+ greenChromaticity.setX(gx * (1.0f / 1024.0f));
+ greenChromaticity.setY(gy * (1.0f / 1024.0f));
+ blueChromaticity.setX(bx * (1.0f / 1024.0f));
+ blueChromaticity.setY(by * (1.0f / 1024.0f));
+ whiteChromaticity.setX(wx * (1.0f / 1024.0f));
+ whiteChromaticity.setY(wy * (1.0f / 1024.0f));
+
+ // Find extensions
+ for (uint i = 1; i < length / 128; ++i) {
+ uint extensionId = data[i * 128];
+ if (extensionId == 0x40) { // DI-EXT
+ // 0x0E (sub-pixel layout)
+ // 0x20->0x22 (bits per color)
+ // 0x51->0x7e Transfer characteristics
+ const uchar desc = data[i * 128 + 0x51];
+ const uchar len = desc & 0x3f;
+ if ((desc & 0xc0) == 0x40) {
+ if (len > 45)
+ return false;
+ QList<uint16_t> whiteTRC;
+ whiteTRC.reserve(len + 1);
+ for (uint j = 0; j < len; ++j)
+ whiteTRC[j] = data[0x52 + j] * 0x101;
+ whiteTRC[len] = 0xffff;
+ tables.append(whiteTRC);
+ } else if ((desc & 0xc0) == 0x80) {
+ if (len > 15)
+ return false;
+ QList<uint16_t> redTRC;
+ QList<uint16_t> greenTRC;
+ QList<uint16_t> blueTRC;
+ blueTRC.reserve(len + 1);
+ greenTRC.reserve(len + 1);
+ redTRC.reserve(len + 1);
+ for (uint j = 0; j < len; ++j)
+ blueTRC[j] = data[0x52 + j] * 0x101;
+ blueTRC[len] = 0xffff;
+ for (uint j = 0; j < len; ++j)
+ greenTRC[j] = data[0x61 + j] * 0x101;
+ greenTRC[len] = 0xffff;
+ for (uint j = 0; j < len; ++j)
+ redTRC[j] = data[0x70 + j] * 0x101;
+ redTRC[len] = 0xffff;
+ tables.append(redTRC);
+ tables.append(greenTRC);
+ tables.append(blueTRC);
+ }
+ }
+ }
+
return true;
}
@@ -159,16 +235,22 @@ QString QEdidParser::parseEdidString(const quint8 *data)
{
QByteArray buffer(reinterpret_cast<const char *>(data), 13);
- // Erase carriage return and line feed
- buffer = buffer.replace('\r', '\0').replace('\n', '\0');
+ for (int i = 0; i < buffer.size(); ++i) {
+ // If there are less than 13 characters in the string, the string
+ // is terminated with the ASCII code ‘0Ah’ (line feed) and padded
+ // with ASCII code ‘20h’ (space). See EDID 1.4, sections 3.10.3.1,
+ // 3.10.3.2, and 3.10.3.4.
+ if (buffer[i] == '\n') {
+ buffer.truncate(i);
+ break;
+ }
- // Replace non-printable characters with dash
- for (int i = 0; i < buffer.count(); ++i) {
+ // Replace non-printable characters with dash
if (buffer[i] < '\040' || buffer[i] > '\176')
buffer[i] = '-';
}
- return QString::fromLatin1(buffer.trimmed());
+ return QString::fromLatin1(buffer);
}
QT_END_NAMESPACE