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