Commit b5c32699 authored by Martin Flöser's avatar Martin Flöser
Browse files

Add platform detection to KWorkspace library to adjust QT_QPA_PLATFORM

Summary:
This is a preparation step to unset QT_QPA_PLATFORM from the wayland
startup session script. Setting QT_QPA_PLATFORM breaks 3rd-party Qt
software which does not bundle QtWayland. Most prominent example is
the Qt installer itself (see
https://bugreports.qt.io/browse/QTBUG-60222).

On the other hand our Plasma workspace applications need to be forced to
Wayland on a Wayland system. So we have a conflict between we want to
set QT_QPA_PLATFORM and we don't want to set QT_QPA_PLATFORM.

This change adds new API to KWorkspace to address this problem. The new
method adjusts the QT_QPA_PLATFORM based on the XDG_SESSION_TYPE
enviornment variable. It is completely opt-in. Meaning applications need
to explicitly add the call prior to creating the QGuiApplication and if
the user specifies either QT_QPA_PLATFORM env variable or any of the
-platform command line argument variants, the platform detection is
skipped.

The change also adjusts all plasma-workspace applications which should
use Wayland on Wayland to use the new API. The startup script on the
other hand still sets QT_QPA_PLATFORM. We also have applications outside
of plasma-workspace which needs this detection. Examples are:
 * powerdevil
 * systemsettings
 * kinfocenter

Once this change is merged those applications can be adjusted by linking
against PW::KWorkspace and afterwards QT_QPA_PLATFORM can be unset from
startplasmacompositor.

Test Plan: See added autotest

Reviewers: #plasma

Subscribers: plasma-devel

Tags: #plasma

Differential Revision: https://phabricator.kde.org/D10816
parent 183c3081
......@@ -23,6 +23,7 @@ target_link_libraries(krunner
KF5::Crash
KF5::WaylandClient
KF5::QuickAddons
PW::KWorkspace
)
target_compile_definitions(krunner PRIVATE -DPROJECT_VERSION="${PROJECT_VERSION}")
......@@ -38,4 +39,4 @@ configure_package_config_file(KRunnerAppDBusInterfaceConfig.cmake.in
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/KRunnerAppDBusInterfaceConfig.cmake
DESTINATION ${CMAKECONFIG_INSTALL_DIR})
add_subdirectory(update)
\ No newline at end of file
add_subdirectory(update)
......@@ -33,6 +33,7 @@
#include <kdeclarative/qmlobject.h>
#include <KQuickAddons/QtQuickSettings>
#include <kworkspace.h>
#include "view.h"
......@@ -43,6 +44,7 @@ int main(int argc, char **argv)
qunsetenv("QT_DEVICE_PIXEL_RATIO");
QCoreApplication::setAttribute(Qt::AA_DisableHighDpiScaling);
KWorkSpace::detectPlatform(argc, argv);
QQuickWindow::setDefaultAlphaBuffer(true);
QApplication app(argc, argv);
KLocalizedString::setApplicationDomain("krunner");
......
......@@ -166,6 +166,7 @@ int main(int argc, char *argv[])
// 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);
......
......@@ -138,6 +138,7 @@ int main(int argc, char *argv[])
// 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);
......
......@@ -16,6 +16,7 @@ target_link_libraries(ksplashqml
KF5::QuickAddons
KF5::WaylandClient
KF5::WindowSystem
PW::KWorkspace
)
install(TARGETS ksplashqml ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
......
......@@ -23,6 +23,8 @@
#include <QTextStream>
#include <kworkspace.h>
#include <iostream>
#include <unistd.h>
......@@ -88,6 +90,7 @@ int main(int argc, char **argv)
//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);
......
......@@ -60,6 +60,7 @@ endif ()
target_link_libraries(kdeinit_kuiserver5
PW::KWorkspace
Qt5::DBus
KF5::ConfigWidgets
KF5::DBusAddons
......
......@@ -27,12 +27,15 @@
#include <kdbusservice.h>
#include <kworkspace.h>
#include <QCommandLineParser>
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"));
......
......@@ -73,3 +73,7 @@ install(EXPORT libkworkspaceLibraryTargets
NAMESPACE PW::
DESTINATION ${CMAKECONFIG_INSTALL_DIR}
FILE LibKWorkspaceLibraryTargets.cmake )
if(BUILD_TESTING)
add_subdirectory(autotests)
endif()
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)
/*
* Copyright 2018 Martin Flöser <mgraesslin@kde.org>
*
* 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 <http://www.gnu.org/licenses/>.
*/
#include <QtTest/QtTest>
#include "../kworkspace.h"
#include <vector>
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<QByteArray>("xdgSessionType");
QTest::addColumn<QByteArray>("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<QByteArray> cppArgv{
QByteArrayLiteral("testPlatformDetction")
};
std::vector<char*> 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<QByteArray>("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<QByteArray> cppArgv{
QByteArrayLiteral("testPlatformDetction"),
arg,
QByteArrayLiteral("wayland")
};
std::vector<char*> 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<QByteArray>("qtQpaPlatform");
QTest::addColumn<QByteArray>("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"
......@@ -260,6 +260,30 @@ void propagateSessionManager()
#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
......@@ -150,6 +150,21 @@ namespace KWorkSpace
*/
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
......@@ -74,6 +74,7 @@ target_link_libraries(plasmashell
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}")
......
......@@ -31,6 +31,7 @@
#include <kdbusservice.h>
#include <klocalizedstring.h>
#include <kcrash.h>
#include <kworkspace.h>
#include "shellcorona.h"
#include "standaloneappcorona.h"
......@@ -58,6 +59,7 @@ int main(int argc, char *argv[])
QQuickWindow::setDefaultAlphaBuffer(true);
KWorkSpace::detectPlatform(argc, argv);
QApplication app(argc, argv);
KLocalizedString::setApplicationDomain("plasmashell");
......
......@@ -14,7 +14,7 @@ qt5_add_dbus_interface(kcminit_KDEINIT_SRCS ${klauncher_xml} klauncher_iface)
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 @@ set(kcminit_startup_KDEINIT_SRCS main.cpp)
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()
......
......@@ -42,6 +42,7 @@
#include <kconfiggroup.h>
#include <klocalizedstring.h>
#include <kservicetypetrader.h>
#include <kworkspace.h>
static int ready[ 2 ];
static bool startup = false;
......@@ -221,6 +222,7 @@ extern "C" Q_DECL_EXPORT int kdemain(int argc, char *argv[])
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");
......
......@@ -31,6 +31,7 @@ target_link_libraries(systemmonitor
KF5::XmlGui
KF5::GlobalAccel
KF5::WindowSystem
PW::KWorkspace
)
install(TARGETS systemmonitor DESTINATION ${KDE_INSTALL_BINDIR})
......
......@@ -21,10 +21,13 @@
#include <QDBusConnection>
#include <KLocalizedString>
#include <kworkspace.h>
#include "ksystemactivitydialog.h"
int main(int argc, char** argv)
{
KWorkSpace::detectPlatform(argc, argv);
QApplication app(argc, argv);
KLocalizedString::setApplicationDomain("systemmonitor");
......
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment