188 drmModeConnectorPtr connector,
ScreenInfo &vinfo)
190 const QByteArray connectorName = nameForConnector(connector);
194 qWarning() <<
"No usable crtc/encoder pair for connector" << connectorName;
199 QSize configurationSize;
200 int configurationRefresh = 0;
201 drmModeModeInfo configurationModeline;
204 QVariantMap userConnectorConfig = userConfig.value(QString::fromUtf8(connectorName));
206 const QByteArray mode = userConnectorConfig.value(QStringLiteral(
"mode"), QStringLiteral(
"preferred"))
207 .toByteArray().toLower();
210 }
else if (mode ==
"preferred") {
212 }
else if (mode ==
"current") {
214 }
else if (mode ==
"skip") {
216 }
else if (sscanf(mode.constData(),
"%dx%d@%d", &configurationSize.rwidth(), &configurationSize.rheight(),
217 &configurationRefresh) == 3)
220 }
else if (sscanf(mode.constData(),
"%dx%d", &configurationSize.rwidth(), &configurationSize.rheight()) == 2) {
222 }
else if (parseModeline(mode, &configurationModeline)) {
225 qWarning(
"Invalid mode \"%s\" for output %s", mode.constData(), connectorName.constData());
229 vinfo
.virtualIndex = userConnectorConfig.value(QStringLiteral(
"virtualIndex"), INT_MAX).toInt();
230 if (userConnectorConfig.contains(QStringLiteral(
"virtualPos"))) {
231 const QByteArray vpos = userConnectorConfig.value(QStringLiteral(
"virtualPos")).toByteArray();
232 const QByteArrayList vposComp = vpos.split(
',');
233 if (vposComp.count() == 2) {
234 vinfo.virtualPos = QPoint(vposComp[0].trimmed().toInt(), vposComp[1].trimmed().toInt());
235 qCDebug(qLcKmsDebug) <<
"Parsing virtualPos to: " << vinfo.virtualPos;
237 vinfo.virtualPos = QPoint(-1, -1);
238 qCDebug(qLcKmsDebug) <<
"Could not parse virtualPos,"
239 <<
"will be calculated based on virtualIndex";
242 vinfo.virtualPos = QPoint(-1, -1);
245 if (userConnectorConfig.value(QStringLiteral(
"primary")).toBool())
248 const uint32_t crtc_id = resources->crtcs[crtc];
251 qCDebug(qLcKmsDebug) <<
"Turning off output" << connectorName;
252 drmModeSetCrtc(
m_dri_fd, crtc_id, 0, 0, 0, 0, 0,
nullptr);
258 qCDebug(qLcKmsDebug) <<
"Skipping disconnected output" << connectorName;
263 qCDebug(qLcKmsDebug) <<
"Skipping output" << connectorName;
268 drmModeModeInfo crtc_mode;
269 memset(&crtc_mode, 0,
sizeof crtc_mode);
270 if (drmModeEncoderPtr encoder = drmModeGetEncoder(
m_dri_fd, connector->encoder_id)) {
272 drmModeCrtcPtr crtc = drmModeGetCrtc(
m_dri_fd, encoder->crtc_id);
273 drmModeFreeEncoder(encoder);
278 if (crtc->mode_valid)
279 crtc_mode = crtc->mode;
281 drmModeFreeCrtc(crtc);
284 QList<drmModeModeInfo> modes;
285 modes.reserve(connector->count_modes);
286 qCDebug(qLcKmsDebug) << connectorName <<
"mode count:" << connector->count_modes
287 <<
"crtc index:" << crtc <<
"crtc id:" << crtc_id;
288 for (
int i = 0; i < connector->count_modes; i++) {
289 const drmModeModeInfo &mode = connector->modes[i];
290 qCDebug(qLcKmsDebug) <<
"mode" << i << mode.hdisplay <<
"x" << mode.vdisplay
291 <<
'@' << mode.vrefresh <<
"hz";
292 modes << connector->modes[i];
300 for (
int i = modes.size() - 1; i >= 0; i--) {
301 const drmModeModeInfo &m = modes.at(i);
304 && m.hdisplay == configurationSize.width()
305 && m.vdisplay == configurationSize.height()
306 && (!configurationRefresh || m.vrefresh == uint32_t(configurationRefresh)))
311 if (!memcmp(&crtc_mode, &m,
sizeof m))
314 if (m.type & DRM_MODE_TYPE_PREFERRED)
321 modes << configurationModeline;
322 configured = modes.size() - 1;
325 if (current < 0 && crtc_mode.clock != 0) {
327 current = modes.size() - 1;
331 configured = current;
333 int selected_mode = -1;
336 selected_mode = configured;
337 else if (preferred >= 0)
338 selected_mode = preferred;
339 else if (current >= 0)
340 selected_mode = current;
342 selected_mode = best;
344 if (selected_mode < 0) {
345 qWarning() <<
"No modes available for output" << connectorName;
348 int width = modes[selected_mode].hdisplay;
349 int height = modes[selected_mode].vdisplay;
350 int refresh = modes[selected_mode].vrefresh;
351 qCDebug(qLcKmsDebug) <<
"Selected mode" << selected_mode <<
":" << width <<
"x" << height
352 <<
'@' << refresh <<
"hz for output" << connectorName;
356 int pwidth = qEnvironmentVariableIntValue(
"QT_QPA_EGLFS_PHYSICAL_WIDTH");
358 pwidth = qEnvironmentVariableIntValue(
"QT_QPA_PHYSICAL_WIDTH");
359 int pheight = qEnvironmentVariableIntValue(
"QT_QPA_EGLFS_PHYSICAL_HEIGHT");
361 pheight = qEnvironmentVariableIntValue(
"QT_QPA_PHYSICAL_HEIGHT");
362 QSizeF physSize(pwidth, pheight);
363 if (physSize.isEmpty()) {
364 physSize = QSize(userConnectorConfig.value(QStringLiteral(
"physicalWidth")).toInt(),
365 userConnectorConfig.value(QStringLiteral(
"physicalHeight")).toInt());
366 if (physSize.isEmpty()) {
367 physSize.setWidth(connector->mmWidth);
368 physSize.setHeight(connector->mmHeight);
371 qCDebug(qLcKmsDebug) <<
"Physical size is" << physSize <<
"mm" <<
"for output" << connectorName;
373 const QByteArray formatStr = userConnectorConfig.value(QStringLiteral(
"format"), QString())
374 .toByteArray().toLower();
376 bool drmFormatExplicit =
true;
377 if (formatStr.isEmpty()) {
378 drmFormat = DRM_FORMAT_XRGB8888;
379 drmFormatExplicit =
false;
380 }
else if (formatStr ==
"xrgb8888") {
381 drmFormat = DRM_FORMAT_XRGB8888;
382 }
else if (formatStr ==
"xbgr8888") {
383 drmFormat = DRM_FORMAT_XBGR8888;
384 }
else if (formatStr ==
"argb8888") {
385 drmFormat = DRM_FORMAT_ARGB8888;
386 }
else if (formatStr ==
"abgr8888") {
387 drmFormat = DRM_FORMAT_ABGR8888;
388 }
else if (formatStr ==
"rgb565") {
389 drmFormat = DRM_FORMAT_RGB565;
390 }
else if (formatStr ==
"bgr565") {
391 drmFormat = DRM_FORMAT_BGR565;
392 }
else if (formatStr ==
"xrgb2101010") {
393 drmFormat = DRM_FORMAT_XRGB2101010;
394 }
else if (formatStr ==
"xbgr2101010") {
395 drmFormat = DRM_FORMAT_XBGR2101010;
396 }
else if (formatStr ==
"argb2101010") {
397 drmFormat = DRM_FORMAT_ARGB2101010;
398 }
else if (formatStr ==
"abgr2101010") {
399 drmFormat = DRM_FORMAT_ABGR2101010;
401 qWarning(
"Invalid pixel format \"%s\" for output %s", formatStr.constData(), connectorName.constData());
402 drmFormat = DRM_FORMAT_XRGB8888;
403 drmFormatExplicit =
false;
405 qCDebug(qLcKmsDebug) <<
"Format is" << Qt::hex << drmFormat << Qt::dec <<
"requested_by_user =" << drmFormatExplicit
406 <<
"for output" << connectorName;
408 const QString cloneSource = userConnectorConfig.value(QStringLiteral(
"clones")).toString();
409 if (!cloneSource.isEmpty())
410 qCDebug(qLcKmsDebug) <<
"Output" << connectorName <<
" clones output " << cloneSource;
412 QSize framebufferSize;
413 bool framebufferSizeSet =
false;
414 const QByteArray fbsize = userConnectorConfig.value(QStringLiteral(
"size")).toByteArray().toLower();
415 if (!fbsize.isEmpty()) {
416 if (sscanf(fbsize.constData(),
"%dx%d", &framebufferSize.rwidth(), &framebufferSize.rheight()) == 2) {
417#if QT_CONFIG(drm_atomic)
418 if (hasAtomicSupport())
419 framebufferSizeSet =
true;
421 if (!framebufferSizeSet)
422 qWarning(
"Setting framebuffer size is only available with DRM atomic API");
424 qWarning(
"Invalid framebuffer size '%s'", fbsize.constData());
427 if (!framebufferSizeSet) {
428 framebufferSize.setWidth(modes[selected_mode].hdisplay);
429 framebufferSize.setHeight(modes[selected_mode].vdisplay);
432 qCDebug(qLcKmsDebug) <<
"Output" << connectorName <<
"framebuffer size is " << framebufferSize;
435 output.name = QString::fromUtf8(connectorName);
439 output.physical_size = physSize;
441 output
.mode = selected_mode;
444 output.modes = modes;
446 output.dpms_prop = connectorProperty(connector, QByteArrayLiteral(
"DPMS"));
447 output.edid_blob = connectorPropertyBlob(connector, QByteArrayLiteral(
"EDID"));
451 output.drm_format = drmFormat;
453 output.clone_source = cloneSource;
454 output.size = framebufferSize;
456#if QT_CONFIG(drm_atomic)
457 if (drmModeCreatePropertyBlob(m_dri_fd, &modes[selected_mode],
sizeof(drmModeModeInfo),
458 &output.mode_blob_id) != 0) {
459 qCDebug(qLcKmsDebug) <<
"Failed to create mode blob for mode" << selected_mode;
462 parseConnectorProperties(output.connector_id, &output);
463 parseCrtcProperties(output.crtc_id, &output);
466 QString planeListStr;
467 for (QKmsPlane &plane : m_planes) {
468 if (plane.possibleCrtcs & (1u << output.crtc_index)) {
469 output.available_planes.append(plane);
470 planeListStr.append(QString::number(plane.id));
471 planeListStr.append(u' ');
475 if (!output.eglfs_plane && plane.type == QKmsPlane::PrimaryPlane && !plane.activeCrtcId)
476 assignPlane(&output, &plane);
479 qCDebug(qLcKmsDebug,
"Output %s can use %d planes: %s",
480 connectorName.constData(),
int(output.available_planes.size()), qPrintable(planeListStr));
486 int idx = qEnvironmentVariableIntValue(
"QT_QPA_EGLFS_KMS_PLANE_INDEX", &ok);
488 drmModePlaneRes *planeResources = drmModeGetPlaneResources(
m_dri_fd);
489 if (planeResources) {
490 if (idx >= 0 && idx <
int(planeResources->count_planes)) {
491 drmModePlane *plane = drmModeGetPlane(
m_dri_fd, planeResources->planes[idx]);
495 qCDebug(qLcKmsDebug,
"Forcing plane index %d, plane id %u (belongs to crtc id %u)",
496 idx, plane->plane_id, plane->crtc_id);
498 for (QKmsPlane &kmsplane : m_planes) {
499 if (kmsplane.id == output.forced_plane_id) {
500 assignPlane(&output, &kmsplane);
505 drmModeFreePlane(plane);
508 qWarning(
"Invalid plane index %d, must be between 0 and %u", idx, planeResources->count_planes - 1);
515 if (qEnvironmentVariableIsSet(
"QT_QPA_EGLFS_KMS_PLANES_FOR_CRTCS")) {
516 const QString val = qEnvironmentVariable(
"QT_QPA_EGLFS_KMS_PLANES_FOR_CRTCS");
517 qCDebug(qLcKmsDebug,
"crtc_id:plane_id override list: %s", qPrintable(val));
518 const QStringList crtcPlanePairs = val.split(u':');
519 for (
const QString &crtcPlanePair : crtcPlanePairs) {
520 const QStringList values = crtcPlanePair.split(u',');
521 if (values.size() == 2 && uint(values[0].toInt()) == output.crtc_id) {
522 uint planeId = values[1].toInt();
523 for (QKmsPlane &kmsplane : m_planes) {
524 if (kmsplane.id == planeId) {
525 assignPlane(&output, &kmsplane);
534 qCDebug(qLcKmsDebug,
"Chose plane %u for output %s (crtc id %u) (may not be applicable)",
538#if QT_CONFIG(drm_atomic)
539 if (hasAtomicSupport() && !output.eglfs_plane) {
540 qCDebug(qLcKmsDebug,
"No plane associated with output %s (crtc id %u) and atomic modesetting is enabled. This is bad.",
541 connectorName.constData(), output.crtc_id);
545 m_crtc_allocator |= (1u << output.crtc_index);
547 vinfo.output = output;
613 drmModeResPtr resources = drmModeGetResources(
m_dri_fd);
615 qErrnoWarning(errno,
"drmModeGetResources failed");
619 QList<uint32_t> newConnects;
620 QList<uint32_t> newDisconnects;
621 const QMap<QString, QVariantMap> userConfig =
m_screenConfig->outputSettings();
623 for (
int i = 0; i < resources->count_connectors; i++) {
624 drmModeConnectorPtr connector = drmModeGetConnector(
m_dri_fd, resources->connectors[i]);
626 qErrnoWarning(errno,
"drmModeGetConnector failed");
630 const uint32_t id = connector->connector_id;
632 const QByteArray connectorName = nameForConnector(connector);
633 const QVariantMap userCConfig = userConfig.value(QString::fromUtf8(connectorName));
634 const QByteArray mode = userCConfig.value(QStringLiteral(
"mode")).toByteArray().toLower();
635 if (mode ==
"off" || mode ==
"skip")
638 if (connector->connection == DRM_MODE_CONNECTED) {
639 if (!m_registeredScreens.contains(id))
640 newConnects.append(id);
642 qCDebug(qLcKmsDebug) <<
"Connected screen already registered: connector id=" << id;
645 if (connector->connection == DRM_MODE_DISCONNECTED) {
646 if (m_registeredScreens.contains(id))
647 newDisconnects.append(id);
649 qCDebug(qLcKmsDebug) <<
"Disconnected screen not registered: connector id=" << id;
652 drmModeFreeConnector(connector);
655 if (newConnects.isEmpty() && newDisconnects.isEmpty()) {
656 qCDebug(qLcKmsDebug) <<
"EGLFS/KMS: KMS-device-change but no new connects or disconnects "
657 <<
"to process - exiting";
660 qCDebug(qLcKmsDebug) <<
"EGLFS/KMS: KMS-device-change, new connects:" << newConnects
661 <<
", and disconnected: " << newDisconnects;
664 const int remainingScreenCount = m_registeredScreens.count() - newDisconnects.count();
665 if (remainingScreenCount == 0 && m_headlessScreen ==
nullptr) {
666 qCDebug(qLcKmsDebug) <<
"EGLFS/KMS: creating headless screen before"
667 <<
"unregistering screens to avoid having no screens";
668 m_headlessScreen = createHeadlessScreen();
669 registerScreen(m_headlessScreen,
true, QPoint(),
670 QList<QPlatformScreen *>() << m_headlessScreen);
673 for (uint32_t connectorId : std::as_const(newDisconnects)) {
674 OrderedScreen orderedScreen = m_registeredScreens.take(connectorId);
675 QPlatformScreen *screen = orderedScreen.screen;
679 uint32_t crtcId = (orderedScreen.vinfo.output.eglfs_plane !=
nullptr)
680 ? orderedScreen.vinfo.output.eglfs_plane->activeCrtcId
681 : orderedScreen.vinfo.output.crtc_id;
683 if (orderedScreen.vinfo.output.eglfs_plane !=
nullptr)
684 orderedScreen.vinfo.output.eglfs_plane->activeCrtcId = 0;
687 const int crtcIdx = orderedScreen.vinfo.output.crtc_index;
688 m_crtc_allocator &= ~(1u << crtcIdx);
690 const int ret = drmModeSetCrtc(m_dri_fd, crtcId, 0, 0, 0,
nullptr, 0,
nullptr);
693 qCWarning(qLcKmsDebug) <<
"Could not disable CRTC" << crtcId
694 <<
"on connector" << connectorId <<
"removal:" << ret;
696 qCDebug(qLcKmsDebug) <<
"Disabled CRTC" << crtcId
697 <<
"for connector " << connectorId <<
"disconnected";
701 if (orderedScreen.vinfo.output.saved_crtc) {
702 drmModeFreeCrtc(orderedScreen.vinfo.output.saved_crtc);
703 orderedScreen.vinfo.output.saved_crtc =
nullptr;
704 updateScreenOutput(orderedScreen.screen, orderedScreen.vinfo.output);
707 unregisterScreen(screen);
710 for (uint32_t connectorId : std::as_const(newConnects)) {
711 drmModeConnectorPtr connector = drmModeGetConnector(m_dri_fd, connectorId);
713 qErrnoWarning(errno,
"drmModeGetConnector failed");
718 bool succ = createScreenInfoForConnector(resources, connector, vinfo);
719 drmModeFreeConnector(connector);
723 QPlatformScreen *screen = createScreen(vinfo.output);
727 OrderedScreen orderedScreen(screen, vinfo);
728 m_registeredScreens[connectorId] = orderedScreen;
731 drmModeFreeResources(resources);
733 registerScreens(newConnects);
741 drmModeResPtr resources = drmModeGetResources(
m_dri_fd);
743 qErrnoWarning(errno,
"drmModeGetResources failed");
747 QList<uint32_t> newConnects;
748 QList<OrderedScreen> newDisconnects;
750 for (
int i = 0; i < resources->count_connectors; i++) {
751 drmModeConnectorPtr connector = drmModeGetConnector(
m_dri_fd, resources->connectors[i]);
755 if (m_registeredScreens.contains(connector->connector_id)) {
756 OrderedScreen &os = m_registeredScreens[connector->connector_id];
764 const int crtcIdx = os.vinfo.output.crtc_index;
765 m_crtc_allocator &= ~(1u << crtcIdx);
768 if (os.vinfo.output.eglfs_plane)
769 os.vinfo.output.eglfs_plane->activeCrtcId = 0;
774 drmModeCrtcPtr saved_saved_crtc =
nullptr;
775 if (os.vinfo.output.saved_crtc)
776 saved_saved_crtc = os.vinfo.output.saved_crtc;
786 if (os.vinfo.output.saved_crtc) {
787 drmModeFreeCrtc(os.vinfo.output.saved_crtc);
788 os.vinfo.output.saved_crtc =
nullptr;
789 updateScreenOutput(os.screen, os.vinfo.output);
794 newDisconnects.append(m_registeredScreens.take(connector->connector_id));
795 drmModeFreeConnector(connector);
798 drmModeFreeConnector(connector);
800 drmModeFreeCrtc(vinfo.output.saved_crtc);
801 vinfo.output.saved_crtc = saved_saved_crtc;
804 updateScreenOutput(os.screen, os.vinfo.output);
812 QPlatformScreen *screen = createScreen(vinfo.output);
814 m_registeredScreens[connector->connector_id] = orderedScreen;
815 newConnects.append(connector->connector_id);
820 if (m_registeredScreens.count() == 0 && m_headlessScreen ==
nullptr) {
822 m_headlessScreen = createHeadlessScreen();
823 registerScreen(m_headlessScreen,
true, QPoint(),
824 QList<QPlatformScreen *>() << m_headlessScreen);
828 registerScreens(newConnects);
831 for (
const OrderedScreen &os : newDisconnects)
832 unregisterScreen(os.screen);
834 drmModeFreeResources(resources);
842 QPlatformScreen *screen = createHeadlessScreen();
844 qCDebug(qLcKmsDebug,
"Headless mode enabled");
845 registerScreen(screen,
true, QPoint(0, 0),
846 QList<QPlatformScreen *>() << screen);
849 qWarning(
"QKmsDevice: Requested headless mode without support in the backend. Request is ignored.");
855#if QT_CONFIG(drm_atomic)
857 m_has_atomic_support = !drmSetClientCap(m_dri_fd, DRM_CLIENT_CAP_ATOMIC, 1);
858 if (m_has_atomic_support) {
859 qCDebug(qLcKmsDebug,
"Atomic reported as supported");
860 if (qEnvironmentVariableIntValue(
"QT_QPA_EGLFS_KMS_ATOMIC")) {
861 qCDebug(qLcKmsDebug,
"Atomic enabled");
863 qCDebug(qLcKmsDebug,
"Atomic disabled");
864 m_has_atomic_support =
false;
869 drmModeResPtr resources = drmModeGetResources(
m_dri_fd);
871 qErrnoWarning(errno,
"drmModeGetResources failed");
877 QList<uint32_t> newConnects;
878 int wantedConnectorIndex = -1;
880 int idx = qEnvironmentVariableIntValue(
"QT_QPA_EGLFS_KMS_CONNECTOR_INDEX", &ok);
882 if (idx >= 0 && idx < resources->count_connectors)
883 wantedConnectorIndex = idx;
885 qWarning(
"Invalid connector index %d, must be between 0 and %u", idx, resources->count_connectors - 1);
888 for (
int i = 0; i < resources->count_connectors; i++) {
889 if (wantedConnectorIndex >= 0 && i != wantedConnectorIndex)
892 drmModeConnectorPtr connector = drmModeGetConnector(
m_dri_fd, resources->connectors[i]);
894 qErrnoWarning(errno,
"drmModeGetConnector failed");
900 uint32_t connectorId = connector->connector_id;
901 drmModeFreeConnector(connector);
905 QPlatformScreen *screen = createScreen(vinfo.output);
910 m_registeredScreens[connectorId] = orderedScreen;
911 newConnects.append(connectorId);
914 drmModeFreeResources(resources);
916 if (!qEnvironmentVariable(
"QT_QPA_EGLFS_HOTPLUG_ENABLED").isEmpty()
917 && newConnects.empty() && m_headlessScreen ==
nullptr) {
918 qCDebug(qLcKmsDebug) <<
"'QT_QPA_EGLFS_HOTPLUG_ENABLED' was set and no screen was connected/found during start-up."
919 <<
"In order for Qt to operate properly a qt_headless screen will be created."
920 <<
"It will be automatically removed as soon as the first screen is connected";
922 m_headlessScreen = createHeadlessScreen();
923 registerScreen(m_headlessScreen,
true, QPoint(),
924 QList<QPlatformScreen *>() << m_headlessScreen);
927 registerScreens(newConnects);
932 QList<OrderedScreen> screens = m_registeredScreens.values();
936 std::stable_sort(screens.begin(), screens.end(), orderedScreenLessThan);
937 qCDebug(qLcKmsDebug) <<
"Sorted screen list:" << screens;
941 for (
const OrderedScreen &orderedScreen : std::as_const(screens)) {
942 QList<QPlatformScreen *> screensCloningThisScreen;
943 for (
const OrderedScreen &s : std::as_const(screens)) {
944 if (s.vinfo.output.clone_source == orderedScreen.vinfo.output.name)
945 screensCloningThisScreen.append(s.screen);
947 QPlatformScreen *screenThisScreenClones =
nullptr;
948 if (!orderedScreen.vinfo.output.clone_source.isEmpty()) {
949 for (
const OrderedScreen &s : std::as_const(screens)) {
950 if (s.vinfo.output.name == orderedScreen.vinfo.output.clone_source) {
951 screenThisScreenClones = s.screen;
956 if (screenThisScreenClones)
957 qCDebug(qLcKmsDebug) << orderedScreen.screen->name() <<
"clones" << screenThisScreenClones;
958 if (!screensCloningThisScreen.isEmpty())
959 qCDebug(qLcKmsDebug) << orderedScreen.screen->name() <<
"is cloned by" << screensCloningThisScreen;
961 registerScreenCloning(orderedScreen.screen, screenThisScreenClones, screensCloningThisScreen);
966 QList<OrderedScreen> siblings;
967 QList<QPoint> virtualPositions;
968 int primarySiblingIdx = -1;
971 for (
const OrderedScreen &orderedScreen : std::as_const(screens)) {
972 QPlatformScreen *s = orderedScreen.screen;
973 QPoint virtualPos(0, 0);
975 if (orderedScreen.vinfo.virtualPos.x() == -1 || orderedScreen.vinfo.virtualPos.y() == -1) {
976 if (orderedScreen.vinfo.output.clone_source.isEmpty()) {
978 if (m_screenConfig->virtualDesktopLayout() == QKmsScreenConfig::VirtualDesktopLayoutVertical)
979 pos.ry() += s->geometry().height();
981 pos.rx() += s->geometry().width();
983 for (
int i = 0; i < screens.count(); i++) {
984 const OrderedScreen &os = screens[i];
985 if (os.vinfo.output.name == orderedScreen.vinfo.output.clone_source) {
986 if (i >= virtualPositions.count()) {
987 qCWarning(qLcKmsDebug)
988 <<
"WARNING: When using clone on kms config,"
989 <<
"you have to either order your screens (virtualIndex),"
990 <<
"so clones come after their source,"
991 <<
"or specify 'virtualPos' for each clone."
992 <<
"Otherwise desktop-geomerty might not work properly!";
995 virtualPos = virtualPositions[i];
1002 virtualPos = orderedScreen.vinfo.virtualPos;
1008 if (!m_screenConfig->separateScreens()) {
1009 siblings.append(orderedScreen);
1010 virtualPositions.append(virtualPos);
1011 if (orderedScreen.vinfo.isPrimary)
1012 primarySiblingIdx = siblings.size() - 1;
1014 const bool isNewScreen = newConnects.contains(orderedScreen.vinfo.output.connector_id);
1016 qCDebug(qLcKmsDebug) <<
"Adding QPlatformScreen" << s <<
"(" << s->name() <<
")"
1017 <<
"to QPA with geometry" << s->geometry()
1018 <<
", virtual position" << virtualPos
1019 <<
"and isPrimary=" << orderedScreen.vinfo.isPrimary;
1020 registerScreen(s, orderedScreen.vinfo.isPrimary, virtualPos,
1021 QList<QPlatformScreen *>() << s);
1022 deskRegion += s->geometry();
1024 qCDebug(qLcKmsDebug) <<
"Updating QPlatformScreen" << s <<
"(" << s->name() <<
")"
1025 <<
"to QPA with geometry" << s->geometry()
1026 <<
", virtual position" << virtualPos
1027 <<
"and isPrimary=" << orderedScreen.vinfo.isPrimary;
1028 updateScreen(s, virtualPos, QList<QPlatformScreen *>() << s);
1029 deskRegion += s->geometry();
1035 QList<QPlatformScreen *> platformScreenSiblings;
1036 for (
int i = 0; i < siblings.count(); ++i) {
1037 platformScreenSiblings.append(siblings[i].screen);
1041 for (
int i = 0; i < siblings.count(); ++i) {
1042 QPlatformScreen *screen = platformScreenSiblings[i];
1044 const bool isNewScreen = newConnects.contains(orderedScreen.vinfo.output.connector_id);
1046 qCDebug(qLcKmsDebug) <<
"Adding QPlatformScreen" << screen
1047 <<
"(" << screen->name() <<
")"
1048 <<
"to QPA with geometry" << screen->geometry()
1049 <<
", virtual position" << virtualPositions[i]
1050 <<
"and isPrimary=" << orderedScreen.vinfo.isPrimary;
1051 registerScreen(screen, i == primarySiblingIdx, virtualPositions[i],
1052 platformScreenSiblings);
1053 deskRegion += screen->geometry();
1055 qCDebug(qLcKmsDebug) <<
"Updating QPlatformScreen" << screen
1056 <<
"(" << screen->name() <<
")"
1057 <<
"to QPA with geometry" << screen->geometry()
1058 <<
", virtual position" << virtualPositions[i]
1059 <<
"and isPrimary=" << orderedScreen.vinfo.isPrimary;
1060 updateScreen(screen, virtualPositions[i], platformScreenSiblings);
1061 deskRegion += screen->geometry();
1067 if (!m_registeredScreens.empty() && m_headlessScreen) {
1068 unregisterScreen(m_headlessScreen);
1069 m_headlessScreen =
nullptr;
1074 QPoint currCPos = QCursor::pos();
1075 if (!deskRegion.contains(currCPos)) {
1078 QRect deskRect = deskRegion.boundingRect();
1079 currCPos.setX(qMin(currCPos.x(), deskRect.width()) - 1);
1080 currCPos.setY(qMin(currCPos.y(), deskRect.height()) - 1);
1083 if (!deskRegion.contains(currCPos))
1084 currCPos = QPoint(0,0);
1086 qCDebug(qLcKmsDebug) <<
"Due to desktop layout change, overriding cursor pos."
1087 <<
"Is: " << QCursor::pos() <<
", will be: " << currCPos;
1088 QCursor::setPos(currCPos);
1177 drmModePlaneResPtr planeResources = drmModeGetPlaneResources(
m_dri_fd);
1178 if (!planeResources)
1181 const int countPlanes = planeResources->count_planes;
1182 qCDebug(qLcKmsDebug,
"Found %d planes", countPlanes);
1183 for (
int planeIdx = 0; planeIdx < countPlanes; ++planeIdx) {
1184 drmModePlanePtr drmplane = drmModeGetPlane(
m_dri_fd, planeResources->planes[planeIdx]);
1186 qCDebug(qLcKmsDebug,
"Failed to query plane %d, ignoring", planeIdx);
1191 plane.id = drmplane->plane_id;
1194 const int countFormats = drmplane->count_formats;
1196 for (
int i = 0; i < countFormats; ++i) {
1197 uint32_t f = drmplane->formats[i];
1198 plane.supportedFormats.append(f);
1199 formatStr += QString::asprintf(
"%c%c%c%c ", f, f >> 8, f >> 16, f >> 24);
1202 qCDebug(qLcKmsDebug,
"plane %d: id = %u countFormats = %d possibleCrtcs = 0x%x supported formats = %s",
1203 planeIdx, plane.id, countFormats, plane
.possibleCrtcs, qPrintable(formatStr));
1205 drmModeFreePlane(drmplane);
1207 drmModeObjectPropertiesPtr objProps = drmModeObjectGetProperties(
m_dri_fd, plane.id, DRM_MODE_OBJECT_PLANE);
1209 qCDebug(qLcKmsDebug,
"Failed to query plane %d object properties, ignoring", planeIdx);
1214 if (!strcmp(prop->name,
"type")) {
1216 }
else if (!strcmp(prop->name,
"rotation")) {
1217 plane.initialRotation = QKmsPlane::Rotations(
int(value));
1218 plane.availableRotations = { };
1219 if (propTypeIs(prop, DRM_MODE_PROP_BITMASK)) {
1220 for (
int i = 0; i < prop->count_enums; ++i)
1221 plane.availableRotations |= QKmsPlane::Rotation(1 << prop->enums[i].value);
1224 }
else if (!strcasecmp(prop->name,
"crtc_id")) {
1226 }
else if (!strcasecmp(prop->name,
"fb_id")) {
1228 }
else if (!strcasecmp(prop->name,
"src_w")) {
1230 }
else if (!strcasecmp(prop->name,
"src_h")) {
1232 }
else if (!strcasecmp(prop->name,
"crtc_w")) {
1234 }
else if (!strcasecmp(prop->name,
"crtc_h")) {
1236 }
else if (!strcasecmp(prop->name,
"src_x")) {
1238 }
else if (!strcasecmp(prop->name,
"src_y")) {
1240 }
else if (!strcasecmp(prop->name,
"crtc_x")) {
1242 }
else if (!strcasecmp(prop->name,
"crtc_y")) {
1244 }
else if (!strcasecmp(prop->name,
"zpos")) {
1246 }
else if (!strcasecmp(prop->name,
"blend_op")) {
1251 m_planes.append(plane);
1253 drmModeFreeObjectProperties(objProps);
1256 drmModeFreePlaneResources(planeResources);
1390 QByteArray json = qgetenv(
"QT_QPA_EGLFS_KMS_CONFIG");
1391 if (json.isEmpty()) {
1392 json = qgetenv(
"QT_QPA_KMS_CONFIG");
1397 qCDebug(qLcKmsDebug) <<
"Loading KMS setup from" << json;
1399 QFile file(QString::fromUtf8(json));
1400 if (!file.open(QFile::ReadOnly)) {
1401 qCWarning(qLcKmsDebug) <<
"Could not open config file"
1402 << json <<
"for reading";
1406 const QJsonDocument doc = QJsonDocument::fromJson(file.readAll());
1407 if (!doc.isObject()) {
1408 qCWarning(qLcKmsDebug) <<
"Invalid config file" << json
1409 <<
"- no top-level JSON object";
1413 const QJsonObject object = doc.object();
1415 const QString headlessStr = object.value(
"headless"_L1).toString();
1416 const QByteArray headless = headlessStr.toUtf8();
1418 if (sscanf(headless.constData(),
"%dx%d", &headlessSize.rwidth(), &headlessSize.rheight()) == 2) {
1420 m_headlessSize = headlessSize;
1425 const QString headlessSizeStr = object.value(QLatin1String(
"headlessSize")).toString();
1426 if (sscanf(headlessSizeStr.toUtf8().constData(),
"%dx%d", &headlessSize.rwidth(),
1427 &headlessSize.rheight()) == 2)
1428 m_headlessSize = headlessSize;
1432 m_devicePath = object.value(
"device"_L1).toString();
1435 const auto vdOriString = object.value(
"virtualDesktopLayout"_L1).toStringView();
1436 if (!vdOriString.isEmpty()) {
1437 if (vdOriString ==
"horizontal"_L1)
1439 else if (vdOriString ==
"vertical"_L1)
1442 qCWarning(qLcKmsDebug,
"Unknown virtualDesktopOrientation value %ls",
1443 qUtf16Printable(vdOriString.toString()));
1446 const QJsonArray outputs = object.value(
"outputs"_L1).toArray();
1447 for (
int i = 0; i < outputs.size(); i++) {
1448 const QVariantMap outputSettings = outputs.at(i).toObject().toVariantMap();
1450 if (outputSettings.contains(QStringLiteral(
"name"))) {
1451 const QString name = outputSettings.value(QStringLiteral(
"name")).toString();
1453 if (m_outputSettings.contains(name)) {
1454 qCDebug(qLcKmsDebug) <<
"Output" << name <<
"configured multiple times!";
1457 m_outputSettings.insert(name, outputSettings);
1461 qCDebug(qLcKmsDebug) <<
"Requested configuration (some settings may be ignored):\n"
1462 <<
"\theadless:" << m_headless <<
"\n"
1463 <<
"\thwcursor:" << m_hwCursor <<
"\n"
1464 <<
"\tpbuffers:" << m_pbuffers <<
"\n"
1465 <<
"\tseparateScreens:" << m_separateScreens <<
"\n"
1466 <<
"\tvirtualDesktopLayout:" << m_virtualDesktopLayout <<
"\n"
1467 <<
"\toutputs:" << m_outputSettings;