diff options
Diffstat (limited to 'src/gui/util/qedidparser.cpp')
-rw-r--r-- | src/gui/util/qedidparser.cpp | 148 |
1 files changed, 73 insertions, 75 deletions
diff --git a/src/gui/util/qedidparser.cpp b/src/gui/util/qedidparser.cpp index 553ca20807..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,6 +12,7 @@ #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 @@ -58,37 +25,54 @@ 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; - file.close(); + QByteArrayView line(buf, read - 1); // -1 to remove the trailing newline + if (line.isEmpty()) + continue; + + 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) @@ -105,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(); @@ -122,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) @@ -137,20 +120,29 @@ 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; @@ -243,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 |