Add workaround for broken Qt EGLFS card selection logic

This commit is contained in:
Cameron Gutman 2024-04-06 14:35:34 -05:00
parent f8c5d3c0ce
commit 011feab6ce
3 changed files with 67 additions and 1 deletions

View file

@ -10,7 +10,7 @@
#include <QFont> #include <QFont>
#include <QCursor> #include <QCursor>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QFile> #include <QTemporaryFile>
// Don't let SDL hook our main function, since Qt is already // Don't let SDL hook our main function, since Qt is already
// doing the same thing. This needs to be before any headers // doing the same thing. This needs to be before any headers
@ -351,6 +351,11 @@ int main(int argc, char *argv[])
SSL_free(nullptr); SSL_free(nullptr);
#endif #endif
// We keep this at function scope to ensure it stays around while we're running,
// becaue the Qt QPA will need to read it. Since the temporary file is only
// created when open() is called, this doesn't do any harm for other platforms.
QTemporaryFile eglfsConfigFile("eglfs_override_XXXXXX.conf");
// Avoid using High DPI on EGLFS. It breaks font rendering. // Avoid using High DPI on EGLFS. It breaks font rendering.
// https://bugreports.qt.io/browse/QTBUG-64377 // https://bugreports.qt.io/browse/QTBUG-64377
// //
@ -372,6 +377,7 @@ int main(int argc, char *argv[])
if (!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { if (!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) {
qInfo() << "Unable to detect Wayland or X11, so EGLFS will be used by default. Set QT_QPA_PLATFORM to override this."; qInfo() << "Unable to detect Wayland or X11, so EGLFS will be used by default. Set QT_QPA_PLATFORM to override this.";
qputenv("QT_QPA_PLATFORM", "eglfs"); qputenv("QT_QPA_PLATFORM", "eglfs");
qputenv("SDL_VIDEODRIVER", "kmsdrm");
if (!qEnvironmentVariableIsSet("QT_QPA_EGLFS_ALWAYS_SET_MODE")) { if (!qEnvironmentVariableIsSet("QT_QPA_EGLFS_ALWAYS_SET_MODE")) {
qInfo() << "Setting display mode by default. Set QT_QPA_EGLFS_ALWAYS_SET_MODE=0 to override this."; qInfo() << "Setting display mode by default. Set QT_QPA_EGLFS_ALWAYS_SET_MODE=0 to override this.";
@ -384,6 +390,17 @@ int main(int argc, char *argv[])
qWarning() << "Unable to find a KMSDRM display device!"; qWarning() << "Unable to find a KMSDRM display device!";
qWarning() << "On the Raspberry Pi, you must enable the 'fake KMS' driver in raspi-config to use Moonlight outside of the GUI environment."; qWarning() << "On the Raspberry Pi, you must enable the 'fake KMS' driver in raspi-config to use Moonlight outside of the GUI environment.";
} }
else if (!qEnvironmentVariableIsSet("QT_QPA_EGLFS_KMS_CONFIG")) {
// HACK: Remove this when Qt is fixed to properly check for display support before picking a card
QString cardOverride = WMUtils::getDrmCardOverride();
if (!cardOverride.isEmpty()) {
if (eglfsConfigFile.open()) {
qInfo() << "Overriding default Qt EGLFS card selection to" << cardOverride;
QTextStream(&eglfsConfigFile) << "{ \"device\": \"" << cardOverride << "\" }";
qputenv("QT_QPA_EGLFS_KMS_CONFIG", eglfsConfigFile.fileName().toUtf8());
}
}
}
} }
// EGLFS uses OpenGLES 2.0, so we will too. Some embedded platforms may not // EGLFS uses OpenGLES 2.0, so we will too. Some embedded platforms may not

View file

@ -1,5 +1,7 @@
#pragma once #pragma once
#include <QString>
#define THROW_BAD_ALLOC_IF_NULL(x) \ #define THROW_BAD_ALLOC_IF_NULL(x) \
if ((x) == nullptr) throw std::bad_alloc() if ((x) == nullptr) throw std::bad_alloc()
@ -8,4 +10,5 @@ namespace WMUtils {
bool isRunningWayland(); bool isRunningWayland();
bool isRunningWindowManager(); bool isRunningWindowManager();
bool isRunningDesktopEnvironment(); bool isRunningDesktopEnvironment();
QString getDrmCardOverride();
} }

View file

@ -1,4 +1,5 @@
#include <QtGlobal> #include <QtGlobal>
#include <QDir>
#include "utils.h" #include "utils.h"
@ -12,6 +13,11 @@
#include <wayland-client.h> #include <wayland-client.h>
#endif #endif
#ifdef HAVE_DRM
#include <xf86drm.h>
#include <xf86drmMode.h>
#endif
#define VALUE_SET 0x01 #define VALUE_SET 0x01
#define VALUE_TRUE 0x02 #define VALUE_TRUE 0x02
@ -96,3 +102,43 @@ bool WMUtils::isRunningDesktopEnvironment()
return isRunningWindowManager(); return isRunningWindowManager();
#endif #endif
} }
QString WMUtils::getDrmCardOverride()
{
#ifdef HAVE_DRM
QDir dir("/dev/dri");
QStringList cardList = dir.entryList(QStringList("card*"), QDir::Files | QDir::System);
if (cardList.length() == 0) {
return QString();
}
bool needsOverride = false;
for (const QString& card : cardList) {
QFile cardFd(dir.filePath(card));
if (!cardFd.open(QFile::ReadOnly)) {
continue;
}
auto resources = drmModeGetResources(cardFd.handle());
if (resources == nullptr) {
// If we find a card that doesn't have a display before a card that
// has one, we'll need to override Qt's EGLFS config because they
// don't properly handle cards without displays.
needsOverride = true;
}
else {
// We found a card with a display
drmModeFreeResources(resources);
if (needsOverride) {
// Override the default card with this one
return dir.filePath(card);
}
else {
return QString();
}
}
}
#endif
return QString();
}