Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qkmsdevice.cpp
Go to the documentation of this file.
1// Copyright (C) 2017 The Qt Company Ltd.
2// Copyright (C) 2016 Pelagicore AG
3// Copyright (C) 2015 Pier Luigi Fiorini <pierluigi.fiorini@gmail.com>
4// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
5
6#include "qkmsdevice_p.h"
7
8#include <QtCore/QJsonDocument>
9#include <QtCore/QJsonObject>
10#include <QtCore/QJsonArray>
11#include <QtCore/QFile>
12#include <QtCore/QLoggingCategory>
13
14#include <errno.h>
15
16#define ARRAY_LENGTH(a) (sizeof (a) / sizeof (a)[0])
17
19
20using namespace Qt::StringLiterals;
21
22Q_LOGGING_CATEGORY(qLcKmsDebug, "qt.qpa.eglfs.kms")
23
32
33int QKmsDevice::crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector)
34{
35 int candidate = -1;
36
37 for (int i = 0; i < connector->count_encoders; i++) {
38 drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->encoders[i]);
39 if (!encoder) {
40 qWarning("Failed to get encoder");
41 continue;
42 }
43
44 quint32 encoderId = encoder->encoder_id;
45 quint32 crtcId = encoder->crtc_id;
46 quint32 possibleCrtcs = encoder->possible_crtcs;
47 drmModeFreeEncoder(encoder);
48
49 for (int j = 0; j < resources->count_crtcs; j++) {
50 bool isPossible = possibleCrtcs & (1 << j);
51 bool isAvailable = !(m_crtc_allocator & (1 << j));
52 // Preserve the existing CRTC -> encoder -> connector routing if
53 // any. It makes the initialization faster, and may be better
54 // since we have a very dumb picking algorithm.
55 bool isBestChoice = (!connector->encoder_id ||
56 (connector->encoder_id == encoderId &&
57 resources->crtcs[j] == crtcId));
58
59 if (isPossible && isAvailable && isBestChoice) {
60 return j;
61 } else if (isPossible && isAvailable) {
62 candidate = j;
63 }
64 }
65 }
66
67 return candidate;
68}
69
70static const char * const connector_type_names[] = { // must match DRM_MODE_CONNECTOR_*
71 "None",
72 "VGA",
73 "DVI",
74 "DVI",
75 "DVI",
76 "Composite",
77 "TV",
78 "LVDS",
79 "CTV",
80 "DIN",
81 "DP",
82 "HDMI",
83 "HDMI",
84 "TV",
85 "eDP",
86 "Virtual",
87 "DSI"
88};
89
90static QByteArray nameForConnector(const drmModeConnectorPtr connector)
91{
92 QByteArray connectorName("UNKNOWN");
93
94 if (connector->connector_type < ARRAY_LENGTH(connector_type_names))
95 connectorName = connector_type_names[connector->connector_type];
96
97 connectorName += QByteArray::number(connector->connector_type_id);
98
99 return connectorName;
100}
101
102static bool parseModeline(const QByteArray &text, drmModeModeInfoPtr mode)
103{
104 char hsync[16];
105 char vsync[16];
106 float fclock;
107
108 mode->type = DRM_MODE_TYPE_USERDEF;
109 mode->hskew = 0;
110 mode->vscan = 0;
111 mode->vrefresh = 0;
112 mode->flags = 0;
113
114 if (sscanf(text.constData(), "%f %hd %hd %hd %hd %hd %hd %hd %hd %15s %15s",
115 &fclock,
116 &mode->hdisplay,
117 &mode->hsync_start,
118 &mode->hsync_end,
119 &mode->htotal,
120 &mode->vdisplay,
121 &mode->vsync_start,
122 &mode->vsync_end,
123 &mode->vtotal, hsync, vsync) != 11)
124 return false;
125
126 mode->clock = fclock * 1000;
127
128 if (strcmp(hsync, "+hsync") == 0)
129 mode->flags |= DRM_MODE_FLAG_PHSYNC;
130 else if (strcmp(hsync, "-hsync") == 0)
131 mode->flags |= DRM_MODE_FLAG_NHSYNC;
132 else
133 return false;
134
135 if (strcmp(vsync, "+vsync") == 0)
136 mode->flags |= DRM_MODE_FLAG_PVSYNC;
137 else if (strcmp(vsync, "-vsync") == 0)
138 mode->flags |= DRM_MODE_FLAG_NVSYNC;
139 else
140 return false;
141
142 return true;
143}
144
145static inline void assignPlane(QKmsOutput *output, QKmsPlane *plane)
146{
147 if (output->eglfs_plane)
148 output->eglfs_plane->activeCrtcId = 0;
149
150 plane->activeCrtcId = output->crtc_id;
151 output->eglfs_plane = plane;
152}
153
155 drmModeConnectorPtr connector,
156 ScreenInfo *vinfo)
157{
158 Q_ASSERT(vinfo);
159 const QByteArray connectorName = nameForConnector(connector);
160
161 const int crtc = crtcForConnector(resources, connector);
162 if (crtc < 0) {
163 qWarning() << "No usable crtc/encoder pair for connector" << connectorName;
164 return nullptr;
165 }
166
167 OutputConfiguration configuration;
168 QSize configurationSize;
169 int configurationRefresh = 0;
170 drmModeModeInfo configurationModeline;
171
172 auto userConfig = m_screenConfig->outputSettings();
173 QVariantMap userConnectorConfig = userConfig.value(QString::fromUtf8(connectorName));
174 // default to the preferred mode unless overridden in the config
175 const QByteArray mode = userConnectorConfig.value(QStringLiteral("mode"), QStringLiteral("preferred"))
176 .toByteArray().toLower();
177 if (mode == "off") {
178 configuration = OutputConfigOff;
179 } else if (mode == "preferred") {
180 configuration = OutputConfigPreferred;
181 } else if (mode == "current") {
182 configuration = OutputConfigCurrent;
183 } else if (mode == "skip") {
184 configuration = OutputConfigSkip;
185 } else if (sscanf(mode.constData(), "%dx%d@%d", &configurationSize.rwidth(), &configurationSize.rheight(),
186 &configurationRefresh) == 3)
187 {
188 configuration = OutputConfigMode;
189 } else if (sscanf(mode.constData(), "%dx%d", &configurationSize.rwidth(), &configurationSize.rheight()) == 2) {
190 configuration = OutputConfigMode;
191 } else if (parseModeline(mode, &configurationModeline)) {
192 configuration = OutputConfigModeline;
193 } else {
194 qWarning("Invalid mode \"%s\" for output %s", mode.constData(), connectorName.constData());
195 configuration = OutputConfigPreferred;
196 }
197
198 *vinfo = ScreenInfo();
199 vinfo->virtualIndex = userConnectorConfig.value(QStringLiteral("virtualIndex"), INT_MAX).toInt();
200 if (userConnectorConfig.contains(QStringLiteral("virtualPos"))) {
201 const QByteArray vpos = userConnectorConfig.value(QStringLiteral("virtualPos")).toByteArray();
202 const QByteArrayList vposComp = vpos.split(',');
203 if (vposComp.size() == 2)
204 vinfo->virtualPos = QPoint(vposComp[0].trimmed().toInt(), vposComp[1].trimmed().toInt());
205 }
206 if (userConnectorConfig.value(QStringLiteral("primary")).toBool())
207 vinfo->isPrimary = true;
208
209 const uint32_t crtc_id = resources->crtcs[crtc];
210
211 if (configuration == OutputConfigOff) {
212 qCDebug(qLcKmsDebug) << "Turning off output" << connectorName;
213 drmModeSetCrtc(m_dri_fd, crtc_id, 0, 0, 0, 0, 0, nullptr);
214 return nullptr;
215 }
216
217 // Skip disconnected output
218 if (configuration == OutputConfigPreferred && connector->connection == DRM_MODE_DISCONNECTED) {
219 qCDebug(qLcKmsDebug) << "Skipping disconnected output" << connectorName;
220 return nullptr;
221 }
222
223 if (configuration == OutputConfigSkip) {
224 qCDebug(qLcKmsDebug) << "Skipping output" << connectorName;
225 return nullptr;
226 }
227
228 // Get the current mode on the current crtc
229 drmModeModeInfo crtc_mode;
230 memset(&crtc_mode, 0, sizeof crtc_mode);
231 if (drmModeEncoderPtr encoder = drmModeGetEncoder(m_dri_fd, connector->encoder_id)) {
232 drmModeCrtcPtr crtc = drmModeGetCrtc(m_dri_fd, encoder->crtc_id);
233 drmModeFreeEncoder(encoder);
234
235 if (!crtc)
236 return nullptr;
237
238 if (crtc->mode_valid)
239 crtc_mode = crtc->mode;
240
241 drmModeFreeCrtc(crtc);
242 }
243
244 QList<drmModeModeInfo> modes;
245 modes.reserve(connector->count_modes);
246 qCDebug(qLcKmsDebug) << connectorName << "mode count:" << connector->count_modes
247 << "crtc index:" << crtc << "crtc id:" << crtc_id;
248 for (int i = 0; i < connector->count_modes; i++) {
249 const drmModeModeInfo &mode = connector->modes[i];
250 qCDebug(qLcKmsDebug) << "mode" << i << mode.hdisplay << "x" << mode.vdisplay
251 << '@' << mode.vrefresh << "hz";
252 modes << connector->modes[i];
253 }
254
255 int preferred = -1;
256 int current = -1;
257 int configured = -1;
258 int best = -1;
259
260 for (int i = modes.size() - 1; i >= 0; i--) {
261 const drmModeModeInfo &m = modes.at(i);
262
263 if (configuration == OutputConfigMode
264 && m.hdisplay == configurationSize.width()
265 && m.vdisplay == configurationSize.height()
266 && (!configurationRefresh || m.vrefresh == uint32_t(configurationRefresh)))
267 {
268 configured = i;
269 }
270
271 if (!memcmp(&crtc_mode, &m, sizeof m))
272 current = i;
273
274 if (m.type & DRM_MODE_TYPE_PREFERRED)
275 preferred = i;
276
277 best = i;
278 }
279
280 if (configuration == OutputConfigModeline) {
281 modes << configurationModeline;
282 configured = modes.size() - 1;
283 }
284
285 if (current < 0 && crtc_mode.clock != 0) {
286 modes << crtc_mode;
287 current = modes.size() - 1;
288 }
289
290 if (configuration == OutputConfigCurrent)
291 configured = current;
292
293 int selected_mode = -1;
294
295 if (configured >= 0)
296 selected_mode = configured;
297 else if (preferred >= 0)
298 selected_mode = preferred;
299 else if (current >= 0)
300 selected_mode = current;
301 else if (best >= 0)
302 selected_mode = best;
303
304 if (selected_mode < 0) {
305 qWarning() << "No modes available for output" << connectorName;
306 return nullptr;
307 } else {
308 int width = modes[selected_mode].hdisplay;
309 int height = modes[selected_mode].vdisplay;
310 int refresh = modes[selected_mode].vrefresh;
311 qCDebug(qLcKmsDebug) << "Selected mode" << selected_mode << ":" << width << "x" << height
312 << '@' << refresh << "hz for output" << connectorName;
313 }
314
315 // physical size from connector < config values < env vars
316 int pwidth = qEnvironmentVariableIntValue("QT_QPA_EGLFS_PHYSICAL_WIDTH");
317 if (!pwidth)
318 pwidth = qEnvironmentVariableIntValue("QT_QPA_PHYSICAL_WIDTH");
319 int pheight = qEnvironmentVariableIntValue("QT_QPA_EGLFS_PHYSICAL_HEIGHT");
320 if (!pheight)
321 pheight = qEnvironmentVariableIntValue("QT_QPA_PHYSICAL_HEIGHT");
322 QSizeF physSize(pwidth, pheight);
323 if (physSize.isEmpty()) {
324 physSize = QSize(userConnectorConfig.value(QStringLiteral("physicalWidth")).toInt(),
325 userConnectorConfig.value(QStringLiteral("physicalHeight")).toInt());
326 if (physSize.isEmpty()) {
327 physSize.setWidth(connector->mmWidth);
328 physSize.setHeight(connector->mmHeight);
329 }
330 }
331 qCDebug(qLcKmsDebug) << "Physical size is" << physSize << "mm" << "for output" << connectorName;
332
333 const QByteArray formatStr = userConnectorConfig.value(QStringLiteral("format"), QString())
334 .toByteArray().toLower();
335 uint32_t drmFormat;
336 bool drmFormatExplicit = true;
337 if (formatStr.isEmpty()) {
338 drmFormat = DRM_FORMAT_XRGB8888;
339 drmFormatExplicit = false;
340 } else if (formatStr == "xrgb8888") {
341 drmFormat = DRM_FORMAT_XRGB8888;
342 } else if (formatStr == "xbgr8888") {
343 drmFormat = DRM_FORMAT_XBGR8888;
344 } else if (formatStr == "argb8888") {
345 drmFormat = DRM_FORMAT_ARGB8888;
346 } else if (formatStr == "abgr8888") {
347 drmFormat = DRM_FORMAT_ABGR8888;
348 } else if (formatStr == "rgb565") {
349 drmFormat = DRM_FORMAT_RGB565;
350 } else if (formatStr == "bgr565") {
351 drmFormat = DRM_FORMAT_BGR565;
352 } else if (formatStr == "xrgb2101010") {
353 drmFormat = DRM_FORMAT_XRGB2101010;
354 } else if (formatStr == "xbgr2101010") {
355 drmFormat = DRM_FORMAT_XBGR2101010;
356 } else if (formatStr == "argb2101010") {
357 drmFormat = DRM_FORMAT_ARGB2101010;
358 } else if (formatStr == "abgr2101010") {
359 drmFormat = DRM_FORMAT_ABGR2101010;
360 } else {
361 qWarning("Invalid pixel format \"%s\" for output %s", formatStr.constData(), connectorName.constData());
362 drmFormat = DRM_FORMAT_XRGB8888;
363 drmFormatExplicit = false;
364 }
365 qCDebug(qLcKmsDebug) << "Format is" << Qt::hex << drmFormat << Qt::dec << "requested_by_user =" << drmFormatExplicit
366 << "for output" << connectorName;
367
368 const QString cloneSource = userConnectorConfig.value(QStringLiteral("clones")).toString();
369 if (!cloneSource.isEmpty())
370 qCDebug(qLcKmsDebug) << "Output" << connectorName << " clones output " << cloneSource;
371
372 QSize framebufferSize;
373 bool framebufferSizeSet = false;
374 const QByteArray fbsize = userConnectorConfig.value(QStringLiteral("size")).toByteArray().toLower();
375 if (!fbsize.isEmpty()) {
376 if (sscanf(fbsize.constData(), "%dx%d", &framebufferSize.rwidth(), &framebufferSize.rheight()) == 2) {
377#if QT_CONFIG(drm_atomic)
378 if (hasAtomicSupport())
379 framebufferSizeSet = true;
380#endif
381 if (!framebufferSizeSet)
382 qWarning("Setting framebuffer size is only available with DRM atomic API");
383 } else {
384 qWarning("Invalid framebuffer size '%s'", fbsize.constData());
385 }
386 }
387 if (!framebufferSizeSet) {
388 framebufferSize.setWidth(modes[selected_mode].hdisplay);
389 framebufferSize.setHeight(modes[selected_mode].vdisplay);
390 }
391
392 qCDebug(qLcKmsDebug) << "Output" << connectorName << "framebuffer size is " << framebufferSize;
393
395 output.name = QString::fromUtf8(connectorName);
396 output.connector_id = connector->connector_id;
397 output.crtc_index = crtc;
398 output.crtc_id = crtc_id;
399 output.physical_size = physSize;
400 output.preferred_mode = preferred >= 0 ? preferred : selected_mode;
401 output.mode = selected_mode;
402 output.mode_set = false;
403 output.saved_crtc = drmModeGetCrtc(m_dri_fd, crtc_id);
404 output.modes = modes;
405 output.subpixel = connector->subpixel;
406 output.dpms_prop = connectorProperty(connector, QByteArrayLiteral("DPMS"));
407 output.edid_blob = connectorPropertyBlob(connector, QByteArrayLiteral("EDID"));
408 output.wants_forced_plane = false;
409 output.forced_plane_id = 0;
410 output.forced_plane_set = false;
411 output.drm_format = drmFormat;
412 output.drm_format_requested_by_user = drmFormatExplicit;
413 output.clone_source = cloneSource;
414 output.size = framebufferSize;
415
416#if QT_CONFIG(drm_atomic)
417 if (drmModeCreatePropertyBlob(m_dri_fd, &modes[selected_mode], sizeof(drmModeModeInfo),
418 &output.mode_blob_id) != 0) {
419 qCDebug(qLcKmsDebug) << "Failed to create mode blob for mode" << selected_mode;
420 }
421
422 parseConnectorProperties(output.connector_id, &output);
424#endif
425
426 QString planeListStr;
427 for (QKmsPlane &plane : m_planes) {
428 if (plane.possibleCrtcs & (1 << output.crtc_index)) {
429 output.available_planes.append(plane);
430 planeListStr.append(QString::number(plane.id));
431 planeListStr.append(u' ');
432
433 // Choose the first primary plane that is not already assigned to
434 // another screen's associated crtc.
435 if (!output.eglfs_plane && plane.type == QKmsPlane::PrimaryPlane && !plane.activeCrtcId)
436 assignPlane(&output, &plane);
437 }
438 }
439 qCDebug(qLcKmsDebug, "Output %s can use %d planes: %s",
440 connectorName.constData(), int(output.available_planes.size()), qPrintable(planeListStr));
441
442 // This is for the EGLDevice/EGLStream backend. On some of those devices one
443 // may want to target a pre-configured plane. It is probably useless for
444 // eglfs_kms and others. Do not confuse with generic plane support (available_planes).
445 bool ok;
446 int idx = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_PLANE_INDEX", &ok);
447 if (ok) {
448 drmModePlaneRes *planeResources = drmModeGetPlaneResources(m_dri_fd);
449 if (planeResources) {
450 if (idx >= 0 && idx < int(planeResources->count_planes)) {
451 drmModePlane *plane = drmModeGetPlane(m_dri_fd, planeResources->planes[idx]);
452 if (plane) {
453 output.wants_forced_plane = true;
454 output.forced_plane_id = plane->plane_id;
455 qCDebug(qLcKmsDebug, "Forcing plane index %d, plane id %u (belongs to crtc id %u)",
456 idx, plane->plane_id, plane->crtc_id);
457
458 for (QKmsPlane &kmsplane : m_planes) {
459 if (kmsplane.id == output.forced_plane_id) {
460 assignPlane(&output, &kmsplane);
461 break;
462 }
463 }
464
465 drmModeFreePlane(plane);
466 }
467 } else {
468 qWarning("Invalid plane index %d, must be between 0 and %u", idx, planeResources->count_planes - 1);
469 }
470 }
471 }
472
473 // A more useful version: allows specifying "crtc_id,plane_id:crtc_id,plane_id:..."
474 // in order to allow overriding the plane used for a given crtc.
475 if (qEnvironmentVariableIsSet("QT_QPA_EGLFS_KMS_PLANES_FOR_CRTCS")) {
476 const QString val = qEnvironmentVariable("QT_QPA_EGLFS_KMS_PLANES_FOR_CRTCS");
477 qCDebug(qLcKmsDebug, "crtc_id:plane_id override list: %s", qPrintable(val));
478 const QStringList crtcPlanePairs = val.split(u':');
479 for (const QString &crtcPlanePair : crtcPlanePairs) {
480 const QStringList values = crtcPlanePair.split(u',');
481 if (values.size() == 2 && uint(values[0].toInt()) == output.crtc_id) {
482 uint planeId = values[1].toInt();
483 for (QKmsPlane &kmsplane : m_planes) {
484 if (kmsplane.id == planeId) {
485 assignPlane(&output, &kmsplane);
486 break;
487 }
488 }
489 }
490 }
491 }
492
493 if (output.eglfs_plane) {
494 qCDebug(qLcKmsDebug, "Chose plane %u for output %s (crtc id %u) (may not be applicable)",
495 output.eglfs_plane->id, connectorName.constData(), output.crtc_id);
496 }
497
498#if QT_CONFIG(drm_atomic)
499 if (hasAtomicSupport() && !output.eglfs_plane) {
500 qCDebug(qLcKmsDebug, "No plane associated with output %s (crtc id %u) and atomic modesetting is enabled. This is bad.",
501 connectorName.constData(), output.crtc_id);
502 }
503#endif
504
505 m_crtc_allocator |= (1 << output.crtc_index);
506
507 vinfo->output = output;
508
509 return createScreen(output);
510}
511
512drmModePropertyPtr QKmsDevice::connectorProperty(drmModeConnectorPtr connector, const QByteArray &name)
513{
514 drmModePropertyPtr prop;
515
516 for (int i = 0; i < connector->count_props; i++) {
517 prop = drmModeGetProperty(m_dri_fd, connector->props[i]);
518 if (!prop)
519 continue;
520 if (strcmp(prop->name, name.constData()) == 0)
521 return prop;
522 drmModeFreeProperty(prop);
523 }
524
525 return nullptr;
526}
527
528drmModePropertyBlobPtr QKmsDevice::connectorPropertyBlob(drmModeConnectorPtr connector, const QByteArray &name)
529{
530 drmModePropertyPtr prop;
531 drmModePropertyBlobPtr blob = nullptr;
532
533 for (int i = 0; i < connector->count_props && !blob; i++) {
534 prop = drmModeGetProperty(m_dri_fd, connector->props[i]);
535 if (!prop)
536 continue;
537 if ((prop->flags & DRM_MODE_PROP_BLOB) && (strcmp(prop->name, name.constData()) == 0))
538 blob = drmModeGetPropertyBlob(m_dri_fd, connector->prop_values[i]);
539 drmModeFreeProperty(prop);
540 }
541
542 return blob;
543}
544
546 : m_screenConfig(screenConfig)
547 , m_path(path)
548 , m_dri_fd(-1)
549 , m_has_atomic_support(false)
550 , m_crtc_allocator(0)
551{
552 if (m_path.isEmpty()) {
554 qCDebug(qLcKmsDebug, "Using DRM device %s specified in config file", qPrintable(m_path));
555 if (m_path.isEmpty())
556 qFatal("No DRM device given");
557 } else {
558 qCDebug(qLcKmsDebug, "Using backend-provided DRM device %s", qPrintable(m_path));
559 }
560}
561
563{
564#if QT_CONFIG(drm_atomic)
565 threadLocalAtomicReset();
566#endif
567}
568
577
579{
580 QDebugStateSaver saver(dbg);
581 dbg.nospace() << "OrderedScreen(QPlatformScreen=" << s.screen << " (" << s.screen->name() << ") : "
582 << s.vinfo.virtualIndex
583 << " / " << s.vinfo.virtualPos
584 << " / primary: " << s.vinfo.isPrimary
585 << ")";
586 return dbg;
587}
588
590{
591 return a.vinfo.virtualIndex < b.vinfo.virtualIndex;
592}
593
595{
596 // Headless mode using a render node: cannot do any output related DRM
597 // stuff. Skip it all and register a dummy screen.
598 if (m_screenConfig->headless()) {
600 if (screen) {
601 qCDebug(qLcKmsDebug, "Headless mode enabled");
602 registerScreen(screen, true, QPoint(0, 0), QList<QPlatformScreen *>());
603 return;
604 } else {
605 qWarning("QKmsDevice: Requested headless mode without support in the backend. Request is ignored.");
606 }
607 }
608
609 drmSetClientCap(m_dri_fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
610
611#if QT_CONFIG(drm_atomic)
612 // check atomic support
615 qCDebug(qLcKmsDebug, "Atomic reported as supported");
616 if (qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_ATOMIC")) {
617 qCDebug(qLcKmsDebug, "Atomic enabled");
618 } else {
619 qCDebug(qLcKmsDebug, "Atomic disabled");
620 m_has_atomic_support = false;
621 }
622 }
623#endif
624
625 drmModeResPtr resources = drmModeGetResources(m_dri_fd);
626 if (!resources) {
627 qErrnoWarning(errno, "drmModeGetResources failed");
628 return;
629 }
630
632
633 QList<OrderedScreen> screens;
634
635 int wantedConnectorIndex = -1;
636 bool ok;
637 int idx = qEnvironmentVariableIntValue("QT_QPA_EGLFS_KMS_CONNECTOR_INDEX", &ok);
638 if (ok) {
639 if (idx >= 0 && idx < resources->count_connectors)
640 wantedConnectorIndex = idx;
641 else
642 qWarning("Invalid connector index %d, must be between 0 and %u", idx, resources->count_connectors - 1);
643 }
644
645 for (int i = 0; i < resources->count_connectors; i++) {
646 if (wantedConnectorIndex >= 0 && i != wantedConnectorIndex)
647 continue;
648
649 drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, resources->connectors[i]);
650 if (!connector)
651 continue;
652
653 ScreenInfo vinfo;
654 QPlatformScreen *screen = createScreenForConnector(resources, connector, &vinfo);
655 if (screen)
656 screens.append(OrderedScreen(screen, vinfo));
657
658 drmModeFreeConnector(connector);
659 }
660
661 drmModeFreeResources(resources);
662
663 // Use stable sort to preserve the original (DRM connector) order
664 // for outputs with unspecified indices.
665 std::stable_sort(screens.begin(), screens.end(), orderedScreenLessThan);
666 qCDebug(qLcKmsDebug) << "Sorted screen list:" << screens;
667
668 // The final list of screens is available, so do the second phase setup.
669 // Hook up clone sources and targets.
670 for (const OrderedScreen &orderedScreen : screens) {
671 QList<QPlatformScreen *> screensCloningThisScreen;
672 for (const OrderedScreen &s : screens) {
673 if (s.vinfo.output.clone_source == orderedScreen.vinfo.output.name)
674 screensCloningThisScreen.append(s.screen);
675 }
676 QPlatformScreen *screenThisScreenClones = nullptr;
677 if (!orderedScreen.vinfo.output.clone_source.isEmpty()) {
678 for (const OrderedScreen &s : screens) {
679 if (s.vinfo.output.name == orderedScreen.vinfo.output.clone_source) {
680 screenThisScreenClones = s.screen;
681 break;
682 }
683 }
684 }
685 if (screenThisScreenClones)
686 qCDebug(qLcKmsDebug) << orderedScreen.screen->name() << "clones" << screenThisScreenClones;
687 if (!screensCloningThisScreen.isEmpty())
688 qCDebug(qLcKmsDebug) << orderedScreen.screen->name() << "is cloned by" << screensCloningThisScreen;
689
690 registerScreenCloning(orderedScreen.screen, screenThisScreenClones, screensCloningThisScreen);
691 }
692
693 // Figure out the virtual desktop and register the screens to QPA/QGuiApplication.
694 QPoint pos(0, 0);
695 QList<QPlatformScreen *> siblings;
696 QList<QPoint> virtualPositions;
697 int primarySiblingIdx = -1;
698
699 for (const OrderedScreen &orderedScreen : screens) {
700 QPlatformScreen *s = orderedScreen.screen;
701 QPoint virtualPos(0, 0);
702 // set up a horizontal or vertical virtual desktop
703 if (orderedScreen.vinfo.virtualPos.isNull()) {
704 virtualPos = pos;
706 pos.ry() += s->geometry().height();
707 else
708 pos.rx() += s->geometry().width();
709 } else {
710 virtualPos = orderedScreen.vinfo.virtualPos;
711 }
712 qCDebug(qLcKmsDebug) << "Adding QPlatformScreen" << s << "(" << s->name() << ")"
713 << "to QPA with geometry" << s->geometry()
714 << "and isPrimary=" << orderedScreen.vinfo.isPrimary;
715 // The order in qguiapp's screens list will match the order set by
716 // virtualIndex. This is not only handy but also required since for instance
717 // evdevtouch relies on it when performing touch device - screen mapping.
719 qCDebug(qLcKmsDebug) << " virtual position is" << virtualPos;
720 siblings.append(s);
721 virtualPositions.append(virtualPos);
722 if (orderedScreen.vinfo.isPrimary)
723 primarySiblingIdx = siblings.size() - 1;
724 } else {
725 registerScreen(s, orderedScreen.vinfo.isPrimary, virtualPos, QList<QPlatformScreen *>() << s);
726 }
727 }
728
730 // enable the virtual desktop
731 for (int i = 0; i < siblings.size(); ++i)
732 registerScreen(siblings[i], i == primarySiblingIdx, virtualPositions[i], siblings);
733 }
734}
735
737{
738 // headless mode not supported by default
739 return nullptr;
740}
741
742// not all subclasses support screen cloning
744 QPlatformScreen *screenThisScreenClones,
745 const QList<QPlatformScreen *> &screensCloningThisScreen)
746{
748 Q_UNUSED(screenThisScreenClones);
749 Q_UNUSED(screensCloningThisScreen);
750}
751
752// drm_property_type_is is not available in old headers
753static inline bool propTypeIs(drmModePropertyPtr prop, uint32_t type)
754{
755 if (prop->flags & DRM_MODE_PROP_EXTENDED_TYPE)
756 return (prop->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
757 return prop->flags & type;
758}
759
760void QKmsDevice::enumerateProperties(drmModeObjectPropertiesPtr objProps, PropCallback callback)
761{
762 for (uint32_t propIdx = 0; propIdx < objProps->count_props; ++propIdx) {
763 drmModePropertyPtr prop = drmModeGetProperty(m_dri_fd, objProps->props[propIdx]);
764 if (!prop)
765 continue;
766
767 const quint64 value = objProps->prop_values[propIdx];
768 qCDebug(qLcKmsDebug, " property %d: id = %u name = '%s'", propIdx, prop->prop_id, prop->name);
769
771 qCDebug(qLcKmsDebug, " type is SIGNED_RANGE, value is %lld, possible values are:", qint64(value));
772 for (int i = 0; i < prop->count_values; ++i)
773 qCDebug(qLcKmsDebug, " %lld", qint64(prop->values[i]));
774 } else if (propTypeIs(prop, DRM_MODE_PROP_RANGE)) {
775 qCDebug(qLcKmsDebug, " type is RANGE, value is %llu, possible values are:", value);
776 for (int i = 0; i < prop->count_values; ++i)
777 qCDebug(qLcKmsDebug, " %llu", quint64(prop->values[i]));
778 } else if (propTypeIs(prop, DRM_MODE_PROP_ENUM)) {
779 qCDebug(qLcKmsDebug, " type is ENUM, value is %llu, possible values are:", value);
780 for (int i = 0; i < prop->count_enums; ++i)
781 qCDebug(qLcKmsDebug, " enum %d: %s - %llu", i, prop->enums[i].name, quint64(prop->enums[i].value));
782 } else if (propTypeIs(prop, DRM_MODE_PROP_BITMASK)) {
783 qCDebug(qLcKmsDebug, " type is BITMASK, value is %llu, possible bits are:", value);
784 for (int i = 0; i < prop->count_enums; ++i)
785 qCDebug(qLcKmsDebug, " bitmask %d: %s - %u", i, prop->enums[i].name, 1 << prop->enums[i].value);
786 } else if (propTypeIs(prop, DRM_MODE_PROP_BLOB)) {
787 qCDebug(qLcKmsDebug, " type is BLOB");
788 } else if (propTypeIs(prop, DRM_MODE_PROP_OBJECT)) {
789 qCDebug(qLcKmsDebug, " type is OBJECT");
790 }
791
792 callback(prop, value);
793
794 drmModeFreeProperty(prop);
795 }
796}
797
799{
800 m_planes.clear();
801
802 drmModePlaneResPtr planeResources = drmModeGetPlaneResources(m_dri_fd);
803 if (!planeResources)
804 return;
805
806 const int countPlanes = planeResources->count_planes;
807 qCDebug(qLcKmsDebug, "Found %d planes", countPlanes);
808 for (int planeIdx = 0; planeIdx < countPlanes; ++planeIdx) {
809 drmModePlanePtr drmplane = drmModeGetPlane(m_dri_fd, planeResources->planes[planeIdx]);
810 if (!drmplane) {
811 qCDebug(qLcKmsDebug, "Failed to query plane %d, ignoring", planeIdx);
812 continue;
813 }
814
815 QKmsPlane plane;
816 plane.id = drmplane->plane_id;
817 plane.possibleCrtcs = drmplane->possible_crtcs;
818
819 const int countFormats = drmplane->count_formats;
820 QString formatStr;
821 for (int i = 0; i < countFormats; ++i) {
822 uint32_t f = drmplane->formats[i];
824 formatStr += QString::asprintf("%c%c%c%c ", f, f >> 8, f >> 16, f >> 24);
825 }
826
827 qCDebug(qLcKmsDebug, "plane %d: id = %u countFormats = %d possibleCrtcs = 0x%x supported formats = %s",
828 planeIdx, plane.id, countFormats, plane.possibleCrtcs, qPrintable(formatStr));
829
830 drmModeFreePlane(drmplane);
831
832 drmModeObjectPropertiesPtr objProps = drmModeObjectGetProperties(m_dri_fd, plane.id, DRM_MODE_OBJECT_PLANE);
833 if (!objProps) {
834 qCDebug(qLcKmsDebug, "Failed to query plane %d object properties, ignoring", planeIdx);
835 continue;
836 }
837
838 enumerateProperties(objProps, [&plane](drmModePropertyPtr prop, quint64 value) {
839 if (!strcmp(prop->name, "type")) {
840 plane.type = QKmsPlane::Type(value);
841 } else if (!strcmp(prop->name, "rotation")) {
842 plane.initialRotation = QKmsPlane::Rotations(int(value));
843 plane.availableRotations = { };
844 if (propTypeIs(prop, DRM_MODE_PROP_BITMASK)) {
845 for (int i = 0; i < prop->count_enums; ++i)
846 plane.availableRotations |= QKmsPlane::Rotation(1 << prop->enums[i].value);
847 }
848 plane.rotationPropertyId = prop->prop_id;
849 } else if (!strcasecmp(prop->name, "crtc_id")) {
850 plane.crtcPropertyId = prop->prop_id;
851 } else if (!strcasecmp(prop->name, "fb_id")) {
852 plane.framebufferPropertyId = prop->prop_id;
853 } else if (!strcasecmp(prop->name, "src_w")) {
854 plane.srcwidthPropertyId = prop->prop_id;
855 } else if (!strcasecmp(prop->name, "src_h")) {
856 plane.srcheightPropertyId = prop->prop_id;
857 } else if (!strcasecmp(prop->name, "crtc_w")) {
858 plane.crtcwidthPropertyId = prop->prop_id;
859 } else if (!strcasecmp(prop->name, "crtc_h")) {
860 plane.crtcheightPropertyId = prop->prop_id;
861 } else if (!strcasecmp(prop->name, "src_x")) {
862 plane.srcXPropertyId = prop->prop_id;
863 } else if (!strcasecmp(prop->name, "src_y")) {
864 plane.srcYPropertyId = prop->prop_id;
865 } else if (!strcasecmp(prop->name, "crtc_x")) {
866 plane.crtcXPropertyId = prop->prop_id;
867 } else if (!strcasecmp(prop->name, "crtc_y")) {
868 plane.crtcYPropertyId = prop->prop_id;
869 } else if (!strcasecmp(prop->name, "zpos")) {
870 plane.zposPropertyId = prop->prop_id;
871 } else if (!strcasecmp(prop->name, "blend_op")) {
872 plane.blendOpPropertyId = prop->prop_id;
873 }
874 });
875
876 m_planes.append(plane);
877
878 drmModeFreeObjectProperties(objProps);
879 }
880
881 drmModeFreePlaneResources(planeResources);
882}
883
884int QKmsDevice::fd() const
885{
886 return m_dri_fd;
887}
888
890{
891 return m_path;
892}
893
895{
896 m_dri_fd = fd;
897}
898
899
904
905#if QT_CONFIG(drm_atomic)
906drmModeAtomicReq *QKmsDevice::threadLocalAtomicRequest()
907{
909 return nullptr;
910
911 AtomicReqs &a(m_atomicReqs.localData());
912 if (!a.request)
913 a.request = drmModeAtomicAlloc();
914
915 return a.request;
916}
917
918bool QKmsDevice::threadLocalAtomicCommit(void *user_data)
919{
921 return false;
922
923 AtomicReqs &a(m_atomicReqs.localData());
924 if (!a.request)
925 return false;
926
927 int ret = drmModeAtomicCommit(m_dri_fd, a.request,
928 DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET,
929 user_data);
930
931 if (ret) {
932 qWarning("Failed to commit atomic request (code=%d)", ret);
933 return false;
934 }
935
936 a.previous_request = a.request;
937 a.request = nullptr;
938
939 return true;
940}
941
942void QKmsDevice::threadLocalAtomicReset()
943{
945 return;
946
947 AtomicReqs &a(m_atomicReqs.localData());
948 if (a.previous_request) {
949 drmModeAtomicFree(a.previous_request);
950 a.previous_request = nullptr;
951 }
952}
953#endif
954
956{
957 drmModeObjectPropertiesPtr objProps = drmModeObjectGetProperties(m_dri_fd, connectorId, DRM_MODE_OBJECT_CONNECTOR);
958 if (!objProps) {
959 qCDebug(qLcKmsDebug, "Failed to query connector %d object properties", connectorId);
960 return;
961 }
962
963 enumerateProperties(objProps, [output](drmModePropertyPtr prop, quint64 value) {
965 if (!strcasecmp(prop->name, "crtc_id"))
966 output->crtcIdPropertyId = prop->prop_id;
967 });
968
969 drmModeFreeObjectProperties(objProps);
970}
971
973{
974 drmModeObjectPropertiesPtr objProps = drmModeObjectGetProperties(m_dri_fd, crtcId, DRM_MODE_OBJECT_CRTC);
975 if (!objProps) {
976 qCDebug(qLcKmsDebug, "Failed to query crtc %d object properties", crtcId);
977 return;
978 }
979
980 enumerateProperties(objProps, [output](drmModePropertyPtr prop, quint64 value) {
982 if (!strcasecmp(prop->name, "mode_id"))
983 output->modeIdPropertyId = prop->prop_id;
984 else if (!strcasecmp(prop->name, "active"))
985 output->activePropertyId = prop->prop_id;
986 });
987
988 drmModeFreeObjectProperties(objProps);
989}
990
995
997 : m_headless(false)
998 , m_hwCursor(true)
999 , m_separateScreens(false)
1000 , m_pbuffers(false)
1001 , m_virtualDesktopLayout(VirtualDesktopLayoutHorizontal)
1002{
1003}
1004
1006{
1007 QByteArray json = qgetenv("QT_QPA_EGLFS_KMS_CONFIG");
1008 if (json.isEmpty()) {
1009 json = qgetenv("QT_QPA_KMS_CONFIG");
1010 if (json.isEmpty())
1011 return;
1012 }
1013
1014 qCDebug(qLcKmsDebug) << "Loading KMS setup from" << json;
1015
1017 if (!file.open(QFile::ReadOnly)) {
1018 qCWarning(qLcKmsDebug) << "Could not open config file"
1019 << json << "for reading";
1020 return;
1021 }
1022
1024 if (!doc.isObject()) {
1025 qCWarning(qLcKmsDebug) << "Invalid config file" << json
1026 << "- no top-level JSON object";
1027 return;
1028 }
1029
1030 const QJsonObject object = doc.object();
1031
1032 const QString headlessStr = object.value("headless"_L1).toString();
1033 const QByteArray headless = headlessStr.toUtf8();
1035 if (sscanf(headless.constData(), "%dx%d", &headlessSize.rwidth(), &headlessSize.rheight()) == 2) {
1036 m_headless = true;
1038 } else {
1039 m_headless = false;
1040 }
1041
1042 m_hwCursor = object.value("hwcursor"_L1).toBool(m_hwCursor);
1043 m_pbuffers = object.value("pbuffers"_L1).toBool(m_pbuffers);
1044 m_devicePath = object.value("device"_L1).toString();
1045 m_separateScreens = object.value("separateScreens"_L1).toBool(m_separateScreens);
1046
1047 const QString vdOriString = object.value("virtualDesktopLayout"_L1).toString();
1048 if (!vdOriString.isEmpty()) {
1049 if (vdOriString == "horizontal"_L1)
1051 else if (vdOriString == "vertical"_L1)
1053 else
1054 qCWarning(qLcKmsDebug) << "Unknown virtualDesktopOrientation value" << vdOriString;
1055 }
1056
1057 const QJsonArray outputs = object.value("outputs"_L1).toArray();
1058 for (int i = 0; i < outputs.size(); i++) {
1059 const QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap();
1060
1061 if (outputSettings.contains(QStringLiteral("name"))) {
1062 const QString name = outputSettings.value(QStringLiteral("name")).toString();
1063
1064 if (m_outputSettings.contains(name)) {
1065 qCDebug(qLcKmsDebug) << "Output" << name << "configured multiple times!";
1066 }
1067
1069 }
1070 }
1071
1072 qCDebug(qLcKmsDebug) << "Requested configuration (some settings may be ignored):\n"
1073 << "\theadless:" << m_headless << "\n"
1074 << "\thwcursor:" << m_hwCursor << "\n"
1075 << "\tpbuffers:" << m_pbuffers << "\n"
1076 << "\tseparateScreens:" << m_separateScreens << "\n"
1077 << "\tvirtualDesktopLayout:" << m_virtualDesktopLayout << "\n"
1078 << "\toutputs:" << m_outputSettings;
1079}
1080
1082{
1083 if (mode_set && saved_crtc) {
1084 drmModeSetCrtc(device->fd(),
1085 saved_crtc->crtc_id,
1086 saved_crtc->buffer_id,
1087 0, 0,
1088 &connector_id, 1,
1089 &saved_crtc->mode);
1090 mode_set = false;
1091 }
1092}
1093
1095{
1096 if (dpms_prop) {
1097 drmModeFreeProperty(dpms_prop);
1098 dpms_prop = nullptr;
1099 }
1100
1101 if (edid_blob) {
1102 drmModeFreePropertyBlob(edid_blob);
1103 edid_blob = nullptr;
1104 }
1105
1107
1108 if (saved_crtc) {
1109 drmModeFreeCrtc(saved_crtc);
1110 saved_crtc = nullptr;
1111 }
1112}
1113
1115{
1116 switch (subpixel) {
1117 default:
1118 case DRM_MODE_SUBPIXEL_UNKNOWN:
1119 case DRM_MODE_SUBPIXEL_NONE:
1121 case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
1123 case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
1125 case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
1127 case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
1129 }
1130}
1131
1133{
1134 if (dpms_prop)
1135 drmModeConnectorSetProperty(device->fd(), connector_id,
1136 dpms_prop->prop_id, (int) state);
1137}
1138
IOBluetoothDevice * device
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
QList< QByteArray > split(char sep) const
Splits the byte array into subarrays wherever sep occurs, and returns the list of those arrays.
bool isEmpty() const noexcept
Returns true if the byte array has size 0; otherwise returns false.
Definition qbytearray.h:107
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QByteArray toLower() const &
Definition qbytearray.h:254
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qfile.h:93
QFILE_MAYBE_NODISCARD bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:904
QByteArray readAll()
Reads all remaining data from the device, and returns it as a byte array.
\inmodule QtCore\reentrant
Definition qjsonarray.h:18
qsizetype size() const
Returns the number of values stored in the array.
QJsonValue at(qsizetype i) const
Returns a QJsonValue representing the value for index i.
\inmodule QtCore\reentrant
QJsonObject object() const
Returns the QJsonObject contained in the document.
bool isObject() const
Returns true if the document contains an object.
static QJsonDocument fromJson(const QByteArray &json, QJsonParseError *error=nullptr)
Parses json as a UTF-8 encoded JSON document, and creates a QJsonDocument from it.
\inmodule QtCore\reentrant
Definition qjsonobject.h:20
QVariantMap toVariantMap() const
Converts this object to a QVariantMap.
QJsonObject toObject() const
This is an overloaded member function, provided for convenience. It differs from the above function o...
quint32 m_crtc_allocator
void enumerateProperties(drmModeObjectPropertiesPtr objProps, PropCallback callback)
int fd() const
void discoverPlanes()
QList< QKmsPlane > m_planes
drmModePropertyPtr connectorProperty(drmModeConnectorPtr connector, const QByteArray &name)
void parseCrtcProperties(uint32_t crtcId, QKmsOutput *output)
void setFd(int fd)
QKmsScreenConfig * screenConfig() const
virtual QPlatformScreen * createHeadlessScreen()
virtual ~QKmsDevice()
QKmsScreenConfig * m_screenConfig
void createScreens()
virtual QPlatformScreen * createScreen(const QKmsOutput &output)=0
int crtcForConnector(drmModeResPtr resources, drmModeConnectorPtr connector)
std::function< void(drmModePropertyPtr, quint64)> PropCallback
void parseConnectorProperties(uint32_t connectorId, QKmsOutput *output)
QKmsDevice(QKmsScreenConfig *screenConfig, const QString &path=QString())
QString devicePath() const
QPlatformScreen * createScreenForConnector(drmModeResPtr resources, drmModeConnectorPtr connector, ScreenInfo *vinfo)
virtual void registerScreen(QPlatformScreen *screen, bool isPrimary, const QPoint &virtualPos, const QList< QPlatformScreen * > &virtualSiblings)=0
bool m_has_atomic_support
drmModePropertyBlobPtr connectorPropertyBlob(drmModeConnectorPtr connector, const QByteArray &name)
virtual void registerScreenCloning(QPlatformScreen *screen, QPlatformScreen *screenThisScreenClones, const QList< QPlatformScreen * > &screensCloningThisScreen)
QString m_path
bool hasAtomicSupport()
bool headless() const
QSize headlessSize() const
QMap< QString, QVariantMap > m_outputSettings
bool separateScreens() const
VirtualDesktopLayout virtualDesktopLayout() const
virtual void loadConfig()
VirtualDesktopLayout m_virtualDesktopLayout
QMap< QString, QVariantMap > outputSettings() const
QString devicePath() const
void append(parameter_type t)
Definition qlist.h:458
void clear()
Definition qlist.h:434
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:357
The QPlatformScreen class provides an abstraction for visual displays.
QScreen * screen() const
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int & ry() noexcept
Returns a reference to the y coordinate of this point.
Definition qpoint.h:160
\inmodule QtCore
Definition qsize.h:208
\inmodule QtCore
Definition qsize.h:25
constexpr int & rheight() noexcept
Returns a reference to the height.
Definition qsize.h:157
constexpr int & rwidth() noexcept
Returns a reference to the width.
Definition qsize.h:154
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
bool isEmpty() const noexcept
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:192
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1246
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:6018
static QString number(int, int base=10)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:8084
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7263
QString text
else opt state
[0]
void qErrnoWarning(const char *msg,...)
Combined button and popup list for selecting options.
QTextStream & hex(QTextStream &stream)
Calls QTextStream::setIntegerBase(16) on stream and returns stream.
QTextStream & dec(QTextStream &stream)
Calls QTextStream::setIntegerBase(10) on stream and returns stream.
#define QByteArrayLiteral(str)
Definition qbytearray.h:52
DBusConnection const char DBusError DBusBusType DBusError return DBusConnection DBusHandleMessageFunction void * user_data
EGLOutputLayerEXT EGLint EGLAttrib value
[5]
#define DRM_FORMAT_RGB565
#define DRM_FORMAT_ABGR8888
static const char *const connector_type_names[]
static void assignPlane(QKmsOutput *output, QKmsPlane *plane)
static bool orderedScreenLessThan(const OrderedScreen &a, const OrderedScreen &b)
static bool propTypeIs(drmModePropertyPtr prop, uint32_t type)
static QByteArray nameForConnector(const drmModeConnectorPtr connector)
QDebug operator<<(QDebug dbg, const OrderedScreen &s)
static bool parseModeline(const QByteArray &text, drmModeModeInfoPtr mode)
OutputConfiguration
@ OutputConfigPreferred
@ OutputConfigSkip
@ OutputConfigMode
@ OutputConfigOff
@ OutputConfigModeline
@ OutputConfigCurrent
#define ARRAY_LENGTH(a)
#define DRM_CLIENT_CAP_UNIVERSAL_PLANES
#define DRM_CLIENT_CAP_ATOMIC
#define DRM_MODE_PROP_SIGNED_RANGE
#define DRM_MODE_PROP_EXTENDED_TYPE
#define DRM_MODE_PROP_OBJECT
#define qWarning
Definition qlogging.h:166
#define qFatal
Definition qlogging.h:168
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
#define qCDebug(category,...)
return ret
GLenum GLsizei GLsizei GLint * values
[15]
GLboolean GLboolean GLboolean b
GLenum mode
const GLfloat * m
GLint GLsizei GLsizei height
GLboolean GLboolean GLboolean GLboolean a
[7]
GLfloat GLfloat f
GLint GLsizei width
GLenum type
GLuint64 GLenum GLint fd
GLuint name
GLdouble s
[6]
Definition qopenglext.h:235
GLsizei const GLchar *const * path
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1531
#define QStringLiteral(str)
QScreen * screen
[1]
Definition main.cpp:29
QString qEnvironmentVariable(const char *varName, const QString &defaultValue)
Q_CORE_EXPORT QByteArray qgetenv(const char *varName)
Q_CORE_EXPORT bool qEnvironmentVariableIsSet(const char *varName) noexcept
Q_CORE_EXPORT int qEnvironmentVariableIntValue(const char *varName, bool *ok=nullptr) noexcept
#define Q_UNUSED(x)
unsigned int quint32
Definition qtypes.h:50
unsigned long long quint64
Definition qtypes.h:61
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
static int toInt(const QChar &qc, int R)
QT_BEGIN_NAMESPACE typedef uchar * output
if(qFloatDistance(a, b)<(1<< 7))
[0]
QFile file
[0]
QObject::connect nullptr
QKmsDevice::ScreenInfo vinfo
QPlatformScreen * screen
OrderedScreen(QPlatformScreen *screen, const QKmsDevice::ScreenInfo &vinfo)
void restoreMode(QKmsDevice *device)
drmModePropertyPtr dpms_prop
void setPowerState(QKmsDevice *device, QPlatformScreen::PowerState state)
drmModeCrtcPtr saved_crtc
drmModePropertyBlobPtr edid_blob
QPlatformScreen::SubpixelAntialiasingType subpixelAntialiasingTypeHint() const
void cleanup(QKmsDevice *device)
uint32_t connector_id
uint32_t srcheightPropertyId
uint32_t zposPropertyId
uint32_t crtcXPropertyId
uint32_t activeCrtcId
uint32_t srcXPropertyId
uint32_t framebufferPropertyId
uint32_t crtcwidthPropertyId
uint32_t crtcPropertyId
uint32_t rotationPropertyId
uint32_t id
QList< uint32_t > supportedFormats
uint32_t srcYPropertyId
uint32_t crtcheightPropertyId
uint32_t crtcYPropertyId
int possibleCrtcs
uint32_t srcwidthPropertyId
uint32_t blendOpPropertyId
Rotations availableRotations
QT_BEGIN_NAMESPACE bool toBool(const QString &str)
Definition utils.h:14