Support multiple entries in the Qt platform plugin string

[ChangeLog][QtGui] QT_QPA_PLATFORM and the -platform argument now support a
list of platform plugins in prioritized order. Platforms are separated by
semicolons.

The plugins are tried in the order they are specified as long as all preceding
platforms fail gracefully by returning nullptr in the implementation of
QPlatformIntegrationPlugin::create()

This is useful on Linux distributions where the Wayland plugin may be
installed, but is not supported by the current session. i.e. if X11 is running
or if the compositor does not provide a compatible shell extension.

Example usage:

QT_QPA_PLATFORM="wayland;xcb" ./application

or

./application -platform "wayland;xcb"

Task-number: QTBUG-59762
Change-Id: Ia3f034ec522ed6729d71acf971d172da9e68a5a0
Reviewed-by: Gatis Paeglis <gatis.paeglis@qt.io>
This commit is contained in:
Johan Klokkhammer Helsing 2018-02-14 15:51:38 +01:00 committed by Johan Helsing
parent 3190edb077
commit 093d85393f
2 changed files with 41 additions and 27 deletions

View File

@ -274,7 +274,8 @@ Gui, printing, widget options:
(Windows only)
-combined-angle-lib .. Merge LibEGL and LibGLESv2 into LibANGLE (Windows only)
-qpa <name> .......... Select default QPA backend (e.g., xcb, cocoa, windows)
-qpa <name> .......... Select default QPA backend(s) (e.g., xcb, cocoa, windows)
A prioritized list separated by semi-colons.
-xcb-xlib............. Enable Xcb-Xlib support [auto]
Platform backends:

View File

@ -1140,8 +1140,14 @@ QString QGuiApplication::platformName()
*QGuiApplicationPrivate::platform_name : QString();
}
static void init_platform(const QString &pluginArgument, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv)
Q_LOGGING_CATEGORY(lcQpaPluginLoading, "qt.qpa.plugin");
static void init_platform(const QString &pluginNamesWithArguments, const QString &platformPluginPath, const QString &platformThemeName, int &argc, char **argv)
{
QStringList plugins = pluginNamesWithArguments.split(QLatin1Char(';'));
QStringList platformArguments;
QStringList availablePlugins = QPlatformIntegrationFactory::keys(platformPluginPath);
for (auto pluginArgument : plugins) {
// Split into platform name and arguments
QStringList arguments = pluginArgument.split(QLatin1Char(':'));
const QString name = arguments.takeFirst().toLower();
@ -1152,21 +1158,29 @@ static void init_platform(const QString &pluginArgument, const QString &platform
// Create the platform integration.
QGuiApplicationPrivate::platform_integration = QPlatformIntegrationFactory::create(name, arguments, argc, argv, platformPluginPath);
if (Q_UNLIKELY(!QGuiApplicationPrivate::platform_integration)) {
QStringList keys = QPlatformIntegrationFactory::keys(platformPluginPath);
QString fatalMessage;
if (keys.contains(name)) {
fatalMessage = QStringLiteral("This application failed to start because it could not load the Qt platform plugin \"%2\"\nin \"%3\", even though it was found. ").arg(name, QDir::toNativeSeparators(platformPluginPath));
fatalMessage += QStringLiteral("This is usually due to missing dependencies, which you can verify by setting the env variable QT_DEBUG_PLUGINS to 1.\n\n");
if (availablePlugins.contains(name)) {
qCInfo(lcQpaPluginLoading).nospace().noquote()
<< "Could not load the Qt platform plugin \"" << name << "\" in \""
<< QDir::toNativeSeparators(platformPluginPath) << "\" even though it was found.";
} else {
fatalMessage = QStringLiteral("This application failed to start because it could not find the Qt platform plugin \"%2\"\nin \"%3\".\n\n").arg(name, QDir::toNativeSeparators(platformPluginPath));
qCWarning(lcQpaPluginLoading).nospace().noquote()
<< "Could not find the Qt platform plugin \"" << name << "\" in \""
<< QDir::toNativeSeparators(platformPluginPath) << "\"";
}
} else {
QGuiApplicationPrivate::platform_name = new QString(name);
platformArguments = arguments;
break;
}
}
if (!keys.isEmpty()) {
fatalMessage += QStringLiteral("Available platform plugins are: %1.\n\n").arg(
keys.join(QLatin1String(", ")));
}
fatalMessage += QStringLiteral("Reinstalling the application may fix this problem.");
if (Q_UNLIKELY(!QGuiApplicationPrivate::platform_integration)) {
QString fatalMessage = QStringLiteral("This application failed to start because no Qt platform plugin could be initialized. "
"Reinstalling the application may fix this problem.\n");
if (!availablePlugins.isEmpty())
fatalMessage += QStringLiteral("\nAvailable platform plugins are: %1.\n").arg(availablePlugins.join(QLatin1String(", ")));
#if defined(Q_OS_WIN) && !defined(Q_OS_WINRT)
// Windows: Display message box unless it is a console application
// or debug build showing an assert box.
@ -1174,11 +1188,10 @@ static void init_platform(const QString &pluginArgument, const QString &platform
MessageBox(0, (LPCTSTR)fatalMessage.utf16(), (LPCTSTR)(QCoreApplication::applicationName().utf16()), MB_OK | MB_ICONERROR);
#endif // Q_OS_WIN && !Q_OS_WINRT
qFatal("%s", qPrintable(fatalMessage));
return;
}
QGuiApplicationPrivate::platform_name = new QString(name);
// Many platforms have created QScreens at this point. Finish initializing
// QHighDpiScaling to be prepared for early calls to qt_defaultDpi().
if (QGuiApplication::primaryScreen()) {
@ -1225,9 +1238,9 @@ static void init_platform(const QString &pluginArgument, const QString &platform
#ifndef QT_NO_PROPERTIES
// Set arguments as dynamic properties on the native interface as
// boolean 'foo' or strings: 'foo=bar'
if (!arguments.isEmpty()) {
if (!platformArguments.isEmpty()) {
if (QObject *nativeInterface = QGuiApplicationPrivate::platform_integration->nativeInterface()) {
for (const QString &argument : qAsConst(arguments)) {
for (const QString &argument : qAsConst(platformArguments)) {
const int equalsPos = argument.indexOf(QLatin1Char('='));
const QByteArray name =
equalsPos != -1 ? argument.left(equalsPos).toUtf8() : argument.toUtf8();