fix(linux): fall back when display is inaccessible (#189)

This commit is contained in:
Dave Lane authored and GitHub committed 2026-09-17 11:34:17 -04:00
1 parent e479042a0e
commit c1f2a8ab56
3 files changed
+115 -6

No files matched your search

+47 -6
View File
@@ -4,6 +4,8 @@
*/
// standard includes
#include <memory>
#include <string>
#include <string_view>
// qt includes
#include <QByteArray>
@@ -16,6 +18,7 @@
// local includes
#include "QtTrayMenu.h"
#include "tray.h"
#include "tray_qt.h"
namespace tray_qt {
/**
@@ -91,16 +94,54 @@ namespace tray_qt {
current_state.trayMenu->configureAppMetadata(current_state.appName, current_state.appDisplayName, current_state.desktopName);
}
std::string select_platform_plugins(
const std::string_view requested_platform,
const std::string_view session_type,
const std::string_view wayland_display,
const std::string_view x11_display
) {
if (!requested_platform.empty()) {
return std::string {requested_platform};
}
std::string platforms;
const auto append_platform = [&platforms](const std::string_view platform) {
if (!platforms.empty()) {
platforms.push_back(';');
}
platforms.append(platform);
};
const bool prefer_x11 = session_type == "x11" || session_type == "X11";
if (prefer_x11 && !x11_display.empty()) {
append_platform("xcb");
}
if (!wayland_display.empty()) {
append_platform("wayland");
}
if (!prefer_x11 && !x11_display.empty()) {
append_platform("xcb");
}
append_platform("minimal");
return platforms;
}
/**
* @brief Configure Linux headless fallback for Qt.
* @brief Configure Linux Qt platform candidates with a headless fallback.
*/
void configure_platform() {
#if defined(__linux__)
// Check if a (wayland_)display is set or fallback to minimal QPA platform
if (qgetenv("WAYLAND_DISPLAY").isEmpty() && qgetenv("DISPLAY").isEmpty()) {
// Force fallback to QT platform minimal if no (WAYLAND_)DISPLAY was found
qputenv("QT_QPA_PLATFORM", QByteArrayLiteral("minimal"));
qWarning("QtTrayMenu: no reachable WAYLAND_DISPLAY or DISPLAY endpoint, forcing QT_QPA_PLATFORM=minimal");
const auto requested_platform = qgetenv("QT_QPA_PLATFORM");
const auto platforms = select_platform_plugins(
requested_platform.toStdString(),
qgetenv("XDG_SESSION_TYPE").toStdString(),
qgetenv("WAYLAND_DISPLAY").toStdString(),
qgetenv("DISPLAY").toStdString()
);
if (requested_platform.isEmpty()) {
const auto qt_platforms = QByteArray::fromStdString(platforms);
qputenv("QT_QPA_PLATFORM", qt_platforms);
qDebug() << "QtTrayMenu: configured platform fallback chain:" << qt_platforms;
}
#endif
}
+31
View File
@@ -0,0 +1,31 @@
/**
* @file src/tray_qt.h
* @brief Internal helpers for the Qt system tray implementation.
*/
#pragma once
// standard includes
#include <string>
#include <string_view>
namespace tray_qt {
/**
* @brief Select the ordered Qt platform plugin candidates for Linux.
*
* An explicit platform selection is preserved. Otherwise, available desktop
* environment hints determine whether Wayland or X11 is tried first, and the
* headless minimal plugin is retained as the final fallback.
*
* @param requested_platform Explicit QT_QPA_PLATFORM value.
* @param session_type XDG session type, such as `wayland` or `x11`.
* @param wayland_display WAYLAND_DISPLAY value.
* @param x11_display DISPLAY value.
* @return Semicolon-separated Qt platform plugin candidates.
*/
std::string select_platform_plugins(
std::string_view requested_platform,
std::string_view session_type,
std::string_view wayland_display,
std::string_view x11_display
);
} // namespace tray_qt
+37
View File
@@ -4,6 +4,7 @@
// local includes
#include "src/tray.h"
#include "src/tray_qt.h"
// standard includes
#include <array>
@@ -50,6 +51,42 @@ namespace {
}
} // namespace
TEST(TrayQtPlatformTest, PreservesExplicitPlatformSelection) {
EXPECT_EQ(
tray_qt::select_platform_plugins("offscreen", "wayland", "wayland-0", ":0"),
"offscreen"
);
}
TEST(TrayQtPlatformTest, UsesMinimalPlatformWithoutDesktopEnvironment) {
EXPECT_EQ(
tray_qt::select_platform_plugins({}, {}, {}, {}),
"minimal"
);
}
TEST(TrayQtPlatformTest, AddsMinimalFallbackForSingleDisplayProtocol) {
EXPECT_EQ(
tray_qt::select_platform_plugins({}, "wayland", "wayland-0", {}),
"wayland;minimal"
);
EXPECT_EQ(
tray_qt::select_platform_plugins({}, "x11", {}, ":0"),
"xcb;minimal"
);
}
TEST(TrayQtPlatformTest, PrefersSessionProtocolBeforeHeadlessFallback) {
EXPECT_EQ(
tray_qt::select_platform_plugins({}, "wayland", "wayland-0", ":0"),
"wayland;xcb;minimal"
);
EXPECT_EQ(
tray_qt::select_platform_plugins({}, "X11", "wayland-0", ":0"),
"xcb;wayland;minimal"
);
}
class TrayQtCoverageTest: public BaseTest {
private:
bool trayRunning_ {false};