mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-27 01:03:09 +00:00
Convert all blocking D-Bus calls in launcher to async
This commit is contained in:
parent
8a9e5a80b4
commit
90bb30416c
2 changed files with 293 additions and 134 deletions
|
|
@ -12,6 +12,7 @@
|
||||||
#include <QDBusConnection>
|
#include <QDBusConnection>
|
||||||
#include <QDBusConnectionInterface>
|
#include <QDBusConnectionInterface>
|
||||||
#include <QDBusError>
|
#include <QDBusError>
|
||||||
|
#include <QDBusPendingCallWatcher>
|
||||||
#include <QDBusReply>
|
#include <QDBusReply>
|
||||||
#include <QDBusVariant>
|
#include <QDBusVariant>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
|
|
@ -28,22 +29,6 @@ static const QString kGameCenterInterface = QStringLiteral("org.kde.GameCenter1"
|
||||||
static const QString kRunnerService = QStringLiteral("org.kde.ALaKarte.Runner1");
|
static const QString kRunnerService = QStringLiteral("org.kde.ALaKarte.Runner1");
|
||||||
static const QString kRunnerPath = QStringLiteral("/org/kde/ALaKarte/Runner1");
|
static const QString kRunnerPath = QStringLiteral("/org/kde/ALaKarte/Runner1");
|
||||||
|
|
||||||
static bool pingDaemon(QDBusConnection bus)
|
|
||||||
{
|
|
||||||
if (!bus.isConnected()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
org::kde::GameCenter1 iface(kGameCenterService, kGameCenterPath, bus);
|
|
||||||
if (!iface.isValid()) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
iface.setTimeout(2000);
|
|
||||||
QDBusPendingReply<QString> reply = iface.Ping();
|
|
||||||
reply.waitForFinished();
|
|
||||||
return !reply.isError() && reply.value() == QLatin1String("ok");
|
|
||||||
}
|
|
||||||
|
|
||||||
static void disconnectDaemonSignals(QDBusConnection bus, GameLauncher *launcher)
|
static void disconnectDaemonSignals(QDBusConnection bus, GameLauncher *launcher)
|
||||||
{
|
{
|
||||||
if (!bus.isConnected()) {
|
if (!bus.isConnected()) {
|
||||||
|
|
@ -577,89 +562,15 @@ QVariantMap GameLauncher::resolveLaunchInfo(Game *game) const
|
||||||
return info;
|
return info;
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameLauncher::launchGame(Game *game)
|
void GameLauncher::dispatchLaunch(Game *game, const QVariantMap &launchSpec)
|
||||||
{
|
{
|
||||||
if (!game) {
|
auto *iface = new org::kde::GameCenter1(kGameCenterService, kGameCenterPath, QDBusConnection::sessionBus(), this);
|
||||||
return;
|
iface->setTimeout(5000);
|
||||||
}
|
auto *watcher = new QDBusPendingCallWatcher(iface->Launch(launchSpec), this);
|
||||||
|
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, game, iface, watcher]() {
|
||||||
// Check if already running
|
watcher->deleteLater();
|
||||||
if (m_daemonGameToSession.contains(game->id())) {
|
iface->deleteLater();
|
||||||
Q_EMIT gameError(game, tr("Game is already running"));
|
QDBusPendingReply<QString> reply = *watcher;
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QVariantMap info = resolveLaunchInfo(game);
|
|
||||||
if (!info.value(QStringLiteral("ok")).toBool()) {
|
|
||||||
Q_EMIT gameError(game, info.value(QStringLiteral("error")).toString());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString launchCommand = info.value(QStringLiteral("launchCommand")).toString();
|
|
||||||
const QString launchType = info.value(QStringLiteral("launchType")).toString();
|
|
||||||
const QString provider = info.value(QStringLiteral("provider")).toString();
|
|
||||||
|
|
||||||
const QString runner = info.value(QStringLiteral("runner")).toString();
|
|
||||||
const QString runnerId = info.value(QStringLiteral("runnerId")).toString();
|
|
||||||
const QString runnerPath = info.value(QStringLiteral("runnerPath")).toString();
|
|
||||||
const QString prefixPath = info.value(QStringLiteral("prefixPath")).toString();
|
|
||||||
const QVariantMap envOverrides = info.value(QStringLiteral("envOverrides")).toMap();
|
|
||||||
if (runner == QLatin1String("proton") || runner == QLatin1String("wine")) {
|
|
||||||
const QString resolvedPrefixPath = info.value(QStringLiteral("resolvedPrefixPath")).toString();
|
|
||||||
if (!resolvedPrefixPath.isEmpty()) {
|
|
||||||
QDir().mkpath(resolvedPrefixPath);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString finalProgram = info.value(QStringLiteral("finalProgram")).toString();
|
|
||||||
const QStringList finalArgs = info.value(QStringLiteral("finalArgs")).toStringList();
|
|
||||||
const QString workingDirectory = info.value(QStringLiteral("workingDirectory")).toString();
|
|
||||||
const QVariantMap effectiveEnv = info.value(QStringLiteral("effectiveEnv")).toMap();
|
|
||||||
|
|
||||||
// Always try daemon first — for all launch types
|
|
||||||
{
|
|
||||||
org::kde::GameCenter1 iface(kGameCenterService, kGameCenterPath, QDBusConnection::sessionBus());
|
|
||||||
QVariantMap launchSpec = {
|
|
||||||
{QStringLiteral("command"), launchCommand},
|
|
||||||
{QStringLiteral("gameId"), game->id()},
|
|
||||||
{QStringLiteral("displayName"), game->name()},
|
|
||||||
{QStringLiteral("provider"), provider},
|
|
||||||
{QStringLiteral("origin"), QStringLiteral("ui")},
|
|
||||||
};
|
|
||||||
|
|
||||||
if (!runnerId.isEmpty()) {
|
|
||||||
launchSpec.insert(QStringLiteral("runnerId"), runnerId);
|
|
||||||
}
|
|
||||||
if (!runner.isEmpty()) {
|
|
||||||
launchSpec.insert(QStringLiteral("runner"), runner);
|
|
||||||
}
|
|
||||||
if (!runnerPath.isEmpty()) {
|
|
||||||
launchSpec.insert(QStringLiteral("runnerPath"), runnerPath);
|
|
||||||
}
|
|
||||||
if (!prefixPath.isEmpty()) {
|
|
||||||
launchSpec.insert(QStringLiteral("prefixPath"), prefixPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
launchSpec.insert(QStringLiteral("requestedProgram"), info.value(QStringLiteral("program")).toString());
|
|
||||||
launchSpec.insert(QStringLiteral("requestedArgs"), info.value(QStringLiteral("args")).toStringList());
|
|
||||||
if (!envOverrides.isEmpty()) {
|
|
||||||
launchSpec.insert(QStringLiteral("requestedEnvOverrides"), envOverrides);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!finalProgram.isEmpty()) {
|
|
||||||
launchSpec.insert(QStringLiteral("program"), finalProgram);
|
|
||||||
launchSpec.insert(QStringLiteral("args"), finalArgs);
|
|
||||||
}
|
|
||||||
if (!effectiveEnv.isEmpty()) {
|
|
||||||
launchSpec.insert(QStringLiteral("envOverrides"), effectiveEnv);
|
|
||||||
}
|
|
||||||
if (!workingDirectory.isEmpty()) {
|
|
||||||
launchSpec.insert(QStringLiteral("workingDirectory"), workingDirectory);
|
|
||||||
}
|
|
||||||
|
|
||||||
iface.setTimeout(5000);
|
|
||||||
QDBusPendingReply<QString> reply = iface.Launch(launchSpec);
|
|
||||||
reply.waitForFinished();
|
|
||||||
if (!reply.isError() && !reply.value().isEmpty()) {
|
if (!reply.isError() && !reply.value().isEmpty()) {
|
||||||
m_daemonGameToSession.insert(game->id(), reply.value());
|
m_daemonGameToSession.insert(game->id(), reply.value());
|
||||||
m_daemonSessionToGame.insert(reply.value(), game->id());
|
m_daemonSessionToGame.insert(reply.value(), game->id());
|
||||||
|
|
@ -667,22 +578,255 @@ void GameLauncher::launchGame(Game *game)
|
||||||
game->setLastPlayed(QDateTime::currentDateTime());
|
game->setLastPlayed(QDateTime::currentDateTime());
|
||||||
Q_EMIT gameStarted(game);
|
Q_EMIT gameStarted(game);
|
||||||
Q_EMIT runningGamesChanged();
|
Q_EMIT runningGamesChanged();
|
||||||
|
|
||||||
if (App::instance()->config()->exitAfterLaunch()) {
|
if (App::instance()->config()->exitAfterLaunch()) {
|
||||||
QTimer::singleShot(500, qApp, &QCoreApplication::quit);
|
QTimer::singleShot(500, qApp, &QCoreApplication::quit);
|
||||||
}
|
}
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString launchError = reply.isError() ? reply.error().message() : QString();
|
const QString launchError = reply.isError() ? reply.error().message() : QString();
|
||||||
|
|
||||||
// No fallback for non-URL commands — emit error
|
|
||||||
if (!launchError.isEmpty()) {
|
if (!launchError.isEmpty()) {
|
||||||
Q_EMIT gameError(game, tr("Game Center launch failed: %1").arg(launchError));
|
Q_EMIT gameError(game, tr("Game Center launch failed: %1").arg(launchError));
|
||||||
} else {
|
} else {
|
||||||
Q_EMIT gameError(game, tr("Game Center daemon is not available"));
|
Q_EMIT gameError(game, tr("Game Center daemon is not available"));
|
||||||
}
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void GameLauncher::launchGame(Game *game)
|
||||||
|
{
|
||||||
|
if (!game) {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (m_daemonGameToSession.contains(game->id())) {
|
||||||
|
Q_EMIT gameError(game, tr("Game is already running"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString command = game->launchCommand();
|
||||||
|
if (command.isEmpty()) {
|
||||||
|
Q_EMIT gameError(game, tr("No launch command configured"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString platform = game->platform().toLower();
|
||||||
|
QString provider;
|
||||||
|
if (platform == QLatin1String("steam")) {
|
||||||
|
provider = QStringLiteral("steam");
|
||||||
|
} else if (platform == QLatin1String("lutris")) {
|
||||||
|
provider = QStringLiteral("lutris");
|
||||||
|
} else {
|
||||||
|
provider = QStringLiteral("manual");
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString runner = game->launchRunner().trimmed();
|
||||||
|
const QString runnerId = game->launchRunnerId().trimmed();
|
||||||
|
const QString runnerPath = game->launchRunnerPath().trimmed();
|
||||||
|
const QString prefixPath = game->launchPrefixPath().trimmed();
|
||||||
|
const bool hasLaunchOverrides = !runner.isEmpty() || !runnerId.isEmpty() || !runnerPath.isEmpty() || !prefixPath.isEmpty() || !game->launchEnv().isEmpty();
|
||||||
|
|
||||||
|
QVariantMap envOverrides;
|
||||||
|
const QVariantMap launchEnv = game->launchEnv();
|
||||||
|
for (auto it = launchEnv.constBegin(); it != launchEnv.constEnd(); ++it) {
|
||||||
|
const QString key = it.key();
|
||||||
|
if (!key.isEmpty() && !key.contains(QLatin1Char('='))) {
|
||||||
|
envOverrides.insert(key, it.value().toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!hasLaunchOverrides && command.startsWith(QLatin1String("steam://"))) {
|
||||||
|
QVariantMap launchSpec = {{QStringLiteral("command"), command},
|
||||||
|
{QStringLiteral("gameId"), game->id()},
|
||||||
|
{QStringLiteral("displayName"), game->name()},
|
||||||
|
{QStringLiteral("provider"), QStringLiteral("steam")},
|
||||||
|
{QStringLiteral("origin"), QStringLiteral("ui")}};
|
||||||
|
dispatchLaunch(game, launchSpec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!hasLaunchOverrides && command.startsWith(QLatin1String("lutris "))) {
|
||||||
|
QVariantMap launchSpec = {{QStringLiteral("command"), command},
|
||||||
|
{QStringLiteral("gameId"), game->id()},
|
||||||
|
{QStringLiteral("displayName"), game->name()},
|
||||||
|
{QStringLiteral("provider"), QStringLiteral("lutris")},
|
||||||
|
{QStringLiteral("origin"), QStringLiteral("ui")}};
|
||||||
|
dispatchLaunch(game, launchSpec);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList parts;
|
||||||
|
if (command.startsWith(QLatin1String("steam://"))) {
|
||||||
|
parts = {QStringLiteral("xdg-open"), command};
|
||||||
|
provider = QStringLiteral("steam");
|
||||||
|
} else if (command.startsWith(QLatin1String("lutris "))) {
|
||||||
|
parts = {QStringLiteral("xdg-open"), command.mid(7)};
|
||||||
|
provider = QStringLiteral("lutris");
|
||||||
|
} else {
|
||||||
|
parts = QProcess::splitCommand(command);
|
||||||
|
}
|
||||||
|
if (parts.isEmpty()) {
|
||||||
|
Q_EMIT gameError(game, tr("Invalid launch command"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QString program = parts.takeFirst();
|
||||||
|
const QStringList args = parts;
|
||||||
|
|
||||||
|
if ((runner == QLatin1String("wine") || runner == QLatin1String("proton")) && (program == QLatin1String("xdg-open"))) {
|
||||||
|
Q_EMIT gameError(game, tr("This runner cannot be used with URL-based launch commands"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!runnerId.isEmpty()) {
|
||||||
|
QVariantMap runnerSpec = {{QStringLiteral("runnerId"), runnerId},
|
||||||
|
{QStringLiteral("runner"), runner},
|
||||||
|
{QStringLiteral("runnerPath"), runnerPath},
|
||||||
|
{QStringLiteral("gameId"), game->id()},
|
||||||
|
{QStringLiteral("prefixPath"), prefixPath},
|
||||||
|
{QStringLiteral("program"), program},
|
||||||
|
{QStringLiteral("args"), args},
|
||||||
|
{QStringLiteral("envOverrides"), envOverrides},
|
||||||
|
{QStringLiteral("useGameProfile"), true}};
|
||||||
|
|
||||||
|
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||||
|
if (bus.isConnected() && bus.interface() && !bus.interface()->isServiceRegistered(kRunnerService)) {
|
||||||
|
bus.interface()->startService(kRunnerService);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto *runnerIface = new org::kde::ALaKarte::Runner1(kRunnerService, kRunnerPath, QDBusConnection::sessionBus(), this);
|
||||||
|
runnerIface->setTimeout(2000);
|
||||||
|
auto *watcher = new QDBusPendingCallWatcher(runnerIface->ResolveLaunch(runnerSpec), this);
|
||||||
|
connect(watcher,
|
||||||
|
&QDBusPendingCallWatcher::finished,
|
||||||
|
this,
|
||||||
|
[this, game, command, provider, runner, runnerId, runnerPath, prefixPath, program, args, envOverrides, runnerIface, watcher]() {
|
||||||
|
watcher->deleteLater();
|
||||||
|
runnerIface->deleteLater();
|
||||||
|
QDBusPendingReply<QVariantMap> reply = *watcher;
|
||||||
|
if (reply.isError()) {
|
||||||
|
Q_EMIT gameError(game, tr("Runner service is not available"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QVariantMap resolved = unwrapVariantMap(reply.value());
|
||||||
|
if (!resolved.value(QStringLiteral("ok")).toBool()) {
|
||||||
|
Q_EMIT gameError(game, resolved.value(QStringLiteral("error")).toString());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QString finalProgram = resolved.value(QStringLiteral("finalProgram")).toString();
|
||||||
|
const QStringList finalArgs = resolved.value(QStringLiteral("finalArgs")).toStringList();
|
||||||
|
const QVariantMap effectiveEnv = resolved.value(QStringLiteral("effectiveEnv")).toMap();
|
||||||
|
const QString resolvedPrefixPath = resolved.value(QStringLiteral("resolvedPrefixPath")).toString();
|
||||||
|
if (!resolvedPrefixPath.isEmpty()) {
|
||||||
|
QDir().mkpath(resolvedPrefixPath);
|
||||||
|
}
|
||||||
|
QVariantMap launchSpec = {{QStringLiteral("command"), command},
|
||||||
|
{QStringLiteral("gameId"), game->id()},
|
||||||
|
{QStringLiteral("displayName"), game->name()},
|
||||||
|
{QStringLiteral("provider"), provider},
|
||||||
|
{QStringLiteral("origin"), QStringLiteral("ui")},
|
||||||
|
{QStringLiteral("runnerId"), runnerId}};
|
||||||
|
if (!runner.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("runner"), runner);
|
||||||
|
if (!runnerPath.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("runnerPath"), runnerPath);
|
||||||
|
if (!prefixPath.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("prefixPath"), prefixPath);
|
||||||
|
launchSpec.insert(QStringLiteral("requestedProgram"), program);
|
||||||
|
launchSpec.insert(QStringLiteral("requestedArgs"), args);
|
||||||
|
if (!envOverrides.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("requestedEnvOverrides"), envOverrides);
|
||||||
|
if (!finalProgram.isEmpty()) {
|
||||||
|
launchSpec.insert(QStringLiteral("program"), finalProgram);
|
||||||
|
launchSpec.insert(QStringLiteral("args"), finalArgs);
|
||||||
|
}
|
||||||
|
if (!effectiveEnv.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("envOverrides"), effectiveEnv);
|
||||||
|
dispatchLaunch(game, launchSpec);
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString finalProgram = program;
|
||||||
|
QStringList finalArgs = args;
|
||||||
|
QVariantMap effectiveEnv = envOverrides;
|
||||||
|
QString resolvedPrefixPath;
|
||||||
|
QString resolvedSteamInstallPath;
|
||||||
|
|
||||||
|
if (runner == QLatin1String("custom")) {
|
||||||
|
if (runnerPath.isEmpty()) {
|
||||||
|
Q_EMIT gameError(game, tr("Custom runner is enabled but no runner path is configured"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
finalProgram = runnerPath;
|
||||||
|
finalArgs = QStringList{program} + args;
|
||||||
|
} else if (runner == QLatin1String("wine")) {
|
||||||
|
QString wineExe = runnerPath;
|
||||||
|
if (wineExe.isEmpty()) {
|
||||||
|
wineExe = QStandardPaths::findExecutable(QStringLiteral("wine"));
|
||||||
|
}
|
||||||
|
if (wineExe.isEmpty()) {
|
||||||
|
Q_EMIT gameError(game, tr("Wine runner is enabled but Wine was not found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
QString winePrefix = prefixPath;
|
||||||
|
if (winePrefix.isEmpty()) {
|
||||||
|
winePrefix = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/prefixes/") + game->id();
|
||||||
|
}
|
||||||
|
effectiveEnv.insert(QStringLiteral("WINEPREFIX"), winePrefix);
|
||||||
|
resolvedPrefixPath = winePrefix;
|
||||||
|
finalProgram = wineExe;
|
||||||
|
finalArgs = QStringList{program} + args;
|
||||||
|
} else if (runner == QLatin1String("proton")) {
|
||||||
|
QString protonExe = runnerPath;
|
||||||
|
if (protonExe.isEmpty()) {
|
||||||
|
protonExe = discoverDefaultProtonExecutable();
|
||||||
|
}
|
||||||
|
if (protonExe.isEmpty()) {
|
||||||
|
Q_EMIT gameError(game, tr("Proton runner is enabled but no Proton installation was found"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const QString steamInstallPath = findSteamClientInstallPathFromProton(protonExe);
|
||||||
|
resolvedSteamInstallPath = steamInstallPath;
|
||||||
|
QString compatDataPath = prefixPath;
|
||||||
|
if (compatDataPath.isEmpty()) {
|
||||||
|
compatDataPath = QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + QStringLiteral("/prefixes/") + game->id();
|
||||||
|
}
|
||||||
|
effectiveEnv.insert(QStringLiteral("STEAM_COMPAT_DATA_PATH"), compatDataPath);
|
||||||
|
resolvedPrefixPath = compatDataPath;
|
||||||
|
if (!steamInstallPath.isEmpty()) {
|
||||||
|
effectiveEnv.insert(QStringLiteral("STEAM_COMPAT_CLIENT_INSTALL_PATH"), steamInstallPath);
|
||||||
|
}
|
||||||
|
finalProgram = protonExe;
|
||||||
|
finalArgs = QStringList{QStringLiteral("run"), program} + args;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!resolvedPrefixPath.isEmpty()) {
|
||||||
|
QDir().mkpath(resolvedPrefixPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariantMap launchSpec = {{QStringLiteral("command"), command},
|
||||||
|
{QStringLiteral("gameId"), game->id()},
|
||||||
|
{QStringLiteral("displayName"), game->name()},
|
||||||
|
{QStringLiteral("provider"), provider},
|
||||||
|
{QStringLiteral("origin"), QStringLiteral("ui")}};
|
||||||
|
if (!runner.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("runner"), runner);
|
||||||
|
if (!runnerPath.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("runnerPath"), runnerPath);
|
||||||
|
if (!prefixPath.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("prefixPath"), prefixPath);
|
||||||
|
launchSpec.insert(QStringLiteral("requestedProgram"), program);
|
||||||
|
launchSpec.insert(QStringLiteral("requestedArgs"), args);
|
||||||
|
if (!envOverrides.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("requestedEnvOverrides"), envOverrides);
|
||||||
|
launchSpec.insert(QStringLiteral("program"), finalProgram);
|
||||||
|
launchSpec.insert(QStringLiteral("args"), finalArgs);
|
||||||
|
if (!effectiveEnv.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("envOverrides"), effectiveEnv);
|
||||||
|
if (!game->workingDirectory().isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("workingDirectory"), game->workingDirectory());
|
||||||
|
if (!resolvedSteamInstallPath.isEmpty())
|
||||||
|
launchSpec.insert(QStringLiteral("resolvedSteamInstallPath"), resolvedSteamInstallPath);
|
||||||
|
|
||||||
|
dispatchLaunch(game, launchSpec);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameLauncher::stopGame(Game *game)
|
void GameLauncher::stopGame(Game *game)
|
||||||
|
|
@ -836,29 +980,30 @@ void GameLauncher::onDaemonLaunchFailed(const QVariantMap &error)
|
||||||
|
|
||||||
void GameLauncher::syncDaemonSessions()
|
void GameLauncher::syncDaemonSessions()
|
||||||
{
|
{
|
||||||
org::kde::GameCenter1 iface(kGameCenterService, kGameCenterPath, QDBusConnection::sessionBus());
|
auto *iface = new org::kde::GameCenter1(kGameCenterService, kGameCenterPath, QDBusConnection::sessionBus(), this);
|
||||||
iface.setTimeout(2000);
|
iface->setTimeout(2000);
|
||||||
|
auto *watcher = new QDBusPendingCallWatcher(iface->ListSessions(), this);
|
||||||
QDBusPendingReply<QVariantList> reply = iface.ListSessions();
|
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, iface, watcher]() {
|
||||||
reply.waitForFinished();
|
watcher->deleteLater();
|
||||||
if (reply.isError()) {
|
iface->deleteLater();
|
||||||
return;
|
QDBusPendingReply<QVariantList> reply = *watcher;
|
||||||
}
|
if (reply.isError()) {
|
||||||
|
return;
|
||||||
const QVariantList list = reply.value();
|
|
||||||
for (const QVariant &v : list) {
|
|
||||||
QVariantMap map;
|
|
||||||
if (v.canConvert<QVariantMap>()) {
|
|
||||||
map = v.toMap();
|
|
||||||
} else if (v.canConvert<QDBusArgument>()) {
|
|
||||||
map = qdbus_cast<QVariantMap>(v.value<QDBusArgument>());
|
|
||||||
}
|
}
|
||||||
if (!map.isEmpty()) {
|
const QVariantList list = reply.value();
|
||||||
onDaemonSessionAdded(map);
|
for (const QVariant &v : list) {
|
||||||
|
QVariantMap map;
|
||||||
|
if (v.canConvert<QVariantMap>()) {
|
||||||
|
map = v.toMap();
|
||||||
|
} else if (v.canConvert<QDBusArgument>()) {
|
||||||
|
map = qdbus_cast<QVariantMap>(v.value<QDBusArgument>());
|
||||||
|
}
|
||||||
|
if (!map.isEmpty()) {
|
||||||
|
onDaemonSessionAdded(map);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
applyRunningStateToLibrary();
|
||||||
|
});
|
||||||
applyRunningStateToLibrary();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameLauncher::applyRunningStateToLibrary()
|
void GameLauncher::applyRunningStateToLibrary()
|
||||||
|
|
@ -876,18 +1021,31 @@ void GameLauncher::applyRunningStateToLibrary()
|
||||||
void GameLauncher::checkDaemonAvailability()
|
void GameLauncher::checkDaemonAvailability()
|
||||||
{
|
{
|
||||||
ensureDaemon();
|
ensureDaemon();
|
||||||
const bool available = pingDaemon(QDBusConnection::sessionBus());
|
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||||
|
if (!bus.isConnected()) {
|
||||||
disconnectDaemonSignals(QDBusConnection::sessionBus(), this);
|
if (m_daemonAvailable) {
|
||||||
|
m_daemonAvailable = false;
|
||||||
if (available) {
|
Q_EMIT daemonAvailableChanged();
|
||||||
connectDaemonSignals(QDBusConnection::sessionBus(), this);
|
}
|
||||||
}
|
return;
|
||||||
|
|
||||||
if (available != m_daemonAvailable) {
|
|
||||||
m_daemonAvailable = available;
|
|
||||||
Q_EMIT daemonAvailableChanged();
|
|
||||||
}
|
}
|
||||||
|
auto *iface = new org::kde::GameCenter1(kGameCenterService, kGameCenterPath, bus, this);
|
||||||
|
iface->setTimeout(2000);
|
||||||
|
auto *watcher = new QDBusPendingCallWatcher(iface->Ping(), this);
|
||||||
|
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, iface, watcher]() {
|
||||||
|
watcher->deleteLater();
|
||||||
|
iface->deleteLater();
|
||||||
|
QDBusPendingReply<QString> reply = *watcher;
|
||||||
|
const bool available = !reply.isError() && reply.value() == QLatin1String("ok");
|
||||||
|
disconnectDaemonSignals(QDBusConnection::sessionBus(), this);
|
||||||
|
if (available) {
|
||||||
|
connectDaemonSignals(QDBusConnection::sessionBus(), this);
|
||||||
|
}
|
||||||
|
if (available != m_daemonAvailable) {
|
||||||
|
m_daemonAvailable = available;
|
||||||
|
Q_EMIT daemonAvailableChanged();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
bool GameLauncher::daemonAvailable() const
|
bool GameLauncher::daemonAvailable() const
|
||||||
|
|
|
||||||
|
|
@ -57,4 +57,5 @@ private:
|
||||||
void shutdownSpawnedDaemon();
|
void shutdownSpawnedDaemon();
|
||||||
void syncDaemonSessions();
|
void syncDaemonSessions();
|
||||||
void applyRunningStateToLibrary();
|
void applyRunningStateToLibrary();
|
||||||
|
void dispatchLaunch(Game *game, const QVariantMap &launchSpec);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue