Qt 6.x
The Qt SDK
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
31};
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
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
570{
573 : screen(screen), vinfo(vinfo) { }
576};
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");
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);
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 siblings.append(s);
720 virtualPositions.append(virtualPos);
721 if (orderedScreen.vinfo.isPrimary)
722 primarySiblingIdx = siblings.size() - 1;
723 } else {
724 registerScreen(s, orderedScreen.vinfo.isPrimary, virtualPos, QList<QPlatformScreen *>() << s);
725 }
726 }
727
729 // enable the virtual desktop
730 for (int i = 0; i < siblings.size(); ++i)
731 registerScreen(siblings[i], i == primarySiblingIdx, virtualPositions[i], siblings);
732 }
733}
734
736{
737 // headless mode not supported by default
738 return nullptr;
739}
740
741// not all subclasses support screen cloning
743 QPlatformScreen *screenThisScreenClones,
744 const QList<QPlatformScreen *> &screensCloningThisScreen)
745{
747 Q_UNUSED(screenThisScreenClones);
748 Q_UNUSED(screensCloningThisScreen);
749}
750
751// drm_property_type_is is not available in old headers
752static inline bool propTypeIs(drmModePropertyPtr prop, uint32_t type)
753{
754 if (prop->flags & DRM_MODE_PROP_EXTENDED_TYPE)
755 return (prop->flags & DRM_MODE_PROP_EXTENDED_TYPE) == type;
756 return prop->flags & type;
757}
758
759void QKmsDevice::enumerateProperties(drmModeObjectPropertiesPtr objProps, PropCallback callback)
760{
761 for (uint32_t propIdx = 0; propIdx < objProps->count_props; ++propIdx) {
762 drmModePropertyPtr prop = drmModeGetProperty(m_dri_fd, objProps->props[propIdx]);
763 if (!prop)
764 continue;
765
766 const quint64 value = objProps->prop_values[propIdx];
767 qCDebug(qLcKmsDebug, " property %d: id = %u name = '%s'", propIdx, prop->prop_id, prop->name);
768
770 qCDebug(qLcKmsDebug, " type is SIGNED_RANGE, value is %lld, possible values are:", qint64(value));
771 for (int i = 0; i < prop->count_values; ++i)
772 qCDebug(qLcKmsDebug, " %lld", qint64(prop->values[i]));
773 } else if (propTypeIs(prop, DRM_MODE_PROP_RANGE)) {
774 qCDebug(qLcKmsDebug, " type is RANGE, value is %llu, possible values are:", value);
775 for (int i = 0; i < prop->count_values; ++i)
776 qCDebug(qLcKmsDebug, " %llu", quint64(prop->values[i]));
777 } else if (propTypeIs(prop, DRM_MODE_PROP_ENUM)) {
778 qCDebug(qLcKmsDebug, " type is ENUM, value is %llu, possible values are:", value);
779 for (int i = 0; i < prop->count_enums; ++i)
780 qCDebug(qLcKmsDebug, " enum %d: %s - %llu", i, prop->enums[i].name, quint64(prop->enums[i].value));
781 } else if (propTypeIs(prop, DRM_MODE_PROP_BITMASK)) {
782 qCDebug(qLcKmsDebug, " type is BITMASK, value is %llu, possible bits are:", value);
783 for (int i = 0; i < prop->count_enums; ++i)
784 qCDebug(qLcKmsDebug, " bitmask %d: %s - %u", i, prop->enums[i].name, 1 << prop->enums[i].value);
785 } else if (propTypeIs(prop, DRM_MODE_PROP_BLOB)) {
786 qCDebug(qLcKmsDebug, " type is BLOB");
787 } else if (propTypeIs(prop, DRM_MODE_PROP_OBJECT)) {
788 qCDebug(qLcKmsDebug, " type is OBJECT");
789 }
790
791 callback(prop, value);
792
793 drmModeFreeProperty(prop);
794 }
795}
796
798{
799 m_planes.clear();
800
801 drmModePlaneResPtr planeResources = drmModeGetPlaneResources(m_dri_fd);
802 if (!planeResources)
803 return;
804
805 const int countPlanes = planeResources->count_planes;
806 qCDebug(qLcKmsDebug, "Found %d planes", countPlanes);
807 for (int planeIdx = 0; planeIdx < countPlanes; ++planeIdx) {
808 drmModePlanePtr drmplane = drmModeGetPlane(m_dri_fd, planeResources->planes[planeIdx]);
809 if (!drmplane) {
810 qCDebug(qLcKmsDebug, "Failed to query plane %d, ignoring", planeIdx);
811 continue;
812 }
813
814 QKmsPlane plane;
815 plane.id = drmplane->plane_id;
816 plane.possibleCrtcs = drmplane->possible_crtcs;
817
818 const int countFormats = drmplane->count_formats;
819 QString formatStr;
820 for (int i = 0; i < countFormats; ++i) {
821 uint32_t f = drmplane->formats[i];
823 formatStr += QString::asprintf("%c%c%c%c ", f, f >> 8, f >> 16, f >> 24);
824 }
825
826 qCDebug(qLcKmsDebug, "plane %d: id = %u countFormats = %d possibleCrtcs = 0x%x supported formats = %s",
827 planeIdx, plane.id, countFormats, plane.possibleCrtcs, qPrintable(formatStr));
828
829 drmModeFreePlane(drmplane);
830
831 drmModeObjectPropertiesPtr objProps = drmModeObjectGetProperties(m_dri_fd, plane.id, DRM_MODE_OBJECT_PLANE);
832 if (!objProps) {
833 qCDebug(qLcKmsDebug, "Failed to query plane %d object properties, ignoring", planeIdx);
834 continue;
835 }
836
837 enumerateProperties(objProps, [&plane](drmModePropertyPtr prop, quint64 value) {
838 if (!strcmp(prop->name, "type")) {
839 plane.type = QKmsPlane::Type(value);
840 } else if (!strcmp(prop->name, "rotation")) {
841 plane.initialRotation = QKmsPlane::Rotations(int(value));
842 plane.availableRotations = { };
843 if (propTypeIs(prop, DRM_MODE_PROP_BITMASK)) {
844 for (int i = 0; i < prop->count_enums; ++i)
845 plane.availableRotations |= QKmsPlane::Rotation(1 << prop->enums[i].value);
846 }
847 plane.rotationPropertyId = prop->prop_id;
848 } else if (!strcasecmp(prop->name, "crtc_id")) {
849 plane.crtcPropertyId = prop->prop_id;
850 } else if (!strcasecmp(prop->name, "fb_id")) {
851 plane.framebufferPropertyId = prop->prop_id;
852 } else if (!strcasecmp(prop->name, "src_w")) {
853 plane.srcwidthPropertyId = prop->prop_id;
854 } else if (!strcasecmp(prop->name, "src_h")) {
855 plane.srcheightPropertyId = prop->prop_id;
856 } else if (!strcasecmp(prop->name, "crtc_w")) {
857 plane.crtcwidthPropertyId = prop->prop_id;
858 } else if (!strcasecmp(prop->name, "crtc_h")) {
859 plane.crtcheightPropertyId = prop->prop_id;
860 } else if (!strcasecmp(prop->name, "src_x")) {
861 plane.srcXPropertyId = prop->prop_id;
862 } else if (!strcasecmp(prop->name, "src_y")) {
863 plane.srcYPropertyId = prop->prop_id;
864 } else if (!strcasecmp(prop->name, "crtc_x")) {
865 plane.crtcXPropertyId = prop->prop_id;
866 } else if (!strcasecmp(prop->name, "crtc_y")) {
867 plane.crtcYPropertyId = prop->prop_id;
868 } else if (!strcasecmp(prop->name, "zpos")) {
869 plane.zposPropertyId = prop->prop_id;
870 } else if (!strcasecmp(prop->name, "blend_op")) {
871 plane.blendOpPropertyId = prop->prop_id;
872 }
873 });
874
875 m_planes.append(plane);
876
877 drmModeFreeObjectProperties(objProps);
878 }
879
880 drmModeFreePlaneResources(planeResources);
881}
882
883int QKmsDevice::fd() const
884{
885 return m_dri_fd;
886}
887
889{
890 return m_path;
891}
892
894{
895 m_dri_fd = fd;
896}
897
898
900{
902}
903
904#if QT_CONFIG(drm_atomic)
905drmModeAtomicReq *QKmsDevice::threadLocalAtomicRequest()
906{
908 return nullptr;
909
910 AtomicReqs &a(m_atomicReqs.localData());
911 if (!a.request)
912 a.request = drmModeAtomicAlloc();
913
914 return a.request;
915}
916
917bool QKmsDevice::threadLocalAtomicCommit(void *user_data)
918{
920 return false;
921
922 AtomicReqs &a(m_atomicReqs.localData());
923 if (!a.request)
924 return false;
925
926 int ret = drmModeAtomicCommit(m_dri_fd, a.request,
927 DRM_MODE_ATOMIC_NONBLOCK | DRM_MODE_PAGE_FLIP_EVENT | DRM_MODE_ATOMIC_ALLOW_MODESET,
928 user_data);
929
930 if (ret) {
931 qWarning("Failed to commit atomic request (code=%d)", ret);
932 return false;
933 }
934
935 a.previous_request = a.request;
936 a.request = nullptr;
937
938 return true;
939}
940
941void QKmsDevice::threadLocalAtomicReset()
942{
944 return;
945
946 AtomicReqs &a(m_atomicReqs.localData());
947 if (a.previous_request) {
948 drmModeAtomicFree(a.previous_request);
949 a.previous_request = nullptr;
950 }
951}
952#endif
953
955{
956 drmModeObjectPropertiesPtr objProps = drmModeObjectGetProperties(m_dri_fd, connectorId, DRM_MODE_OBJECT_CONNECTOR);
957 if (!objProps) {
958 qCDebug(qLcKmsDebug, "Failed to query connector %d object properties", connectorId);
959 return;
960 }
961
962 enumerateProperties(objProps, [output](drmModePropertyPtr prop, quint64 value) {
964 if (!strcasecmp(prop->name, "crtc_id"))
965 output->crtcIdPropertyId = prop->prop_id;
966 });
967
968 drmModeFreeObjectProperties(objProps);
969}
970
972{
973 drmModeObjectPropertiesPtr objProps = drmModeObjectGetProperties(m_dri_fd, crtcId, DRM_MODE_OBJECT_CRTC);
974 if (!objProps) {
975 qCDebug(qLcKmsDebug, "Failed to query crtc %d object properties", crtcId);
976 return;
977 }
978
979 enumerateProperties(objProps, [output](drmModePropertyPtr prop, quint64 value) {
981 if (!strcasecmp(prop->name, "mode_id"))
982 output->modeIdPropertyId = prop->prop_id;
983 else if (!strcasecmp(prop->name, "active"))
984 output->activePropertyId = prop->prop_id;
985 });
986
987 drmModeFreeObjectProperties(objProps);
988}
989
991{
992 return m_screenConfig;
993}
994
996 : m_headless(false)
997 , m_hwCursor(true)
998 , m_separateScreens(false)
999 , m_pbuffers(false)
1000 , m_virtualDesktopLayout(VirtualDesktopLayoutHorizontal)
1001{
1002}
1003
1005{
1006 QByteArray json = qgetenv("QT_QPA_EGLFS_KMS_CONFIG");
1007 if (json.isEmpty()) {
1008 json = qgetenv("QT_QPA_KMS_CONFIG");
1009 if (json.isEmpty())
1010 return;
1011 }
1012
1013 qCDebug(qLcKmsDebug) << "Loading KMS setup from" << json;
1014
1016 if (!file.open(QFile::ReadOnly)) {
1017 qCWarning(qLcKmsDebug) << "Could not open config file"
1018 << json << "for reading";
1019 return;
1020 }
1021
1023 if (!doc.isObject()) {
1024 qCWarning(qLcKmsDebug) << "Invalid config file" << json
1025 << "- no top-level JSON object";
1026 return;
1027 }
1028
1029 const QJsonObject object = doc.object();
1030
1031 const QString headlessStr = object.value("headless"_L1).toString();
1032 const QByteArray headless = headlessStr.toUtf8();
1034 if (sscanf(headless.constData(), "%dx%d", &headlessSize.rwidth(), &headlessSize.rheight()) == 2) {
1035 m_headless = true;
1037 } else {
1038 m_headless = false;
1039 }
1040
1041 m_hwCursor = object.value("hwcursor"_L1).toBool(m_hwCursor);
1042 m_pbuffers = object.value("pbuffers"_L1).toBool(m_pbuffers);
1043 m_devicePath = object.value("device"_L1).toString();
1044 m_separateScreens = object.value("separateScreens"_L1).toBool(m_separateScreens);
1045
1046 const QString vdOriString = object.value("virtualDesktopLayout"_L1).toString();
1047 if (!vdOriString.isEmpty()) {
1048 if (vdOriString == "horizontal"_L1)
1050 else if (vdOriString == "vertical"_L1)
1052 else
1053 qCWarning(qLcKmsDebug) << "Unknown virtualDesktopOrientation value" << vdOriString;
1054 }
1055
1056 const QJsonArray outputs = object.value("outputs"_L1).toArray();
1057 for (int i = 0; i < outputs.size(); i++) {
1058 const QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap();
1059
1060 if (outputSettings.contains(QStringLiteral("name"))) {
1061 const QString name = outputSettings.value(QStringLiteral("name")).toString();
1062
1063 if (m_outputSettings.contains(name)) {
1064 qCDebug(qLcKmsDebug) << "Output" << name << "configured multiple times!";
1065 }
1066
1068 }
1069 }
1070
1071 qCDebug(qLcKmsDebug) << "Requested configuration (some settings may be ignored):\n"
1072 << "\theadless:" << m_headless << "\n"
1073 << "\thwcursor:" << m_hwCursor << "\n"
1074 << "\tpbuffers:" << m_pbuffers << "\n"
1075 << "\tseparateScreens:" << m_separateScreens << "\n"
1076 << "\tvirtualDesktopLayout:" << m_virtualDesktopLayout << "\n"
1077 << "\toutputs:" << m_outputSettings;
1078}
1079
1081{
1082 if (mode_set && saved_crtc) {
1083 drmModeSetCrtc(device->fd(),
1084 saved_crtc->crtc_id,
1085 saved_crtc->buffer_id,
1086 0, 0,
1087 &connector_id, 1,
1088 &saved_crtc->mode);
1089 mode_set = false;
1090 }
1091}
1092
1094{
1095 if (dpms_prop) {
1096 drmModeFreeProperty(dpms_prop);
1097 dpms_prop = nullptr;
1098 }
1099
1100 if (edid_blob) {
1101 drmModeFreePropertyBlob(edid_blob);
1102 edid_blob = nullptr;
1103 }
1104
1106
1107 if (saved_crtc) {
1108 drmModeFreeCrtc(saved_crtc);
1109 saved_crtc = nullptr;
1110 }
1111}
1112
1114{
1115 switch (subpixel) {
1116 default:
1117 case DRM_MODE_SUBPIXEL_UNKNOWN:
1118 case DRM_MODE_SUBPIXEL_NONE:
1120 case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
1122 case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
1124 case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
1126 case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
1128 }
1129}
1130
1132{
1133 if (dpms_prop)
1134 drmModeConnectorSetProperty(device->fd(), connector_id,
1135 dpms_prop->prop_id, (int) state);
1136}
1137
IOBluetoothDevice * device
\inmodule QtCore
\inmodule QtCore
Definition qbytearray.h:57
const char * constData() const noexcept
Returns a pointer to the const data stored in the byte array.
Definition qbytearray.h:122
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:106
static QByteArray number(int, int base=10)
Returns a byte-array representing the whole number n as text.
QByteArray toLower() const &
Definition qbytearray.h:190
\inmodule QtCore
\inmodule QtCore
\inmodule QtCore
Definition qfile.h:93
bool open(OpenMode flags) override
Opens the file using OpenMode mode, returning true if successful; otherwise false.
Definition qfile.cpp:881
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
Definition qlist.h:74
qsizetype size() const noexcept
Definition qlist.h:386
bool isEmpty() const noexcept
Definition qlist.h:390
iterator end()
Definition qlist.h:609
const_reference at(qsizetype i) const noexcept
Definition qlist.h:429
iterator begin()
Definition qlist.h:608
void reserve(qsizetype size)
Definition qlist.h:746
void append(parameter_type t)
Definition qlist.h:441
void clear()
Definition qlist.h:417
T value(const Key &key, const T &defaultValue=T()) const
Definition qmap.h:356
bool contains(const Key &key) const
Definition qmap.h:340
The QPlatformScreen class provides an abstraction for visual displays.
QScreen * screen() const
\inmodule QtCore\reentrant
Definition qpoint.h:23
\inmodule QtCore
Definition qsize.h:207
constexpr void setHeight(qreal h) noexcept
Sets the height to the given finite height.
Definition qsize.h:330
constexpr void setWidth(qreal w) noexcept
Sets the width to the given finite width.
Definition qsize.h:327
constexpr bool isEmpty() const noexcept
Returns true if either of the width and height is less than or equal to 0; otherwise returns false.
Definition qsize.h:315
\inmodule QtCore
Definition qsize.h:25
constexpr int height() const noexcept
Returns the height.
Definition qsize.h:132
constexpr int width() const noexcept
Returns the width.
Definition qsize.h:129
constexpr int & rheight() noexcept
Returns a reference to the height.
Definition qsize.h:156
constexpr void setWidth(int w) noexcept
Sets the width to the given width.
Definition qsize.h:135
constexpr int & rwidth() noexcept
Returns a reference to the width.
Definition qsize.h:153
constexpr void setHeight(int h) noexcept
Sets the height to the given height.
Definition qsize.h:138
\inmodule QtCore
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:127
const QChar * constData() const
Returns a pointer to the data stored in the QString.
Definition qstring.h:1101
static QString fromUtf8(QByteArrayView utf8)
This is an overloaded member function, provided for convenience. It differs from the above function o...
Definition qstring.cpp:5857
bool isEmpty() const
Returns true if the string has no characters; otherwise returns false.
Definition qstring.h:1083
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:7822
QString & append(QChar c)
Definition qstring.cpp:3227
QByteArray toUtf8() const &
Definition qstring.h:563
static QString static QString asprintf(const char *format,...) Q_ATTRIBUTE_FORMAT_PRINTF(1
Definition qstring.cpp:7005
int toInt(bool *ok=nullptr) const
Returns the variant as an int if the variant has userType() \l QMetaType::Int, \l QMetaType::Bool,...
QString toString() const
Returns the variant as a QString if the variant has a userType() including, but not limited to:
QByteArray toByteArray() const
Returns the variant as a QByteArray if the variant has userType() \l QMetaType::QByteArray or \l QMet...
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:162
#define qFatal
Definition qlogging.h:164
#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
GLuint GLfloat * val
GLsizei const GLchar *const * path
GLdouble s
[6]
Definition qopenglext.h:235
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define qPrintable(string)
Definition qstring.h:1391
#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:45
unsigned long long quint64
Definition qtypes.h:56
unsigned int uint
Definition qtypes.h:29
long long qint64
Definition qtypes.h:55
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