diff --git a/krunner/CMakeLists.txt b/krunner/CMakeLists.txt --- a/krunner/CMakeLists.txt +++ b/krunner/CMakeLists.txt @@ -23,6 +23,7 @@ KF5::Crash KF5::WaylandClient KF5::QuickAddons + PW::KWorkspace ) target_compile_definitions(krunner PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}") @@ -38,4 +39,4 @@ install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KRunnerAppDBusInterfaceConfig.cmake DESTINATION ${CMAKECONFIG_INSTALL_DIR}) -add_subdirectory(update) \ No newline at end of file +add_subdirectory(update) diff --git a/krunner/main.cpp b/krunner/main.cpp --- a/krunner/main.cpp +++ b/krunner/main.cpp @@ -33,6 +33,7 @@ #include #include +#include #include "view.h" @@ -43,6 +44,7 @@ qunsetenv("QT_DEVICE_PIXEL_RATIO"); QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling); + KWorkSpace::detectPlatform(argc, argv); QQuickWindow::setDefaultAlphaBuffer(true); QApplication app(argc, argv); KLocalizedString::setApplicationDomain("krunner"); diff --git a/ksmserver/logout-greeter/main.cpp b/ksmserver/logout-greeter/main.cpp --- a/ksmserver/logout-greeter/main.cpp +++ b/ksmserver/logout-greeter/main.cpp @@ -166,6 +166,7 @@ // Qt does not currently (5.9.4) support fullscreen on xdg_shell v6. qputenv("QT_WAYLAND_SHELL_INTEGRATION", "wl-shell"); + KWorkSpace::detectPlatform(argc, argv); QQuickWindow::setDefaultAlphaBuffer(true); QApplication app(argc, argv); diff --git a/ksmserver/switchuser-greeter/main.cpp b/ksmserver/switchuser-greeter/main.cpp --- a/ksmserver/switchuser-greeter/main.cpp +++ b/ksmserver/switchuser-greeter/main.cpp @@ -138,6 +138,7 @@ // Qt does not currently (5.9.4) support fullscreen on xdg_shell v6. qputenv("QT_WAYLAND_SHELL_INTEGRATION", "wl-shell"); + KWorkSpace::detectPlatform(argc, argv); QQuickWindow::setDefaultAlphaBuffer(true); QGuiApplication app(argc, argv); diff --git a/ksplash/ksplashqml/CMakeLists.txt b/ksplash/ksplashqml/CMakeLists.txt --- a/ksplash/ksplashqml/CMakeLists.txt +++ b/ksplash/ksplashqml/CMakeLists.txt @@ -16,6 +16,7 @@ KF5::QuickAddons KF5::WaylandClient KF5::WindowSystem + PW::KWorkspace ) install(TARGETS ksplashqml ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}) diff --git a/ksplash/ksplashqml/main.cpp b/ksplash/ksplashqml/main.cpp --- a/ksplash/ksplashqml/main.cpp +++ b/ksplash/ksplashqml/main.cpp @@ -23,6 +23,8 @@ #include +#include + #include #include @@ -88,6 +90,7 @@ //enable to send log output to /tmp/ksplash //which is useful for debugging // qInstallMsgHandler(myMessageHandler); + KWorkSpace::detectPlatform(argc, argv); QQuickWindow::setDefaultAlphaBuffer(true); SplashApp app(argc, argv); diff --git a/kuiserver/CMakeLists.txt b/kuiserver/CMakeLists.txt --- a/kuiserver/CMakeLists.txt +++ b/kuiserver/CMakeLists.txt @@ -60,6 +60,7 @@ target_link_libraries(kdeinit_kuiserver5 + PW::KWorkspace Qt5::DBus KF5::ConfigWidgets KF5::DBusAddons diff --git a/kuiserver/main.cpp b/kuiserver/main.cpp --- a/kuiserver/main.cpp +++ b/kuiserver/main.cpp @@ -27,12 +27,15 @@ #include +#include + #include Q_LOGGING_CATEGORY(KUISERVER, "kuiserver", QtInfoMsg) extern "C" Q_DECL_EXPORT int kdemain(int argc, char **argv) { + KWorkSpace::detectPlatform(argc, argv); QApplication app(argc, argv); app.setApplicationName(QStringLiteral("kuiserver")); app.setApplicationVersion(QStringLiteral("2.0")); diff --git a/libkworkspace/CMakeLists.txt b/libkworkspace/CMakeLists.txt --- a/libkworkspace/CMakeLists.txt +++ b/libkworkspace/CMakeLists.txt @@ -73,3 +73,7 @@ NAMESPACE PW:: DESTINATION ${CMAKECONFIG_INSTALL_DIR} FILE LibKWorkspaceLibraryTargets.cmake ) + +if(BUILD_TESTING) + add_subdirectory(autotests) +endif() diff --git a/libkworkspace/autotests/CMakeLists.txt b/libkworkspace/autotests/CMakeLists.txt new file mode 100644 --- /dev/null +++ b/libkworkspace/autotests/CMakeLists.txt @@ -0,0 +1,5 @@ +include(ECMMarkAsTest) +add_executable(testPlatformDetection testPlatformDetection.cpp) +target_link_libraries(testPlatformDetection Qt5::Test PW::KWorkspace) +add_test(NAME kworkspace-testPlatformDetection COMMAND testPlatformDetection) +ecm_mark_as_test(testPlatformDetection) diff --git a/libkworkspace/autotests/testPlatformDetection.cpp b/libkworkspace/autotests/testPlatformDetection.cpp new file mode 100644 --- /dev/null +++ b/libkworkspace/autotests/testPlatformDetection.cpp @@ -0,0 +1,128 @@ +/* + * Copyright 2018 Martin Flöser + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) version 3, or any + * later version accepted by the membership of KDE e.V. (or its + * successor approved by the membership of KDE e.V.), which shall + * act as a proxy defined in Section 6 of version 3 of the license. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see . +*/ + +#include + +#include "../kworkspace.h" + +#include + +class TestPlatformDetection : public QObject +{ + Q_OBJECT +private Q_SLOTS: + void init(); + void testPlatformSelection_data(); + void testPlatformSelection(); + void testArguments_data(); + void testArguments(); + void testQtQpaPlatformIsSet_data(); + void testQtQpaPlatformIsSet(); +}; + +void TestPlatformDetection::init() +{ + qunsetenv("QT_QPA_PLATFORM"); + qunsetenv("XDG_SESSION_TYPE"); +} + +void TestPlatformDetection::testPlatformSelection_data() +{ + QTest::addColumn("xdgSessionType"); + QTest::addColumn("expectedQtQpaPlatform"); + + QTest::newRow("wayland") << QByteArrayLiteral("wayland") << QByteArrayLiteral("wayland"); + QTest::newRow("x11") << QByteArrayLiteral("x11") << QByteArrayLiteral("xcb"); + QTest::newRow("unknown") << QByteArrayLiteral("mir") << QByteArray(); +} + +void TestPlatformDetection::testPlatformSelection() +{ + QVERIFY(!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")); + QFETCH(QByteArray, xdgSessionType); + qputenv("XDG_SESSION_TYPE", xdgSessionType); + + std::vector cppArgv{ + QByteArrayLiteral("testPlatformDetction") + }; + std::vector argv; + for (QByteArray &arg : cppArgv) { + argv.push_back(arg.data()); + } + KWorkSpace::detectPlatform(1, argv.data()); + QTEST(qgetenv("QT_QPA_PLATFORM"), "expectedQtQpaPlatform"); +} + +void TestPlatformDetection::testArguments_data() +{ + QTest::addColumn("arg"); + + QTest::newRow("-platform") << QByteArrayLiteral("-platform"); + QTest::newRow("--platform") << QByteArrayLiteral("--platform"); + QTest::newRow("-platform=wayland") << QByteArrayLiteral("-platform=wayland"); + QTest::newRow("--platform=wayland") << QByteArrayLiteral("--platform=wayland"); +} + +void TestPlatformDetection::testArguments() +{ + QVERIFY(!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")); + qputenv("XDG_SESSION_TYPE", "wayland"); + + QFETCH(QByteArray, arg); + std::vector cppArgv{ + QByteArrayLiteral("testPlatformDetction"), + arg, + QByteArrayLiteral("wayland") + }; + std::vector argv; + for (QByteArray &arg : cppArgv) { + argv.push_back(arg.data()); + } + KWorkSpace::detectPlatform(3, argv.data()); + QVERIFY(!qEnvironmentVariableIsSet("QT_QPA_PLATFORM")); +} + +void TestPlatformDetection::testQtQpaPlatformIsSet_data() +{ + QTest::addColumn("qtQpaPlatform"); + QTest::addColumn("xdgSessionType"); + + QTest::newRow("xcb - x11") << QByteArrayLiteral("xcb") << QByteArrayLiteral("x11"); + QTest::newRow("xcb - wayland") << QByteArrayLiteral("xcb") << QByteArrayLiteral("wayland"); + QTest::newRow("wayland - x11") << QByteArrayLiteral("wayland") << QByteArrayLiteral("x11"); + QTest::newRow("wayland - wayland") << QByteArrayLiteral("wayland") << QByteArrayLiteral("wayland"); + QTest::newRow("windows - x11") << QByteArrayLiteral("windows") << QByteArrayLiteral("x11"); +} + +void TestPlatformDetection::testQtQpaPlatformIsSet() +{ + // test verifies that if QT_QPA_PLATFORM is set the env variable does not get adjusted + QFETCH(QByteArray, qtQpaPlatform); + QFETCH(QByteArray, xdgSessionType); + qputenv("QT_QPA_PLATFORM", qtQpaPlatform); + qputenv("XDG_SESSION_TYPE", xdgSessionType); + + KWorkSpace::detectPlatform(0, nullptr); + QCOMPARE(qgetenv("QT_QPA_PLATFORM"), qtQpaPlatform); +} + +QTEST_GUILESS_MAIN(TestPlatformDetection) + +#include "testPlatformDetection.moc" diff --git a/libkworkspace/kworkspace.h b/libkworkspace/kworkspace.h --- a/libkworkspace/kworkspace.h +++ b/libkworkspace/kworkspace.h @@ -150,6 +150,21 @@ */ KWORKSPACE_EXPORT void propagateSessionManager(); + /** + * Performs platform detection and adjusts QT_QPA_PLATFORM environment + * variable to either xcb or wayland depending on the detected platform. + * + * The detection is based on the XDG_SESSION_TYPE environment variable. + * The detection is skipped in case QT_QPA_PLATFORM is already set or + * if one of the command line arguments contains the "-platform" variable. + * + * In order to make use of this function, it has to be invoked before the + * QGuiApplication instance is constructed. Invoking after constructing the + * QGuiApplication has no effect. + * @since 5.13 + **/ + KWORKSPACE_EXPORT void detectPlatform(int argc, char **argv); + } #endif diff --git a/libkworkspace/kworkspace.cpp b/libkworkspace/kworkspace.cpp --- a/libkworkspace/kworkspace.cpp +++ b/libkworkspace/kworkspace.cpp @@ -260,6 +260,30 @@ #endif } +void detectPlatform(int argc, char **argv) +{ + if (qEnvironmentVariableIsSet("QT_QPA_PLATFORM")) { + return; + } + for (int i = 0; i < argc; i++) { + if (qstrcmp(argv[i], "-platform") == 0 || + qstrcmp(argv[i], "--platform") == 0 || + QByteArray(argv[i]).startsWith("-platform=") || + QByteArray(argv[i]).startsWith("--platform=")) { + return; + } + } + const QByteArray sessionType = qgetenv("XDG_SESSION_TYPE"); + if (sessionType.isEmpty()) { + return; + } + if (qstrcmp(sessionType, "wayland") == 0) { + qputenv("QT_QPA_PLATFORM", "wayland"); + } else if (qstrcmp(sessionType, "x11") == 0) { + qputenv("QT_QPA_PLATFORM", "xcb"); + } +} + } // end namespace diff --git a/shell/CMakeLists.txt b/shell/CMakeLists.txt --- a/shell/CMakeLists.txt +++ b/shell/CMakeLists.txt @@ -74,6 +74,7 @@ KF5::Package KF5::WaylandClient KF5::WindowSystem + PW::KWorkspace ) target_include_directories(plasmashell PRIVATE "${CMAKE_BINARY_DIR}") target_compile_definitions(plasmashell PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}") diff --git a/shell/main.cpp b/shell/main.cpp --- a/shell/main.cpp +++ b/shell/main.cpp @@ -31,6 +31,7 @@ #include #include #include +#include #include "shellcorona.h" #include "standaloneappcorona.h" @@ -58,6 +59,7 @@ QQuickWindow::setDefaultAlphaBuffer(true); + KWorkSpace::detectPlatform(argc, argv); QApplication app(argc, argv); KLocalizedString::setApplicationDomain("plasmashell"); diff --git a/startkde/kcminit/CMakeLists.txt b/startkde/kcminit/CMakeLists.txt --- a/startkde/kcminit/CMakeLists.txt +++ b/startkde/kcminit/CMakeLists.txt @@ -14,7 +14,7 @@ kf5_add_kdeinit_executable( kcminit ${kcminit_KDEINIT_SRCS}) -target_link_libraries(kdeinit_kcminit Qt5::Core Qt5::Gui Qt5::DBus KF5::CoreAddons KF5::Service KF5::I18n) +target_link_libraries(kdeinit_kcminit Qt5::Core Qt5::Gui Qt5::DBus KF5::CoreAddons KF5::Service KF5::I18n PW::KWorkspace) if (XCB_XCB_FOUND) target_link_libraries(kdeinit_kcminit XCB::XCB) endif() @@ -32,7 +32,7 @@ qt5_add_dbus_interface(kcminit_startup_KDEINIT_SRCS ${klauncher_xml} klauncher_iface) kf5_add_kdeinit_executable( kcminit_startup ${kcminit_startup_KDEINIT_SRCS}) -target_link_libraries(kdeinit_kcminit_startup Qt5::Core Qt5::Gui Qt5::DBus KF5::CoreAddons KF5::Service KF5::I18n) +target_link_libraries(kdeinit_kcminit_startup Qt5::Core Qt5::Gui Qt5::DBus KF5::CoreAddons KF5::Service KF5::I18n PW::KWorkspace) if (XCB_XCB_FOUND) target_link_libraries(kdeinit_kcminit_startup XCB::XCB) endif() diff --git a/startkde/kcminit/main.cpp b/startkde/kcminit/main.cpp --- a/startkde/kcminit/main.cpp +++ b/startkde/kcminit/main.cpp @@ -42,6 +42,7 @@ #include #include #include +#include static int ready[ 2 ]; static bool startup = false; @@ -221,6 +222,7 @@ startup = ( strcmp( argv[ 0 ], "kcminit_startup" ) == 0 ); // started from startkde? + KWorkSpace::detectPlatform(argc, argv); QGuiApplication::setDesktopSettingsAware(false); QGuiApplication app(argc, argv); //gui is needed for several modules KLocalizedString::setApplicationDomain("kcminit"); diff --git a/systemmonitor/CMakeLists.txt b/systemmonitor/CMakeLists.txt --- a/systemmonitor/CMakeLists.txt +++ b/systemmonitor/CMakeLists.txt @@ -31,6 +31,7 @@ KF5::XmlGui KF5::GlobalAccel KF5::WindowSystem + PW::KWorkspace ) install(TARGETS systemmonitor DESTINATION ${KDE_INSTALL_BINDIR}) diff --git a/systemmonitor/main.cpp b/systemmonitor/main.cpp --- a/systemmonitor/main.cpp +++ b/systemmonitor/main.cpp @@ -21,10 +21,13 @@ #include #include +#include + #include "ksystemactivitydialog.h" int main(int argc, char** argv) { + KWorkSpace::detectPlatform(argc, argv); QApplication app(argc, argv); KLocalizedString::setApplicationDomain("systemmonitor");