diff --git a/src/tray_qt.cpp b/src/tray_qt.cpp index 811c062..f23fb90 100644 --- a/src/tray_qt.cpp +++ b/src/tray_qt.cpp @@ -4,6 +4,8 @@ */ // standard includes #include +#include +#include // qt includes #include @@ -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 } diff --git a/src/tray_qt.h b/src/tray_qt.h new file mode 100644 index 0000000..67f619a --- /dev/null +++ b/src/tray_qt.h @@ -0,0 +1,31 @@ +/** + * @file src/tray_qt.h + * @brief Internal helpers for the Qt system tray implementation. + */ +#pragma once + +// standard includes +#include +#include + +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 diff --git a/tests/unit/test_tray_qt.cpp b/tests/unit/test_tray_qt.cpp index 3e25e92..d4c4c1b 100644 --- a/tests/unit/test_tray_qt.cpp +++ b/tests/unit/test_tray_qt.cpp @@ -4,6 +4,7 @@ // local includes #include "src/tray.h" +#include "src/tray_qt.h" // standard includes #include @@ -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};