From 011feab6ceb1922e997cc9e8d82dd7387f9479a5 Mon Sep 17 00:00:00 2001 From: Cameron Gutman Date: Sat, 6 Apr 2024 14:35:34 -0500 Subject: [PATCH] Add workaround for broken Qt EGLFS card selection logic --- app/main.cpp | 19 ++++++++++++++++++- app/utils.h | 3 +++ app/wm.cpp | 46 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) diff --git a/app/main.cpp b/app/main.cpp index 67eac1d7..ca13eddc 100644 --- a/app/main.cpp +++ b/app/main.cpp @@ -10,7 +10,7 @@ #include #include #include -#include +#include // Don't let SDL hook our main function, since Qt is already // doing the same thing. This needs to be before any headers @@ -351,6 +351,11 @@ int main(int argc, char *argv[]) SSL_free(nullptr); #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. // https://bugreports.qt.io/browse/QTBUG-64377 // @@ -372,6 +377,7 @@ int main(int argc, char *argv[]) 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."; qputenv("QT_QPA_PLATFORM", "eglfs"); + qputenv("SDL_VIDEODRIVER", "kmsdrm"); 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."; @@ -384,6 +390,17 @@ int main(int argc, char *argv[]) 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."; } + 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 diff --git a/app/utils.h b/app/utils.h index c4e6a6ed..1a61231a 100644 --- a/app/utils.h +++ b/app/utils.h @@ -1,5 +1,7 @@ #pragma once +#include + #define THROW_BAD_ALLOC_IF_NULL(x) \ if ((x) == nullptr) throw std::bad_alloc() @@ -8,4 +10,5 @@ namespace WMUtils { bool isRunningWayland(); bool isRunningWindowManager(); bool isRunningDesktopEnvironment(); + QString getDrmCardOverride(); } diff --git a/app/wm.cpp b/app/wm.cpp index c244a377..542d664e 100644 --- a/app/wm.cpp +++ b/app/wm.cpp @@ -1,4 +1,5 @@ #include +#include #include "utils.h" @@ -12,6 +13,11 @@ #include #endif +#ifdef HAVE_DRM +#include +#include +#endif + #define VALUE_SET 0x01 #define VALUE_TRUE 0x02 @@ -96,3 +102,43 @@ bool WMUtils::isRunningDesktopEnvironment() return isRunningWindowManager(); #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(); +}