KWin
Loading...
Searching...
No Matches
edid.cpp
Go to the documentation of this file.
1/*
2 KWin - the KDE window manager
3 This file is part of the KDE project.
4
5 SPDX-FileCopyrightText: 2015 Martin Flöser <mgraesslin@kde.org>
6 SPDX-FileCopyrightText: 2019 Vlad Zahorodnii <vlad.zahorodnii@kde.org>
7
8 SPDX-License-Identifier: GPL-2.0-or-later
9*/
10#include "edid.h"
11
12#include "c_ptr.h"
13#include "common.h"
14#include "config-kwin.h"
15
16#include <QFile>
17#include <QStandardPaths>
18
19#include <KLocalizedString>
20#include <QCryptographicHash>
21
22extern "C" {
23#include <libdisplay-info/cta.h>
24#include <libdisplay-info/edid.h>
25#include <libdisplay-info/info.h>
26}
27
28namespace KWin
29{
30
31static QByteArray parsePnpId(const uint8_t *data)
32{
33 // Decode PNP ID from three 5 bit words packed into 2 bytes:
34 //
35 // | Byte | Bit |
36 // | | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
37 // ----------------------------------------
38 // | 1 | 0)| (4| 3 | 2 | 1 | 0)| (4| 3 |
39 // | | * | Character 1 | Char 2|
40 // ----------------------------------------
41 // | 2 | 2 | 1 | 0)| (4| 3 | 2 | 1 | 0)|
42 // | | Character2| Character 3 |
43 // ----------------------------------------
44 const uint offset = 0x8;
45
46 char pnpId[4];
47 pnpId[0] = 'A' + ((data[offset + 0] >> 2) & 0x1f) - 1;
48 pnpId[1] = 'A' + (((data[offset + 0] & 0x3) << 3) | ((data[offset + 1] >> 5) & 0x7)) - 1;
49 pnpId[2] = 'A' + (data[offset + 1] & 0x1f) - 1;
50 pnpId[3] = '\0';
51
52 return QByteArray(pnpId);
53}
54
55static QByteArray parseEisaId(const uint8_t *data)
56{
57 for (int i = 72; i <= 108; i += 18) {
58 // Skip the block if it isn't used as monitor descriptor.
59 if (data[i]) {
60 continue;
61 }
62 if (data[i + 1]) {
63 continue;
64 }
65
66 // We have found the EISA ID, it's stored as ASCII.
67 if (data[i + 3] == 0xfe) {
68 return QByteArray(reinterpret_cast<const char *>(&data[i + 5]), 13).trimmed();
69 }
70 }
71
72 // If there isn't an ASCII EISA ID descriptor, try to decode PNP ID
73 return parsePnpId(data);
74}
75
76static QByteArray parseVendor(const uint8_t *data)
77{
78 const auto pnpId = parsePnpId(data);
79
80 // Map to vendor name
81 QFile pnpFile(QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("hwdata/pnp.ids")));
82 if (pnpFile.exists() && pnpFile.open(QIODevice::ReadOnly)) {
83 while (!pnpFile.atEnd()) {
84 const auto line = pnpFile.readLine();
85 if (line.startsWith(pnpId)) {
86 return line.mid(4).trimmed();
87 }
88 }
89 }
90
91 return {};
92}
93
95{
96}
97
98Edid::Edid(const void *data, uint32_t size)
99{
100 m_raw.resize(size);
101 memcpy(m_raw.data(), data, size);
102
103 const uint8_t *bytes = static_cast<const uint8_t *>(data);
104
105 auto info = di_info_parse_edid(data, size);
106 if (!info) {
107 qCWarning(KWIN_CORE, "parsing edid failed");
108 return;
109 }
110 const di_edid *edid = di_info_get_edid(info);
111 const di_edid_vendor_product *productInfo = di_edid_get_vendor_product(edid);
112 const di_edid_screen_size *screenSize = di_edid_get_screen_size(edid);
113
114 // basic output information
115 m_physicalSize = QSize(screenSize->width_cm, screenSize->height_cm) * 10;
116 m_eisaId = parseEisaId(bytes);
117 UniqueCPtr<char> monitorName{di_info_get_model(info)};
118 m_monitorName = QByteArray(monitorName.get());
119 m_serialNumber = QByteArray::number(productInfo->serial);
120 m_vendor = parseVendor(bytes);
121 QCryptographicHash hash(QCryptographicHash::Md5);
122 hash.addData(m_raw);
123 m_hash = QString::fromLatin1(hash.result().toHex());
124
125 m_identifier = QByteArray(productInfo->manufacturer, 3) + " " + QByteArray::number(productInfo->product) + " " + QByteArray::number(productInfo->serial) + " "
126 + QByteArray::number(productInfo->manufacture_week) + " " + QByteArray::number(productInfo->manufacture_year) + " " + QByteArray::number(productInfo->model_year);
127
128 // colorimetry and HDR metadata
129 const auto chromaticity = di_edid_get_chromaticity_coords(edid);
130 if (chromaticity) {
131 m_colorimetry = Colorimetry{
132 QVector2D{chromaticity->red_x, chromaticity->red_y},
133 QVector2D{chromaticity->green_x, chromaticity->green_y},
134 QVector2D{chromaticity->blue_x, chromaticity->blue_y},
135 QVector2D{chromaticity->white_x, chromaticity->white_y},
136 };
137 } else {
138 m_colorimetry.reset();
139 }
140
141 const di_edid_cta *cta = nullptr;
142 const di_edid_ext *const *exts = di_edid_get_extensions(edid);
143 const di_cta_hdr_static_metadata_block *hdr_static_metadata = nullptr;
144 const di_cta_colorimetry_block *colorimetry = nullptr;
145 for (; *exts != nullptr; exts++) {
146 if ((cta = di_edid_ext_get_cta(*exts))) {
147 break;
148 }
149 }
150 if (cta) {
151 const di_cta_data_block *const *blocks = di_edid_cta_get_data_blocks(cta);
152 for (; *blocks != nullptr; blocks++) {
153 if (!hdr_static_metadata && (hdr_static_metadata = di_cta_data_block_get_hdr_static_metadata(*blocks))) {
154 continue;
155 }
156 if (!colorimetry && (colorimetry = di_cta_data_block_get_colorimetry(*blocks))) {
157 continue;
158 }
159 }
160 if (hdr_static_metadata) {
161 m_hdrMetadata = HDRMetadata{
162 .desiredContentMinLuminance = hdr_static_metadata->desired_content_min_luminance,
163 .desiredContentMaxLuminance = hdr_static_metadata->desired_content_max_luminance > 0 ? std::make_optional(hdr_static_metadata->desired_content_max_luminance) : std::nullopt,
164 .desiredMaxFrameAverageLuminance = hdr_static_metadata->desired_content_max_frame_avg_luminance > 0 ? std::make_optional(hdr_static_metadata->desired_content_max_frame_avg_luminance) : std::nullopt,
165 .supportsPQ = hdr_static_metadata->eotfs->pq,
166 .supportsBT2020 = colorimetry && colorimetry->bt2020_rgb,
167 };
168 }
169 }
170
171 m_isValid = true;
172 di_info_destroy(info);
173}
174
175bool Edid::isValid() const
176{
177 return m_isValid;
178}
179
181{
182 return m_physicalSize;
183}
184
185QByteArray Edid::eisaId() const
186{
187 return m_eisaId;
188}
189
190QByteArray Edid::monitorName() const
191{
192 return m_monitorName;
193}
194
195QByteArray Edid::serialNumber() const
196{
197 return m_serialNumber;
198}
199
200QByteArray Edid::vendor() const
201{
202 return m_vendor;
203}
204
205QByteArray Edid::raw() const
206{
207 return m_raw;
208}
209
211{
212 QString manufacturer;
213 if (!m_vendor.isEmpty()) {
214 manufacturer = QString::fromLatin1(m_vendor);
215 } else if (!m_eisaId.isEmpty()) {
216 manufacturer = QString::fromLatin1(m_eisaId);
217 }
218 return manufacturer;
219}
220
221QString Edid::nameString() const
222{
223 if (!m_monitorName.isEmpty()) {
224 QString m = QString::fromLatin1(m_monitorName);
225 if (!m_serialNumber.isEmpty()) {
226 m.append('/');
227 m.append(QString::fromLatin1(m_serialNumber));
228 }
229 return m;
230 } else if (!m_serialNumber.isEmpty()) {
231 return QString::fromLatin1(m_serialNumber);
232 } else {
233 return i18n("unknown");
234 }
235}
236
237QString Edid::hash() const
238{
239 return m_hash;
240}
241
242std::optional<Colorimetry> Edid::colorimetry() const
243{
244 return m_colorimetry;
245}
246
248{
249 return m_hdrMetadata ? m_hdrMetadata->desiredContentMinLuminance : 0;
250}
251
252std::optional<double> Edid::desiredMaxFrameAverageLuminance() const
253{
254 return m_hdrMetadata ? m_hdrMetadata->desiredMaxFrameAverageLuminance : std::nullopt;
255}
256
257std::optional<double> Edid::desiredMaxLuminance() const
258{
259 return m_hdrMetadata ? m_hdrMetadata->desiredContentMaxLuminance : std::nullopt;
260}
261
263{
264 return m_hdrMetadata && m_hdrMetadata->supportsPQ;
265}
266
268{
269 return m_hdrMetadata && m_hdrMetadata->supportsBT2020;
270}
271
272QByteArray Edid::identifier() const
273{
274 return m_identifier;
275}
276
277} // namespace KWin
QString manufacturerString() const
Definition edid.cpp:210
double desiredMinLuminance() const
Definition edid.cpp:247
QByteArray eisaId() const
Definition edid.cpp:185
QSize physicalSize() const
Definition edid.cpp:180
std::optional< double > desiredMaxFrameAverageLuminance() const
Definition edid.cpp:252
QByteArray raw() const
Definition edid.cpp:205
QByteArray serialNumber() const
Definition edid.cpp:195
std::optional< Colorimetry > colorimetry() const
Definition edid.cpp:242
bool supportsBT2020() const
Definition edid.cpp:267
QString hash() const
Definition edid.cpp:237
bool supportsPQ() const
Definition edid.cpp:262
QByteArray identifier() const
Definition edid.cpp:272
std::optional< double > desiredMaxLuminance() const
Definition edid.cpp:257
QByteArray monitorName() const
Definition edid.cpp:190
QString nameString() const
Definition edid.cpp:221
bool isValid() const
Definition edid.cpp:175
QByteArray vendor() const
Definition edid.cpp:200
std::unique_ptr< T, CDeleter > UniqueCPtr
Definition c_ptr.h:28