diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-06-30 08:01:23 +0200 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2020-07-01 00:46:59 +0200 |
commit | acbe4190e9a126f48a0f7ecd6889c010ea44ce39 (patch) | |
tree | ef59ac7c0b58478c6c6424381a0b2e11cd2527bc /src/gui/util/qedidparser.cpp | |
parent | b92a41ea218d3ae73e9c8afa5a795e4893c68951 (diff) |
Move the EDID parser into QtGui
As a drive by, fix recursive inclusion in qxcbscreen.h.
Task-number: QTBUG-83255
Change-Id: Ia008921b559ef450c07aa17ca554c6b35e0a88bd
Reviewed-by: Qt CI Bot <qt_ci_bot@qt-project.org>
Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src/gui/util/qedidparser.cpp')
-rw-r--r-- | src/gui/util/qedidparser.cpp | 174 |
1 files changed, 174 insertions, 0 deletions
diff --git a/src/gui/util/qedidparser.cpp b/src/gui/util/qedidparser.cpp new file mode 100644 index 0000000000..7ca514e7c6 --- /dev/null +++ b/src/gui/util/qedidparser.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** 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$ +** +****************************************************************************/ + +#include <QtCore/QFile> + +#include "qedidparser_p.h" +#include "qedidvendortable_p.h" + +#define EDID_DESCRIPTOR_ALPHANUMERIC_STRING 0xfe +#define EDID_DESCRIPTOR_PRODUCT_NAME 0xfc +#define EDID_DESCRIPTOR_SERIAL_NUMBER 0xff + +#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 + +QT_BEGIN_NAMESPACE + +QEdidParser::QEdidParser() +{ + // 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(' ')); + } + } + + file.close(); + } + } +} + +bool QEdidParser::parse(const QByteArray &blob) +{ + const quint8 *data = reinterpret_cast<const quint8 *>(blob.constData()); + const size_t length = blob.length(); + + // Verify header + if (length < 128) + return false; + if (data[0] != 0x00 || data[1] != 0xff) + return false; + + /* Decode the PNP ID from three 5 bit words packed into 2 bytes + * /--08--\/--09--\ + * 7654321076543210 + * |\---/\---/\---/ + * R C1 C2 C3 */ + char pnpId[3]; + 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(); + + // Serial number, will be overwritten by an ASCII descriptor + // when and if it will be found + quint32 serial = data[EDID_OFFSET_SERIAL] + + (data[EDID_OFFSET_SERIAL + 1] << 8) + + (data[EDID_OFFSET_SERIAL + 2] << 16) + + (data[EDID_OFFSET_SERIAL + 3] << 24); + if (serial > 0) + serialNumber = QString::number(serial); + else + serialNumber = QString(); + + // Parse EDID data + for (int i = 0; i < 5; ++i) { + const uint offset = EDID_OFFSET_DATA_BLOCKS + i * 18; + + if (data[offset] != 0 || data[offset + 1] != 0 || data[offset + 2] != 0) + continue; + + if (data[offset + 3] == EDID_DESCRIPTOR_PRODUCT_NAME) + model = parseEdidString(&data[offset + 5]); + else if (data[offset + 3] == EDID_DESCRIPTOR_ALPHANUMERIC_STRING) + identifier = parseEdidString(&data[offset + 5]); + else if (data[offset + 3] == EDID_DESCRIPTOR_SERIAL_NUMBER) + serialNumber = parseEdidString(&data[offset + 5]); + } + + // Try to use cache first because it is potentially more updated + manufacturer = m_vendorCache.value(pnpIdString); + 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; + } + } + } + + // If we don't know the manufacturer, fallback to PNP ID + if (manufacturer.isEmpty()) + manufacturer = pnpIdString; + + // Physical size + physicalSize = QSizeF(data[EDID_PHYSICAL_WIDTH], data[EDID_OFFSET_PHYSICAL_HEIGHT]) * 10; + + return true; +} + +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'); + + // Replace non-printable characters with dash + for (int i = 0; i < buffer.count(); ++i) { + if (buffer[i] < '\040' || buffer[i] > '\176') + buffer[i] = '-'; + } + + return QString::fromLatin1(buffer.trimmed()); +} + +QT_END_NAMESPACE |