mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-26 17:03:08 +00:00
Compare commits
29 commits
cca49615d6
...
0b10b01cc4
| Author | SHA1 | Date | |
|---|---|---|---|
| 0b10b01cc4 | |||
| 09026ca26a | |||
| b24417df69 | |||
| 13b65135f1 | |||
| d254b272fe | |||
| a893d4fd71 | |||
| cd23253386 | |||
| 47cab1cbe5 | |||
| c58801828c | |||
| 1d61f3f84f | |||
| 7c9a0c4a89 | |||
| fe253d89bc | |||
| 239e3a9071 | |||
| 16d4129ede | |||
| 4676b1d9a6 | |||
| d93dbc9ecc | |||
| d94029fbc4 | |||
| ed0e6ae181 | |||
| c0938aa62a | |||
| 9cda4ce476 | |||
| 5b26b85cc3 | |||
| f03eb95b52 | |||
| 7200ad179c | |||
| 985f6dac03 | |||
| d56b91dbd0 | |||
| f3b130008f | |||
| 8891e85dbc | |||
| 57d1e6e130 | |||
| d4e0326974 |
33 changed files with 1658 additions and 311 deletions
|
|
@ -47,6 +47,7 @@ find_package(Qt6 ${QT_MIN_VERSION} REQUIRED COMPONENTS
|
|||
Network
|
||||
Concurrent
|
||||
DBus
|
||||
DBusTools
|
||||
Svg
|
||||
Sql
|
||||
)
|
||||
|
|
|
|||
|
|
@ -28,6 +28,31 @@ add_executable(alakarte
|
|||
config.cpp
|
||||
)
|
||||
|
||||
set(alakarte_dbus_sources)
|
||||
|
||||
qt_add_dbus_interface(alakarte_dbus_sources
|
||||
${CMAKE_SOURCE_DIR}/src/gamecenter/dbus/org.kde.GameCenter1.xml
|
||||
gamecenter1interface
|
||||
)
|
||||
|
||||
qt_add_dbus_interface(alakarte_dbus_sources
|
||||
${CMAKE_SOURCE_DIR}/src/runner/dbus/org.kde.ALaKarte.Runner1.xml
|
||||
runner1interface
|
||||
)
|
||||
|
||||
qt_add_dbus_interface(alakarte_dbus_sources
|
||||
${CMAKE_SOURCE_DIR}/src/input/dbus/org.kde.ALaKarte.Input1.xml
|
||||
input1interface
|
||||
)
|
||||
|
||||
target_sources(alakarte PRIVATE
|
||||
${alakarte_dbus_sources}
|
||||
)
|
||||
|
||||
target_include_directories(alakarte PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
set_source_files_properties(qml/icons/app/org.kde.alakarte.svg PROPERTIES
|
||||
QT_RESOURCE_ALIAS "sc-apps-org.kde.alakarte.svg"
|
||||
)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,30 @@ add_executable(alakarte_gamecenter
|
|||
systemdusermanager.h
|
||||
)
|
||||
|
||||
set(alakarte_gamecenter_dbus_sources)
|
||||
|
||||
qt_add_dbus_adaptor(alakarte_gamecenter_dbus_sources
|
||||
dbus/org.kde.GameCenter1.xml
|
||||
gamecenterdaemon.h
|
||||
GameCenterDaemon
|
||||
gamecenter1adaptor
|
||||
GameCenter1Adaptor
|
||||
)
|
||||
|
||||
qt_add_dbus_interface(alakarte_gamecenter_dbus_sources
|
||||
dbus/org.kde.GameCenter1.xml
|
||||
gamecenter1interface
|
||||
)
|
||||
|
||||
target_sources(alakarte_gamecenter PRIVATE
|
||||
${alakarte_gamecenter_dbus_sources}
|
||||
)
|
||||
|
||||
target_include_directories(alakarte_gamecenter PRIVATE
|
||||
${PROJECT_BINARY_DIR}/src
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(alakarte_gamecenter PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Concurrent
|
||||
|
|
|
|||
|
|
@ -1,13 +0,0 @@
|
|||
<!DOCTYPE busconfig PUBLIC
|
||||
"-//freedesktop//DTD D-Bus Bus Configuration 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
|
||||
<busconfig>
|
||||
<policy user="root">
|
||||
<allow own="org.kde.GameCenter1"/>
|
||||
</policy>
|
||||
|
||||
<policy context="default">
|
||||
<allow send_destination="org.kde.GameCenter1"/>
|
||||
<allow receive_sender="org.kde.GameCenter1"/>
|
||||
</policy>
|
||||
</busconfig>
|
||||
|
|
@ -1,4 +0,0 @@
|
|||
[D-BUS Service]
|
||||
Name=org.kde.GameCenter1
|
||||
Exec=@CMAKE_INSTALL_PREFIX@/@KDE_INSTALL_BINDIR@/alakarte-gamecenter --system
|
||||
SystemdService=org.kde.GameCenter1.service
|
||||
63
src/gamecenter/dbus/org.kde.GameCenter1.xml
Normal file
63
src/gamecenter/dbus/org.kde.GameCenter1.xml
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.GameCenter1">
|
||||
<method name="Ping">
|
||||
<arg name="out" type="s" direction="out"/>
|
||||
</method>
|
||||
<method name="Version">
|
||||
<arg name="major" type="u" direction="out"/>
|
||||
<arg name="minor" type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="GetCapabilities">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="caps" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="ListSessions">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantList"/>
|
||||
<arg name="sessions" type="av" direction="out"/>
|
||||
</method>
|
||||
<method name="GetSession">
|
||||
<arg name="sessionId" type="s" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="session" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="Launch">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<arg name="launchSpec" type="a{sv}" direction="in"/>
|
||||
<arg name="sessionId" type="s" direction="out"/>
|
||||
</method>
|
||||
<method name="Stop">
|
||||
<arg name="sessionId" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="StopByGameId">
|
||||
<arg name="gameId" type="s" direction="in"/>
|
||||
</method>
|
||||
<method name="SetPolicy">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<arg name="policy" type="a{sv}" direction="in"/>
|
||||
</method>
|
||||
<method name="GetPolicy">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="policy" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
|
||||
<signal name="SessionAdded">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="session" type="a{sv}"/>
|
||||
</signal>
|
||||
<signal name="SessionChanged">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="session" type="a{sv}"/>
|
||||
</signal>
|
||||
<signal name="SessionRemoved">
|
||||
<arg name="sessionId" type="s"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
|
||||
<arg name="finalState" type="a{sv}"/>
|
||||
</signal>
|
||||
<signal name="LaunchFailed">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="error" type="a{sv}"/>
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "gamecenterdaemon.h"
|
||||
|
||||
#include "gamecenter1adaptor.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
|
|
@ -803,6 +805,18 @@ void GameCenterDaemon::handleSystemdUnitRemoved(const QString &unitName, const Q
|
|||
return;
|
||||
}
|
||||
|
||||
if (sit.value().scanner) {
|
||||
if (!sit.value().unitPath.path().isEmpty()) {
|
||||
const QString unitPathKey = sit.value().unitPath.path();
|
||||
m_unitPathToSessionId.remove(unitPathKey);
|
||||
if (QObject *watcher = m_unitPathWatchers.take(unitPathKey)) {
|
||||
watcher->deleteLater();
|
||||
}
|
||||
}
|
||||
sit.value().unitPath = {};
|
||||
return;
|
||||
}
|
||||
|
||||
unwatchSystemdUnit(sit.value().unitName, sit.value().unitPath);
|
||||
removeSessionInternal(sessionId, sit.value().stopping ? QStringLiteral("Stopped") : QStringLiteral("Exited"));
|
||||
}
|
||||
|
|
@ -821,6 +835,15 @@ void GameCenterDaemon::handleSystemdUnitPropertiesChanged(const QDBusObjectPath
|
|||
|
||||
auto sit = m_sessions.find(sessionId);
|
||||
if (sit == m_sessions.end()) {
|
||||
unwatchSystemdUnit({}, unitPath);
|
||||
|
||||
for (auto nameIt = m_unitNameToSessionId.begin(); nameIt != m_unitNameToSessionId.end();) {
|
||||
if (nameIt.value() == sessionId) {
|
||||
nameIt = m_unitNameToSessionId.erase(nameIt);
|
||||
} else {
|
||||
++nameIt;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -833,6 +856,25 @@ void GameCenterDaemon::handleSystemdUnitPropertiesChanged(const QDBusObjectPath
|
|||
}
|
||||
const QString activeState = activeStateV.toString();
|
||||
|
||||
if (sit.value().scanner) {
|
||||
if (activeState == QLatin1String("active") || activeState == QLatin1String("activating") || activeState == QLatin1String("deactivating")) {
|
||||
const QList<uint> pids = m_systemd.scopePids(unitPath);
|
||||
if (!pids.isEmpty()) {
|
||||
sit.value().mainPid = pids.first();
|
||||
}
|
||||
} else {
|
||||
const QString unitPathKey = unitPath.path();
|
||||
m_unitPathToSessionId.remove(unitPathKey);
|
||||
if (QObject *watcher = m_unitPathWatchers.take(unitPathKey)) {
|
||||
watcher->deleteLater();
|
||||
}
|
||||
sit.value().unitPath = {};
|
||||
}
|
||||
|
||||
Q_EMIT SessionChanged(sessionToVariantMap(sit.value(), sessionState(sit.value())));
|
||||
return;
|
||||
}
|
||||
|
||||
if (activeState == QLatin1String("active") || activeState == QLatin1String("activating") || activeState == QLatin1String("deactivating")) {
|
||||
const QList<uint> pids = m_systemd.scopePids(unitPath);
|
||||
if (!pids.isEmpty()) {
|
||||
|
|
@ -928,11 +970,10 @@ bool GameCenterDaemon::init()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!bus.registerService(QStringLiteral("org.kde.GameCenter1"))) {
|
||||
return false;
|
||||
}
|
||||
new GameCenter1Adaptor(this);
|
||||
|
||||
if (!bus.registerObject(QStringLiteral("/org/kde/GameCenter1"), this, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) {
|
||||
if (!bus.registerObject(QStringLiteral("/org/kde/ALaKarte/GameCenter1"), this, QDBusConnection::ExportAdaptors)) {
|
||||
qWarning() << "Failed to register D-Bus object" << bus.lastError().name() << bus.lastError().message();
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -978,10 +1019,10 @@ QString GameCenterDaemon::Ping() const
|
|||
return QStringLiteral("ok");
|
||||
}
|
||||
|
||||
void GameCenterDaemon::Version(uint &major, uint &minor) const
|
||||
uint GameCenterDaemon::Version(uint &minor) const
|
||||
{
|
||||
major = 1;
|
||||
minor = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariantMap GameCenterDaemon::GetCapabilities() const
|
||||
|
|
@ -1365,7 +1406,7 @@ QString GameCenterDaemon::launchMonitored(const QVariantMap &launchSpec, const Q
|
|||
// Determine bootstrap command and scanner matcher
|
||||
QString bootstrapProgram;
|
||||
QStringList bootstrapArgs;
|
||||
std::function<QList<ProcessScanner::Match>()> matcher;
|
||||
ProcessScanner::Matcher matcher;
|
||||
|
||||
if (provider == QLatin1String("steam")) {
|
||||
const QString appId = extractSteamAppId(command);
|
||||
|
|
@ -1377,9 +1418,13 @@ QString GameCenterDaemon::launchMonitored(const QVariantMap &launchSpec, const Q
|
|||
bootstrapProgram = QStringLiteral("steam");
|
||||
bootstrapArgs = {QStringLiteral("-silent"), QStringLiteral("-applaunch"), appId};
|
||||
|
||||
matcher = [appId]() -> QList<ProcessScanner::Match> {
|
||||
// Primary: look for SteamAppId=<appId> in environ
|
||||
QList<ProcessScanner::Match> results = ProcessScanner::findByAnyEnvironment({QStringLiteral("SteamAppId"), QStringLiteral("SteamGameId")}, appId);
|
||||
matcher = [appId](const std::atomic_bool &cancelled) -> QList<ProcessScanner::Match> {
|
||||
ProcessScanner::ScanOptions opts;
|
||||
opts.envKeys = {QStringLiteral("SteamAppId"), QStringLiteral("SteamGameId")};
|
||||
opts.envValue = appId;
|
||||
opts.preferEnvironmentMatches = true;
|
||||
|
||||
QList<ProcessScanner::Match> results = ProcessScanner::scan(opts, cancelled);
|
||||
// Filter out the steam client itself
|
||||
QList<ProcessScanner::Match> filtered;
|
||||
for (const auto &m : std::as_const(results)) {
|
||||
|
|
@ -1401,11 +1446,25 @@ QString GameCenterDaemon::launchMonitored(const QVariantMap &launchSpec, const Q
|
|||
bootstrapArgs = {QStringLiteral("lutris:rungameid/%1").arg(lutrisId)};
|
||||
|
||||
// For Lutris, look for child processes with the game slug or lutris game env
|
||||
matcher = [lutrisId]() -> QList<ProcessScanner::Match> {
|
||||
QList<ProcessScanner::Match> results = ProcessScanner::findByEnvironment(QStringLiteral("LUTRIS_GAME_SLUG"), lutrisId);
|
||||
if (results.isEmpty()) {
|
||||
results = ProcessScanner::findByCmdline(lutrisId);
|
||||
// Filter out lutris itself
|
||||
matcher = [lutrisId](const std::atomic_bool &cancelled) -> QList<ProcessScanner::Match> {
|
||||
ProcessScanner::ScanOptions opts;
|
||||
opts.envKeys = {QStringLiteral("LUTRIS_GAME_SLUG")};
|
||||
opts.envValue = lutrisId;
|
||||
opts.cmdlineSubstring = lutrisId;
|
||||
opts.preferEnvironmentMatches = true;
|
||||
|
||||
QList<ProcessScanner::Match> results = ProcessScanner::scan(opts, cancelled);
|
||||
|
||||
bool anyEnvMatch = false;
|
||||
for (const auto &m : std::as_const(results)) {
|
||||
if (m.envMatched) {
|
||||
anyEnvMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!anyEnvMatch) {
|
||||
// Filter out lutris itself (only for cmdline fallback)
|
||||
QList<ProcessScanner::Match> filtered;
|
||||
for (const auto &m : std::as_const(results)) {
|
||||
if (!m.exe.endsWith(QLatin1String("/lutris")) && !m.exe.endsWith(QLatin1String("/python3"))) {
|
||||
|
|
@ -1416,6 +1475,7 @@ QString GameCenterDaemon::launchMonitored(const QVariantMap &launchSpec, const Q
|
|||
results = filtered;
|
||||
}
|
||||
}
|
||||
|
||||
return results;
|
||||
};
|
||||
}
|
||||
|
|
@ -1498,6 +1558,8 @@ QString GameCenterDaemon::launchMonitored(const QVariantMap &launchSpec, const Q
|
|||
|
||||
m_sessions[sessionId] = session;
|
||||
|
||||
watchSystemdUnit(sessionId, session.unitName, session.unitPath);
|
||||
|
||||
connect(bootstrap, qOverload<int, QProcess::ExitStatus>(&QProcess::finished), this, [this, sessionId](int, QProcess::ExitStatus) {
|
||||
const auto it = m_sessions.find(sessionId);
|
||||
if (it == m_sessions.end()) {
|
||||
|
|
@ -1540,8 +1602,31 @@ QString GameCenterDaemon::launchMonitored(const QVariantMap &launchSpec, const Q
|
|||
QVariantMap finalState = sessionToVariantMap(it.value(), stopping ? QStringLiteral("Stopped") : QStringLiteral("Failed"));
|
||||
|
||||
if (!it.value().unitName.isEmpty()) {
|
||||
m_systemd.stopUnit(it.value().unitName);
|
||||
unwatchSystemdUnit(it.value().unitName, it.value().unitPath);
|
||||
const QString unitName = it.value().unitName;
|
||||
const QDBusObjectPath unitPath = it.value().unitPath;
|
||||
const uint mainPid = it.value().mainPid;
|
||||
|
||||
const QDBusReply<QDBusObjectPath> stopReply = m_systemd.stopUnit(unitName);
|
||||
if (!stopReply.isValid() && stopReply.error().name() != QLatin1String("org.freedesktop.systemd1.NoSuchUnit")) {
|
||||
QList<uint> pids;
|
||||
if (!unitPath.path().isEmpty()) {
|
||||
pids = m_systemd.scopePids(unitPath);
|
||||
}
|
||||
if (pids.isEmpty() && it.value().process && it.value().process->processId() > 0) {
|
||||
pids = {static_cast<uint>(it.value().process->processId())};
|
||||
}
|
||||
if (pids.isEmpty() && mainPid > 0) {
|
||||
pids = {mainPid};
|
||||
}
|
||||
if (!pids.isEmpty()) {
|
||||
terminatePids(pids);
|
||||
QTimer::singleShot(5000, this, [pids]() {
|
||||
killPids(pids);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
unwatchSystemdUnit(unitName, unitPath);
|
||||
}
|
||||
|
||||
if (it.value().scanner) {
|
||||
|
|
@ -1612,6 +1697,28 @@ void GameCenterDaemon::attachPidsToSession(const QString &sessionId, const QList
|
|||
{QStringLiteral("error"), attachReply.error().message()},
|
||||
};
|
||||
|
||||
if (!it.value().unitName.isEmpty()) {
|
||||
const QString staleUnitName = it.value().unitName;
|
||||
const QDBusObjectPath staleUnitPath = it.value().unitPath;
|
||||
|
||||
const QDBusReply<QDBusObjectPath> stopReply = m_systemd.stopUnit(staleUnitName);
|
||||
if (!stopReply.isValid() && stopReply.error().name() != QLatin1String("org.freedesktop.systemd1.NoSuchUnit")) {
|
||||
QList<uint> stalePids;
|
||||
if (!staleUnitPath.path().isEmpty()) {
|
||||
stalePids = m_systemd.scopePids(staleUnitPath);
|
||||
}
|
||||
if (stalePids.isEmpty()) {
|
||||
stalePids = pids;
|
||||
}
|
||||
terminatePids(stalePids);
|
||||
QTimer::singleShot(5000, this, [stalePids]() {
|
||||
killPids(stalePids);
|
||||
});
|
||||
}
|
||||
|
||||
unwatchSystemdUnit(staleUnitName, staleUnitPath);
|
||||
}
|
||||
|
||||
QVariantMap finalState = sessionToVariantMap(it.value(), QStringLiteral("Failed"));
|
||||
if (it.value().process) {
|
||||
it.value().process->deleteLater();
|
||||
|
|
@ -1783,6 +1890,32 @@ void GameCenterDaemon::Stop(const QString &sessionId)
|
|||
return;
|
||||
}
|
||||
|
||||
QDBusObjectPath unitPath = it.value().unitPath;
|
||||
if (unitPath.path().isEmpty()) {
|
||||
const QDBusReply<QDBusObjectPath> getUnitReply = m_systemd.getUnit(it.value().unitName);
|
||||
if (getUnitReply.isValid()) {
|
||||
unitPath = getUnitReply.value();
|
||||
it.value().unitPath = unitPath;
|
||||
watchSystemdUnit(sessionId, it.value().unitName, unitPath);
|
||||
}
|
||||
}
|
||||
|
||||
QList<uint> pids;
|
||||
if (!unitPath.path().isEmpty()) {
|
||||
pids = m_systemd.scopePids(unitPath);
|
||||
}
|
||||
if (pids.isEmpty() && it.value().mainPid > 0) {
|
||||
pids = {it.value().mainPid};
|
||||
}
|
||||
if (!pids.isEmpty()) {
|
||||
terminatePids(pids);
|
||||
QTimer::singleShot(5000, this, [pids]() {
|
||||
killPids(pids);
|
||||
});
|
||||
Q_EMIT SessionChanged(sessionToVariantMap(it.value(), QStringLiteral("Stopping")));
|
||||
return;
|
||||
}
|
||||
|
||||
if (it.value().process && it.value().process->state() != QProcess::NotRunning) {
|
||||
it.value().process->terminate();
|
||||
QTimer::singleShot(5000, it.value().process, [process = it.value().process]() {
|
||||
|
|
@ -1889,7 +2022,8 @@ void GameCenterDaemon::recoverExistingSessions()
|
|||
|
||||
static const QString prefix = QStringLiteral("alakarte-game-");
|
||||
static const QString suffix = QStringLiteral(".scope");
|
||||
static const QString descPrefix = QStringLiteral("A-La-Karte game ");
|
||||
static const QString descBase = QStringLiteral("A-La-Karte game");
|
||||
static const QString descPrefix = descBase + QLatin1Char(' ');
|
||||
|
||||
const SystemdUnitInfoList units = reply.value();
|
||||
for (const SystemdUnitInfo &unit : units) {
|
||||
|
|
@ -1900,6 +2034,10 @@ void GameCenterDaemon::recoverExistingSessions()
|
|||
continue;
|
||||
}
|
||||
|
||||
if (unit.description != descBase && !unit.description.startsWith(descPrefix)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const QString sessionId = unit.name.mid(prefix.length(), unit.name.length() - prefix.length() - suffix.length());
|
||||
if (sessionId.isEmpty() || m_sessions.contains(sessionId)) {
|
||||
continue;
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ public:
|
|||
|
||||
public Q_SLOTS:
|
||||
QString Ping() const;
|
||||
void Version(uint &major, uint &minor) const;
|
||||
uint Version(uint &minor) const;
|
||||
QVariantMap GetCapabilities() const;
|
||||
|
||||
QVariantList ListSessions() const;
|
||||
|
|
|
|||
|
|
@ -4,7 +4,9 @@
|
|||
#include <KAuth/ActionReply>
|
||||
#include <KAuth/HelperSupport>
|
||||
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusError>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusMessage>
|
||||
|
|
@ -28,6 +30,14 @@ public Q_SLOTS:
|
|||
ActionReply setpowerprofile(const QVariantMap &args);
|
||||
};
|
||||
|
||||
static QVariant unwrapDbusVariant(QVariant v)
|
||||
{
|
||||
if (v.canConvert<QDBusVariant>()) {
|
||||
v = v.value<QDBusVariant>().variant();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static QString unwrapStringArg(const QVariantMap &args, const QString &key)
|
||||
{
|
||||
QVariant v = args.value(key);
|
||||
|
|
@ -53,6 +63,12 @@ ActionReply GameCenterHelper::setpowerprofile(const QVariantMap &args)
|
|||
return reply;
|
||||
}
|
||||
|
||||
if (bus.interface() && !bus.interface()->isServiceRegistered(kPowerProfilesService)) {
|
||||
ActionReply reply = ActionReply::HelperErrorReply();
|
||||
reply.setErrorDescription(QStringLiteral("power-profiles-daemon service not available"));
|
||||
return reply;
|
||||
}
|
||||
|
||||
QDBusInterface props(kPowerProfilesService, kPowerProfilesPath, QStringLiteral("org.freedesktop.DBus.Properties"), bus);
|
||||
if (!props.isValid()) {
|
||||
ActionReply reply = ActionReply::HelperErrorReply();
|
||||
|
|
@ -60,6 +76,44 @@ ActionReply GameCenterHelper::setpowerprofile(const QVariantMap &args)
|
|||
return reply;
|
||||
}
|
||||
|
||||
{
|
||||
const QDBusReply<QVariant> profilesReply = props.call(QStringLiteral("Get"), kPowerProfilesInterface, QStringLiteral("Profiles"));
|
||||
if (profilesReply.isValid()) {
|
||||
QVariant v = unwrapDbusVariant(profilesReply.value());
|
||||
QVariantList list;
|
||||
if (v.canConvert<QDBusArgument>()) {
|
||||
list = qdbus_cast<QVariantList>(v.value<QDBusArgument>());
|
||||
} else if (v.canConvert<QVariantList>()) {
|
||||
list = v.toList();
|
||||
}
|
||||
|
||||
if (!list.isEmpty()) {
|
||||
QStringList available;
|
||||
available.reserve(list.size());
|
||||
for (const QVariant &item : list) {
|
||||
QVariant mV = unwrapDbusVariant(item);
|
||||
QVariantMap m;
|
||||
if (mV.canConvert<QDBusArgument>()) {
|
||||
m = qdbus_cast<QVariantMap>(mV.value<QDBusArgument>());
|
||||
} else if (mV.canConvert<QVariantMap>()) {
|
||||
m = mV.toMap();
|
||||
}
|
||||
const QString name = m.value(QStringLiteral("Profile")).toString();
|
||||
if (!name.isEmpty()) {
|
||||
available.push_back(name);
|
||||
}
|
||||
}
|
||||
|
||||
if (!available.isEmpty() && !available.contains(profile)) {
|
||||
ActionReply reply = ActionReply::HelperErrorReply();
|
||||
reply.setErrorDescription(QStringLiteral("unsupported profile '%1'").arg(profile));
|
||||
reply.addData(QStringLiteral("available"), available);
|
||||
return reply;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
QDBusMessage msg =
|
||||
QDBusMessage::createMethodCall(kPowerProfilesService, kPowerProfilesPath, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Set"));
|
||||
msg.setArguments({kPowerProfilesInterface, QStringLiteral("ActiveProfile"), QVariant::fromValue(QDBusVariant(profile))});
|
||||
|
|
|
|||
|
|
@ -5,9 +5,11 @@
|
|||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <KAboutData>
|
||||
#include <KDBusService>
|
||||
#include <KSignalHandler>
|
||||
|
||||
#include "alakarte-version.h"
|
||||
#include "gamecenterdaemon.h"
|
||||
|
||||
#include <signal.h>
|
||||
|
|
@ -16,17 +18,34 @@ int main(int argc, char *argv[])
|
|||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setOrganizationDomain(QStringLiteral("kde.org"));
|
||||
app.setApplicationName(QStringLiteral("alakarte_gamecenter"));
|
||||
app.setApplicationName(QStringLiteral("GameCenter1"));
|
||||
|
||||
KDBusService service(KDBusService::Unique);
|
||||
KAboutData aboutData(QStringLiteral("alakarte-gamecenter"),
|
||||
QStringLiteral("A-La-Karte Game Center"),
|
||||
QStringLiteral(ALAKARTE_VERSION_STRING),
|
||||
QStringLiteral("Game session management daemon for A-La-Karte"),
|
||||
KAboutLicense::GPL_V3,
|
||||
QStringLiteral("© 2026 A-La-Karte Contributors"));
|
||||
aboutData.addAuthor(QStringLiteral("A-La-Karte Contributors"), QStringLiteral("Developer"), QString());
|
||||
KAboutData::setApplicationData(aboutData);
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QStringLiteral("A-La-Karte Game Center"));
|
||||
parser.addHelpOption();
|
||||
aboutData.setupCommandLine(&parser);
|
||||
parser.process(app);
|
||||
aboutData.processCommandLine(&parser);
|
||||
|
||||
GameCenterDaemon daemon;
|
||||
if (!daemon.init()) {
|
||||
qWarning() << "GameCenter daemon initialization failed";
|
||||
return 1;
|
||||
}
|
||||
|
||||
app.setOrganizationDomain(QStringLiteral("kde.org"));
|
||||
app.setApplicationName(QStringLiteral("GameCenter1"));
|
||||
|
||||
KDBusService service(KDBusService::Unique | KDBusService::NoExitOnFailure);
|
||||
if (!service.isRegistered()) {
|
||||
qWarning() << "Failed to register D-Bus service" << service.serviceName() << service.errorMessage();
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -6,8 +6,19 @@
|
|||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QFileInfo>
|
||||
#include <QLoggingCategory>
|
||||
#include <QtConcurrentRun>
|
||||
|
||||
#include <utility>
|
||||
|
||||
Q_LOGGING_CATEGORY(lcProcessScanner, "org.kde.alakarte.processscanner")
|
||||
|
||||
static QString readExeLink(const QString &pidDir);
|
||||
static QByteArray readCmdline(const QString &pidDir);
|
||||
static QByteArray readEnviron(const QString &pidDir);
|
||||
static bool containsNullSeparatedEntry(const QByteArray &blob, const QByteArray &needle);
|
||||
static QList<uint> listPids();
|
||||
|
||||
ProcessScanner::ProcessScanner(QObject *parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
|
|
@ -16,7 +27,11 @@ ProcessScanner::ProcessScanner(QObject *parent)
|
|||
|
||||
connect(&m_deadline, &QTimer::timeout, this, [this]() {
|
||||
m_timer.stop();
|
||||
m_matcher = nullptr;
|
||||
if (m_cancelToken) {
|
||||
m_cancelToken->store(true);
|
||||
}
|
||||
m_cancelToken.reset();
|
||||
m_matcher = {};
|
||||
++m_generation;
|
||||
m_scanInFlight = false;
|
||||
Q_EMIT timedOut();
|
||||
|
|
@ -42,7 +57,8 @@ void ProcessScanner::startScan()
|
|||
m_scanInFlight = true;
|
||||
m_scanGeneration = gen;
|
||||
|
||||
const std::function<QList<Match>()> matcher = m_matcher;
|
||||
const Matcher matcher = m_matcher;
|
||||
const std::shared_ptr<std::atomic_bool> cancelToken = m_cancelToken;
|
||||
auto *watcher = new QFutureWatcher<QList<Match>>(this);
|
||||
|
||||
connect(watcher, &QFutureWatcher<QList<Match>>::finished, this, [this, watcher, gen]() {
|
||||
|
|
@ -60,19 +76,130 @@ void ProcessScanner::startScan()
|
|||
if (!results.isEmpty()) {
|
||||
m_timer.stop();
|
||||
m_deadline.stop();
|
||||
m_matcher = nullptr;
|
||||
if (m_cancelToken) {
|
||||
m_cancelToken->store(true);
|
||||
}
|
||||
m_cancelToken.reset();
|
||||
m_matcher = {};
|
||||
Q_EMIT found(results);
|
||||
}
|
||||
});
|
||||
|
||||
watcher->setFuture(QtConcurrent::run([matcher]() {
|
||||
watcher->setFuture(QtConcurrent::run([matcher, cancelToken]() {
|
||||
static const std::atomic_bool neverCancelled{false};
|
||||
const std::atomic_bool &cancelled = cancelToken ? *cancelToken : neverCancelled;
|
||||
if (!matcher) {
|
||||
return QList<Match>{};
|
||||
}
|
||||
return matcher();
|
||||
return matcher(cancelled);
|
||||
}));
|
||||
}
|
||||
|
||||
QList<ProcessScanner::Match> ProcessScanner::scan(const ScanOptions &options)
|
||||
{
|
||||
static const std::atomic_bool neverCancelled{false};
|
||||
return scan(options, neverCancelled);
|
||||
}
|
||||
|
||||
QList<ProcessScanner::Match> ProcessScanner::scan(const ScanOptions &options, const std::atomic_bool &cancelled)
|
||||
{
|
||||
QList<Match> matches;
|
||||
|
||||
QList<QByteArray> envNeedles;
|
||||
if (!options.envKeys.isEmpty() && !options.envValue.isEmpty()) {
|
||||
envNeedles.reserve(options.envKeys.size());
|
||||
for (const QString &key : options.envKeys) {
|
||||
if (!key.isEmpty()) {
|
||||
envNeedles.push_back((key + QLatin1Char('=') + options.envValue).toUtf8());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const QByteArray cmdNeedle = options.cmdlineSubstring.isEmpty() ? QByteArray{} : options.cmdlineSubstring.toUtf8();
|
||||
const bool checkExePrefix = !options.exePathPrefix.isEmpty();
|
||||
|
||||
const QList<uint> pids = listPids();
|
||||
for (uint pid : pids) {
|
||||
if (cancelled.load()) {
|
||||
break;
|
||||
}
|
||||
|
||||
const QString pidDir = QStringLiteral("/proc/%1").arg(pid);
|
||||
|
||||
bool envMatched = false;
|
||||
if (!envNeedles.isEmpty()) {
|
||||
const QByteArray env = readEnviron(pidDir);
|
||||
if (!env.isEmpty()) {
|
||||
for (const QByteArray &needle : envNeedles) {
|
||||
if (containsNullSeparatedEntry(env, needle)) {
|
||||
envMatched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool cmdlineMatched = false;
|
||||
QByteArray cmd;
|
||||
if (!cmdNeedle.isEmpty()) {
|
||||
cmd = readCmdline(pidDir);
|
||||
cmdlineMatched = !cmd.isEmpty() && cmd.contains(cmdNeedle);
|
||||
}
|
||||
|
||||
bool exeMatched = false;
|
||||
QString exe;
|
||||
if (checkExePrefix) {
|
||||
exe = readExeLink(pidDir);
|
||||
exeMatched = !exe.isEmpty() && exe.startsWith(options.exePathPrefix);
|
||||
}
|
||||
|
||||
if (!envMatched && !cmdlineMatched && !exeMatched) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Match m;
|
||||
m.pid = pid;
|
||||
m.envMatched = envMatched;
|
||||
m.cmdlineMatched = cmdlineMatched;
|
||||
m.exeMatched = exeMatched;
|
||||
|
||||
if (exe.isEmpty()) {
|
||||
exe = readExeLink(pidDir);
|
||||
}
|
||||
m.exe = exe;
|
||||
|
||||
if (cmd.isEmpty()) {
|
||||
cmd = readCmdline(pidDir);
|
||||
}
|
||||
m.cmdline = QString::fromLocal8Bit(cmd).replace(QLatin1Char('\0'), QLatin1Char(' ')).trimmed();
|
||||
|
||||
matches.push_back(m);
|
||||
}
|
||||
|
||||
if (options.preferEnvironmentMatches) {
|
||||
bool anyEnvMatch = false;
|
||||
for (const Match &m : std::as_const(matches)) {
|
||||
if (m.envMatched) {
|
||||
anyEnvMatch = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (anyEnvMatch) {
|
||||
QList<Match> filtered;
|
||||
filtered.reserve(matches.size());
|
||||
for (const Match &m : std::as_const(matches)) {
|
||||
if (m.envMatched) {
|
||||
filtered.push_back(m);
|
||||
}
|
||||
}
|
||||
return filtered;
|
||||
}
|
||||
}
|
||||
|
||||
return matches;
|
||||
}
|
||||
|
||||
static QByteArray readProcFile(const QString &path, qint64 maxSize = 65536)
|
||||
{
|
||||
QFile f(path);
|
||||
|
|
@ -136,141 +263,75 @@ static QList<uint> listPids()
|
|||
|
||||
QList<ProcessScanner::Match> ProcessScanner::findByEnvironment(const QString &key, const QString &value)
|
||||
{
|
||||
QList<Match> matches;
|
||||
const QByteArray needle = (key + QLatin1Char('=') + value).toUtf8();
|
||||
|
||||
const QList<uint> pids = listPids();
|
||||
for (uint pid : pids) {
|
||||
const QString pidDir = QStringLiteral("/proc/%1").arg(pid);
|
||||
const QByteArray env = readEnviron(pidDir);
|
||||
if (env.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (containsNullSeparatedEntry(env, needle)) {
|
||||
Match m;
|
||||
m.pid = pid;
|
||||
m.exe = readExeLink(pidDir);
|
||||
const QByteArray cmd = readCmdline(pidDir);
|
||||
m.cmdline = QString::fromLocal8Bit(cmd).replace(QLatin1Char('\0'), QLatin1Char(' ')).trimmed();
|
||||
matches.push_back(m);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
ScanOptions opts;
|
||||
opts.envKeys = {key};
|
||||
opts.envValue = value;
|
||||
return scan(opts);
|
||||
}
|
||||
|
||||
QList<ProcessScanner::Match> ProcessScanner::findByAnyEnvironment(const QStringList &keys, const QString &value)
|
||||
{
|
||||
QList<Match> matches;
|
||||
if (keys.isEmpty()) {
|
||||
return matches;
|
||||
}
|
||||
|
||||
QList<QByteArray> needles;
|
||||
needles.reserve(keys.size());
|
||||
for (const QString &key : keys) {
|
||||
if (!key.isEmpty()) {
|
||||
needles.push_back((key + QLatin1Char('=') + value).toUtf8());
|
||||
}
|
||||
}
|
||||
if (needles.isEmpty()) {
|
||||
return matches;
|
||||
}
|
||||
|
||||
const QList<uint> pids = listPids();
|
||||
for (uint pid : pids) {
|
||||
const QString pidDir = QStringLiteral("/proc/%1").arg(pid);
|
||||
const QByteArray env = readEnviron(pidDir);
|
||||
if (env.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
bool found = false;
|
||||
for (const QByteArray &needle : needles) {
|
||||
if (containsNullSeparatedEntry(env, needle)) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Match m;
|
||||
m.pid = pid;
|
||||
m.exe = readExeLink(pidDir);
|
||||
const QByteArray cmd = readCmdline(pidDir);
|
||||
m.cmdline = QString::fromLocal8Bit(cmd).replace(QLatin1Char('\0'), QLatin1Char(' ')).trimmed();
|
||||
matches.push_back(m);
|
||||
}
|
||||
|
||||
return matches;
|
||||
ScanOptions opts;
|
||||
opts.envKeys = keys;
|
||||
opts.envValue = value;
|
||||
return scan(opts);
|
||||
}
|
||||
|
||||
QList<ProcessScanner::Match> ProcessScanner::findByCmdline(const QString &substring)
|
||||
{
|
||||
QList<Match> matches;
|
||||
const QByteArray needle = substring.toUtf8();
|
||||
|
||||
const QList<uint> pids = listPids();
|
||||
for (uint pid : pids) {
|
||||
const QString pidDir = QStringLiteral("/proc/%1").arg(pid);
|
||||
const QByteArray cmd = readCmdline(pidDir);
|
||||
if (cmd.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// cmdline has null-separated args; search the whole blob
|
||||
if (cmd.contains(needle)) {
|
||||
Match m;
|
||||
m.pid = pid;
|
||||
m.exe = readExeLink(pidDir);
|
||||
m.cmdline = QString::fromLocal8Bit(cmd).replace(QLatin1Char('\0'), QLatin1Char(' ')).trimmed();
|
||||
matches.push_back(m);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
ScanOptions opts;
|
||||
opts.cmdlineSubstring = substring;
|
||||
return scan(opts);
|
||||
}
|
||||
|
||||
QList<ProcessScanner::Match> ProcessScanner::findByExePath(const QString &dirPrefix)
|
||||
{
|
||||
QList<Match> matches;
|
||||
|
||||
const QList<uint> pids = listPids();
|
||||
for (uint pid : pids) {
|
||||
const QString pidDir = QStringLiteral("/proc/%1").arg(pid);
|
||||
const QString exe = readExeLink(pidDir);
|
||||
if (exe.isEmpty()) {
|
||||
continue;
|
||||
}
|
||||
if (exe.startsWith(dirPrefix)) {
|
||||
Match m;
|
||||
m.pid = pid;
|
||||
m.exe = exe;
|
||||
const QByteArray cmd = readCmdline(pidDir);
|
||||
m.cmdline = QString::fromLocal8Bit(cmd).replace(QLatin1Char('\0'), QLatin1Char(' ')).trimmed();
|
||||
matches.push_back(m);
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
ScanOptions opts;
|
||||
opts.exePathPrefix = dirPrefix;
|
||||
return scan(opts);
|
||||
}
|
||||
|
||||
void ProcessScanner::pollUntilFound(std::function<QList<Match>()> matcher, int intervalMs, int timeoutMs)
|
||||
void ProcessScanner::pollUntilFound(Matcher matcher, int intervalMs, int timeoutMs)
|
||||
{
|
||||
cancel();
|
||||
m_cancelToken = std::make_shared<std::atomic_bool>(false);
|
||||
m_matcher = std::move(matcher);
|
||||
|
||||
if (!m_matcher) {
|
||||
return;
|
||||
}
|
||||
|
||||
m_deadline.start(timeoutMs);
|
||||
m_timer.start(intervalMs);
|
||||
|
||||
startScan();
|
||||
}
|
||||
|
||||
void ProcessScanner::pollUntilFound(std::function<QList<Match>()> matcher, int intervalMs, int timeoutMs)
|
||||
{
|
||||
pollUntilFound(
|
||||
[matcher = std::move(matcher)](const std::atomic_bool &cancelled) {
|
||||
if (cancelled.load()) {
|
||||
return QList<Match>{};
|
||||
}
|
||||
if (!matcher) {
|
||||
return QList<Match>{};
|
||||
}
|
||||
return matcher();
|
||||
},
|
||||
intervalMs,
|
||||
timeoutMs);
|
||||
}
|
||||
|
||||
void ProcessScanner::cancel()
|
||||
{
|
||||
m_timer.stop();
|
||||
m_deadline.stop();
|
||||
m_matcher = nullptr;
|
||||
if (m_cancelToken) {
|
||||
m_cancelToken->store(true);
|
||||
}
|
||||
m_cancelToken.reset();
|
||||
m_matcher = {};
|
||||
++m_generation;
|
||||
m_scanInFlight = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,11 +5,16 @@
|
|||
|
||||
#include <QFutureWatcher>
|
||||
#include <QList>
|
||||
#include <QLoggingCategory>
|
||||
#include <QObject>
|
||||
#include <QString>
|
||||
#include <QTimer>
|
||||
#include <QtGlobal>
|
||||
#include <atomic>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
Q_DECLARE_LOGGING_CATEGORY(lcProcessScanner)
|
||||
|
||||
class ProcessScanner : public QObject
|
||||
{
|
||||
|
|
@ -18,12 +23,28 @@ class ProcessScanner : public QObject
|
|||
public:
|
||||
explicit ProcessScanner(QObject *parent = nullptr);
|
||||
|
||||
struct ScanOptions {
|
||||
QStringList envKeys;
|
||||
QString envValue;
|
||||
QString cmdlineSubstring;
|
||||
QString exePathPrefix;
|
||||
bool preferEnvironmentMatches = false;
|
||||
};
|
||||
|
||||
struct Match {
|
||||
uint pid = 0;
|
||||
QString exe;
|
||||
QString cmdline;
|
||||
bool envMatched = false;
|
||||
bool cmdlineMatched = false;
|
||||
bool exeMatched = false;
|
||||
};
|
||||
|
||||
using Matcher = std::function<QList<Match>(const std::atomic_bool &cancelled)>;
|
||||
|
||||
static QList<Match> scan(const ScanOptions &options);
|
||||
static QList<Match> scan(const ScanOptions &options, const std::atomic_bool &cancelled);
|
||||
|
||||
// Find PIDs whose /proc/<pid>/environ contains key=value
|
||||
static QList<Match> findByEnvironment(const QString &key, const QString &value);
|
||||
|
||||
|
|
@ -38,6 +59,7 @@ public:
|
|||
|
||||
// Async poll: calls matcher repeatedly until it returns non-empty or timeout.
|
||||
// Emits found() with matching PIDs, or timedOut() on failure.
|
||||
void pollUntilFound(Matcher matcher, int intervalMs = 500, int timeoutMs = 15000);
|
||||
void pollUntilFound(std::function<QList<Match>()> matcher, int intervalMs = 500, int timeoutMs = 15000);
|
||||
|
||||
void cancel();
|
||||
|
|
@ -51,7 +73,8 @@ private:
|
|||
|
||||
QTimer m_timer;
|
||||
QTimer m_deadline;
|
||||
std::function<QList<Match>()> m_matcher;
|
||||
Matcher m_matcher;
|
||||
std::shared_ptr<std::atomic_bool> m_cancelToken;
|
||||
quint64 m_generation = 0;
|
||||
bool m_scanInFlight = false;
|
||||
quint64 m_scanGeneration = 0;
|
||||
|
|
|
|||
|
|
@ -1,8 +0,0 @@
|
|||
[Unit]
|
||||
Description=A-La-Karte Game Center
|
||||
|
||||
[Service]
|
||||
ExecStart=@CMAKE_INSTALL_PREFIX@/@KDE_INSTALL_BINDIR@/alakarte-gamecenter --system
|
||||
Type=dbus
|
||||
BusName=org.kde.GameCenter1
|
||||
Restart=no
|
||||
|
|
@ -4,13 +4,14 @@
|
|||
#include "gamelauncher.h"
|
||||
#include "app.h"
|
||||
|
||||
#include "gamecenter1interface.h"
|
||||
#include "runner1interface.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusError>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusVariant>
|
||||
#include <QDateTime>
|
||||
#include <QDir>
|
||||
|
|
@ -20,12 +21,11 @@
|
|||
#include <QTimer>
|
||||
|
||||
static const QString kGameCenterService = QStringLiteral("org.kde.GameCenter1");
|
||||
static const QString kGameCenterPath = QStringLiteral("/org/kde/GameCenter1");
|
||||
static const QString kGameCenterPath = QStringLiteral("/org/kde/ALaKarte/GameCenter1");
|
||||
static const QString kGameCenterInterface = QStringLiteral("org.kde.GameCenter1");
|
||||
|
||||
static const QString kRunnerService = QStringLiteral("org.kde.ALaKarte.Runner1");
|
||||
static const QString kRunnerPath = QStringLiteral("/org/kde/ALaKarte/Runner1");
|
||||
static const QString kRunnerInterface = QStringLiteral("org.kde.ALaKarte.Runner1");
|
||||
|
||||
static bool pingDaemon(QDBusConnection bus)
|
||||
{
|
||||
|
|
@ -33,14 +33,15 @@ static bool pingDaemon(QDBusConnection bus)
|
|||
return false;
|
||||
}
|
||||
|
||||
QDBusInterface iface(kGameCenterService, kGameCenterPath, kGameCenterInterface, bus);
|
||||
org::kde::GameCenter1 iface(kGameCenterService, kGameCenterPath, bus);
|
||||
if (!iface.isValid()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
iface.setTimeout(2000);
|
||||
const QDBusReply<QString> reply = iface.call(QStringLiteral("Ping"));
|
||||
return reply.isValid() && reply.value() == QLatin1String("ok");
|
||||
QDBusPendingReply<QString> reply = iface.Ping();
|
||||
reply.waitForFinished();
|
||||
return !reply.isError() && reply.value() == QLatin1String("ok");
|
||||
}
|
||||
|
||||
static void disconnectDaemonSignals(QDBusConnection bus, GameLauncher *launcher)
|
||||
|
|
@ -131,14 +132,15 @@ static bool tryResolveWithRunnerManager(const QVariantMap &spec, QVariantMap &ou
|
|||
bus.interface()->startService(kRunnerService);
|
||||
}
|
||||
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, bus);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, bus);
|
||||
if (!iface.isValid()) {
|
||||
if (bus.interface()->startService(kRunnerService).isValid()) {
|
||||
QDBusInterface retryIface(kRunnerService, kRunnerPath, kRunnerInterface, bus);
|
||||
org::kde::ALaKarte::Runner1 retryIface(kRunnerService, kRunnerPath, bus);
|
||||
if (retryIface.isValid()) {
|
||||
retryIface.setTimeout(2000);
|
||||
const QDBusReply<QVariantMap> retryReply = retryIface.call(QStringLiteral("ResolveLaunch"), spec);
|
||||
if (retryReply.isValid()) {
|
||||
QDBusPendingReply<QVariantMap> retryReply = retryIface.ResolveLaunch(spec);
|
||||
retryReply.waitForFinished();
|
||||
if (!retryReply.isError()) {
|
||||
out = unwrapVariantMap(retryReply.value());
|
||||
return true;
|
||||
}
|
||||
|
|
@ -148,17 +150,19 @@ static bool tryResolveWithRunnerManager(const QVariantMap &spec, QVariantMap &ou
|
|||
}
|
||||
|
||||
iface.setTimeout(2000);
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ResolveLaunch"), spec);
|
||||
if (!reply.isValid()) {
|
||||
QDBusPendingReply<QVariantMap> reply = iface.ResolveLaunch(spec);
|
||||
reply.waitForFinished();
|
||||
if (reply.isError()) {
|
||||
if (reply.error().type() == QDBusError::ServiceUnknown) {
|
||||
bus.interface()->startService(kRunnerService);
|
||||
QDBusInterface retryIface(kRunnerService, kRunnerPath, kRunnerInterface, bus);
|
||||
org::kde::ALaKarte::Runner1 retryIface(kRunnerService, kRunnerPath, bus);
|
||||
if (!retryIface.isValid()) {
|
||||
return false;
|
||||
}
|
||||
retryIface.setTimeout(2000);
|
||||
const QDBusReply<QVariantMap> retryReply = retryIface.call(QStringLiteral("ResolveLaunch"), spec);
|
||||
if (!retryReply.isValid()) {
|
||||
QDBusPendingReply<QVariantMap> retryReply = retryIface.ResolveLaunch(spec);
|
||||
retryReply.waitForFinished();
|
||||
if (retryReply.isError()) {
|
||||
return false;
|
||||
}
|
||||
out = unwrapVariantMap(retryReply.value());
|
||||
|
|
@ -270,7 +274,6 @@ static QStringList steamCandidateRoots()
|
|||
GameLauncher::GameLauncher(QObject *parent)
|
||||
: QObject(parent)
|
||||
, m_daemonAvailable(false)
|
||||
, m_usingSystemBus(false)
|
||||
{
|
||||
if (auto *app = qobject_cast<App *>(parent)) {
|
||||
connect(app->gameModel(), &GameModel::countChanged, this, [this]() {
|
||||
|
|
@ -557,10 +560,7 @@ void GameLauncher::launchGame(Game *game)
|
|||
|
||||
// Always try daemon first — for all launch types
|
||||
{
|
||||
QDBusInterface iface(kGameCenterService,
|
||||
kGameCenterPath,
|
||||
kGameCenterInterface,
|
||||
m_usingSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus());
|
||||
org::kde::GameCenter1 iface(kGameCenterService, kGameCenterPath, QDBusConnection::sessionBus());
|
||||
QVariantMap launchSpec = {
|
||||
{QStringLiteral("command"), launchCommand},
|
||||
{QStringLiteral("gameId"), game->id()},
|
||||
|
|
@ -599,8 +599,10 @@ void GameLauncher::launchGame(Game *game)
|
|||
launchSpec.insert(QStringLiteral("workingDirectory"), workingDirectory);
|
||||
}
|
||||
|
||||
const QDBusReply<QString> reply = iface.call(QStringLiteral("Launch"), launchSpec);
|
||||
if (reply.isValid() && !reply.value().isEmpty()) {
|
||||
iface.setTimeout(5000);
|
||||
QDBusPendingReply<QString> reply = iface.Launch(launchSpec);
|
||||
reply.waitForFinished();
|
||||
if (!reply.isError() && !reply.value().isEmpty()) {
|
||||
m_daemonGameToSession.insert(game->id(), reply.value());
|
||||
m_daemonSessionToGame.insert(reply.value(), game->id());
|
||||
game->setRunning(true);
|
||||
|
|
@ -614,7 +616,7 @@ void GameLauncher::launchGame(Game *game)
|
|||
return;
|
||||
}
|
||||
|
||||
const QString launchError = reply.isValid() ? QString() : reply.error().message();
|
||||
const QString launchError = reply.isError() ? reply.error().message() : QString();
|
||||
|
||||
// No fallback for non-URL commands — emit error
|
||||
if (!launchError.isEmpty()) {
|
||||
|
|
@ -631,11 +633,8 @@ void GameLauncher::stopGame(Game *game)
|
|||
return;
|
||||
}
|
||||
|
||||
QDBusInterface iface(kGameCenterService,
|
||||
kGameCenterPath,
|
||||
kGameCenterInterface,
|
||||
m_usingSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus());
|
||||
iface.call(QStringLiteral("StopByGameId"), game->id());
|
||||
org::kde::GameCenter1 iface(kGameCenterService, kGameCenterPath, QDBusConnection::sessionBus());
|
||||
iface.StopByGameId(game->id());
|
||||
}
|
||||
|
||||
bool GameLauncher::isGameRunning(Game *game) const
|
||||
|
|
@ -765,13 +764,12 @@ void GameLauncher::onDaemonLaunchFailed(const QVariantMap &error)
|
|||
|
||||
void GameLauncher::syncDaemonSessions()
|
||||
{
|
||||
QDBusInterface iface(kGameCenterService,
|
||||
kGameCenterPath,
|
||||
kGameCenterInterface,
|
||||
m_usingSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus());
|
||||
org::kde::GameCenter1 iface(kGameCenterService, kGameCenterPath, QDBusConnection::sessionBus());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QDBusReply<QVariantList> reply = iface.call(QStringLiteral("ListSessions"));
|
||||
if (!reply.isValid()) {
|
||||
QDBusPendingReply<QVariantList> reply = iface.ListSessions();
|
||||
reply.waitForFinished();
|
||||
if (reply.isError()) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -805,28 +803,14 @@ void GameLauncher::applyRunningStateToLibrary()
|
|||
|
||||
void GameLauncher::checkDaemonAvailability()
|
||||
{
|
||||
const bool systemAvailable = pingDaemon(QDBusConnection::systemBus());
|
||||
const bool sessionAvailable = systemAvailable ? false : pingDaemon(QDBusConnection::sessionBus());
|
||||
const bool available = systemAvailable || sessionAvailable;
|
||||
const bool useSystemBus = systemAvailable;
|
||||
const bool available = pingDaemon(QDBusConnection::sessionBus());
|
||||
|
||||
disconnectDaemonSignals(QDBusConnection::systemBus(), this);
|
||||
disconnectDaemonSignals(QDBusConnection::sessionBus(), this);
|
||||
|
||||
if (available) {
|
||||
connectDaemonSignals(useSystemBus ? QDBusConnection::systemBus() : QDBusConnection::sessionBus(), this);
|
||||
connectDaemonSignals(QDBusConnection::sessionBus(), this);
|
||||
}
|
||||
|
||||
const bool busChanged = m_usingSystemBus != useSystemBus;
|
||||
if (busChanged) {
|
||||
m_daemonGameToSession.clear();
|
||||
m_daemonSessionToGame.clear();
|
||||
Q_EMIT runningGamesChanged();
|
||||
applyRunningStateToLibrary();
|
||||
}
|
||||
|
||||
m_usingSystemBus = useSystemBus;
|
||||
|
||||
if (available != m_daemonAvailable) {
|
||||
m_daemonAvailable = available;
|
||||
Q_EMIT daemonAvailableChanged();
|
||||
|
|
|
|||
|
|
@ -48,7 +48,6 @@ private:
|
|||
QHash<QString, QString> m_daemonGameToSession;
|
||||
QHash<QString, QString> m_daemonSessionToGame;
|
||||
bool m_daemonAvailable = false;
|
||||
bool m_usingSystemBus = false;
|
||||
|
||||
void checkDaemonAvailability();
|
||||
void syncDaemonSessions();
|
||||
|
|
|
|||
|
|
@ -7,10 +7,35 @@ add_executable(alakarte_inputd
|
|||
inputdaemon.h
|
||||
)
|
||||
|
||||
set(alakarte_inputd_dbus_sources)
|
||||
|
||||
qt_add_dbus_adaptor(alakarte_inputd_dbus_sources
|
||||
dbus/org.kde.ALaKarte.Input1.xml
|
||||
inputdaemon.h
|
||||
InputDaemon
|
||||
input1adaptor
|
||||
Input1Adaptor
|
||||
)
|
||||
|
||||
qt_add_dbus_interface(alakarte_inputd_dbus_sources
|
||||
dbus/org.kde.ALaKarte.Input1.xml
|
||||
input1interface
|
||||
)
|
||||
|
||||
target_sources(alakarte_inputd PRIVATE
|
||||
${alakarte_inputd_dbus_sources}
|
||||
)
|
||||
|
||||
target_include_directories(alakarte_inputd PRIVATE
|
||||
${PROJECT_BINARY_DIR}/src
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(alakarte_inputd PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::DBus
|
||||
SDL3::SDL3
|
||||
KF6::CoreAddons
|
||||
)
|
||||
|
||||
set_target_properties(alakarte_inputd PROPERTIES
|
||||
|
|
|
|||
76
src/input/dbus/org.kde.ALaKarte.Input1.xml
Normal file
76
src/input/dbus/org.kde.ALaKarte.Input1.xml
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.ALaKarte.Input1">
|
||||
<method name="Ping">
|
||||
<arg name="out" type="s" direction="out"/>
|
||||
</method>
|
||||
<method name="Version">
|
||||
<arg name="major" type="u" direction="out"/>
|
||||
<arg name="minor" type="u" direction="out"/>
|
||||
</method>
|
||||
<method name="GetCapabilities">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="caps" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="ListControllers">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantList"/>
|
||||
<arg name="controllers" type="av" direction="out"/>
|
||||
</method>
|
||||
<method name="GetController">
|
||||
<arg name="controllerId" type="s" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="controller" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="Rescan">
|
||||
<arg name="ok" type="b" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="ListProfiles">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantList"/>
|
||||
<arg name="profiles" type="av" direction="out"/>
|
||||
</method>
|
||||
<method name="GetProfile">
|
||||
<arg name="profileId" type="s" direction="in"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="profile" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="CreateProfile">
|
||||
<arg name="name" type="s" direction="in"/>
|
||||
<arg name="profileId" type="s" direction="out"/>
|
||||
</method>
|
||||
<method name="DeleteProfile">
|
||||
<arg name="profileId" type="s" direction="in"/>
|
||||
<arg name="ok" type="b" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="SetActiveProfile">
|
||||
<arg name="controllerId" type="s" direction="in"/>
|
||||
<arg name="profileId" type="s" direction="in"/>
|
||||
<arg name="ok" type="b" direction="out"/>
|
||||
</method>
|
||||
<method name="GetActiveProfile">
|
||||
<arg name="controllerId" type="s" direction="in"/>
|
||||
<arg name="profileId" type="s" direction="out"/>
|
||||
</method>
|
||||
|
||||
<signal name="ControllerAdded">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="controller" type="a{sv}"/>
|
||||
</signal>
|
||||
<signal name="ControllerRemoved">
|
||||
<arg name="controllerId" type="s"/>
|
||||
</signal>
|
||||
<signal name="ControllerChanged">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="controller" type="a{sv}"/>
|
||||
</signal>
|
||||
|
||||
<signal name="ProfilesChanged"/>
|
||||
<signal name="ControllerProfileChanged">
|
||||
<arg name="controllerId" type="s"/>
|
||||
<arg name="profileId" type="s"/>
|
||||
</signal>
|
||||
</interface>
|
||||
</node>
|
||||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "inputdaemon.h"
|
||||
|
||||
#include "input1adaptor.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
|
|
@ -150,7 +152,9 @@ bool InputDaemon::init()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!bus.registerObject(kInputPath, this, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) {
|
||||
new Input1Adaptor(this);
|
||||
|
||||
if (!bus.registerObject(kInputPath, this, QDBusConnection::ExportAdaptors)) {
|
||||
qWarning() << "InputDaemon: failed to register object" << kInputPath << bus.lastError().message();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -199,10 +203,10 @@ QString InputDaemon::Ping() const
|
|||
return QStringLiteral("ok");
|
||||
}
|
||||
|
||||
void InputDaemon::Version(uint &major, uint &minor) const
|
||||
uint InputDaemon::Version(uint &minor) const
|
||||
{
|
||||
major = 1;
|
||||
minor = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariantMap InputDaemon::GetCapabilities() const
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public:
|
|||
|
||||
public Q_SLOTS:
|
||||
QString Ping() const;
|
||||
void Version(uint &major, uint &minor) const;
|
||||
uint Version(uint &minor) const;
|
||||
QVariantMap GetCapabilities() const;
|
||||
|
||||
QVariantList ListControllers() const;
|
||||
|
|
|
|||
|
|
@ -1,15 +1,35 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
|
||||
#include <KAboutData>
|
||||
|
||||
#include "alakarte-version.h"
|
||||
|
||||
#include "inputdaemon.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setOrganizationDomain(QStringLiteral("kde.org"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("alakarte"));
|
||||
|
||||
KAboutData aboutData(QStringLiteral("alakarte-inputd"),
|
||||
QStringLiteral("A-La-Karte Input Daemon"),
|
||||
QStringLiteral(ALAKARTE_VERSION_STRING),
|
||||
QStringLiteral("Gamepad input service for A-La-Karte"),
|
||||
KAboutLicense::GPL_V3,
|
||||
QStringLiteral("© 2026 A-La-Karte Contributors"));
|
||||
aboutData.addAuthor(QStringLiteral("A-La-Karte Contributors"), QStringLiteral("Developer"), QString());
|
||||
KAboutData::setApplicationData(aboutData);
|
||||
|
||||
QCommandLineParser parser;
|
||||
aboutData.setupCommandLine(&parser);
|
||||
parser.process(app);
|
||||
aboutData.processCommandLine(&parser);
|
||||
|
||||
InputDaemon daemon;
|
||||
if (!daemon.init()) {
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -3,11 +3,13 @@
|
|||
|
||||
#include "inputserviceclient.h"
|
||||
|
||||
#include "input1interface.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusError>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusPendingReply>
|
||||
#include <QDBusReply>
|
||||
#include <QDBusServiceWatcher>
|
||||
#include <QDBusVariant>
|
||||
|
|
@ -243,17 +245,21 @@ void InputServiceClient::refreshControllers()
|
|||
return;
|
||||
}
|
||||
|
||||
QDBusInterface i(kInputService, kInputPath, kInputInterface, QDBusConnection::sessionBus());
|
||||
i.setTimeout(2000);
|
||||
org::kde::ALaKarte::Input1 iface(kInputService, kInputPath, QDBusConnection::sessionBus());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QDBusReply<bool> rescanReply = i.call(QStringLiteral("Rescan"));
|
||||
if (!rescanReply.isValid()) {
|
||||
setLastError(rescanReply.error().message());
|
||||
{
|
||||
QDBusPendingReply<bool> rescanReply = iface.Rescan();
|
||||
rescanReply.waitForFinished();
|
||||
if (rescanReply.isError()) {
|
||||
setLastError(rescanReply.error().message());
|
||||
}
|
||||
}
|
||||
|
||||
const QDBusReply<QVariantList> reply = i.call(QStringLiteral("ListControllers"));
|
||||
QDBusPendingReply<QVariantList> reply = iface.ListControllers();
|
||||
reply.waitForFinished();
|
||||
|
||||
if (!reply.isValid()) {
|
||||
if (reply.isError()) {
|
||||
setLastError(reply.error().message());
|
||||
return;
|
||||
}
|
||||
|
|
@ -274,11 +280,13 @@ void InputServiceClient::refreshProfiles()
|
|||
return;
|
||||
}
|
||||
|
||||
QDBusInterface i(kInputService, kInputPath, kInputInterface, QDBusConnection::sessionBus());
|
||||
i.setTimeout(2000);
|
||||
const QDBusReply<QVariantList> reply = i.call(QStringLiteral("ListProfiles"));
|
||||
org::kde::ALaKarte::Input1 iface(kInputService, kInputPath, QDBusConnection::sessionBus());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
if (!reply.isValid()) {
|
||||
QDBusPendingReply<QVariantList> reply = iface.ListProfiles();
|
||||
reply.waitForFinished();
|
||||
|
||||
if (reply.isError()) {
|
||||
setLastError(reply.error().message());
|
||||
return;
|
||||
}
|
||||
|
|
@ -300,11 +308,13 @@ bool InputServiceClient::setActiveProfile(const QString &controllerId, const QSt
|
|||
return false;
|
||||
}
|
||||
|
||||
QDBusInterface i(kInputService, kInputPath, kInputInterface, QDBusConnection::sessionBus());
|
||||
i.setTimeout(2000);
|
||||
const QDBusReply<bool> reply = i.call(QStringLiteral("SetActiveProfile"), controllerId, profileId);
|
||||
org::kde::ALaKarte::Input1 iface(kInputService, kInputPath, QDBusConnection::sessionBus());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
if (!reply.isValid()) {
|
||||
QDBusPendingReply<bool> reply = iface.SetActiveProfile(controllerId, profileId);
|
||||
reply.waitForFinished();
|
||||
|
||||
if (reply.isError()) {
|
||||
setLastError(reply.error().message());
|
||||
return false;
|
||||
}
|
||||
|
|
@ -319,11 +329,13 @@ QString InputServiceClient::createProfile(const QString &name)
|
|||
return {};
|
||||
}
|
||||
|
||||
QDBusInterface i(kInputService, kInputPath, kInputInterface, QDBusConnection::sessionBus());
|
||||
i.setTimeout(2000);
|
||||
const QDBusReply<QString> reply = i.call(QStringLiteral("CreateProfile"), name);
|
||||
org::kde::ALaKarte::Input1 iface(kInputService, kInputPath, QDBusConnection::sessionBus());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
if (!reply.isValid()) {
|
||||
QDBusPendingReply<QString> reply = iface.CreateProfile(name);
|
||||
reply.waitForFinished();
|
||||
|
||||
if (reply.isError()) {
|
||||
setLastError(reply.error().message());
|
||||
return {};
|
||||
}
|
||||
|
|
@ -339,11 +351,13 @@ bool InputServiceClient::deleteProfile(const QString &profileId)
|
|||
return false;
|
||||
}
|
||||
|
||||
QDBusInterface i(kInputService, kInputPath, kInputInterface, QDBusConnection::sessionBus());
|
||||
i.setTimeout(2000);
|
||||
const QDBusReply<bool> reply = i.call(QStringLiteral("DeleteProfile"), profileId);
|
||||
org::kde::ALaKarte::Input1 iface(kInputService, kInputPath, QDBusConnection::sessionBus());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
if (!reply.isValid()) {
|
||||
QDBusPendingReply<bool> reply = iface.DeleteProfile(profileId);
|
||||
reply.waitForFinished();
|
||||
|
||||
if (reply.isError()) {
|
||||
setLastError(reply.error().message());
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
K_PLUGIN_CLASS_WITH_JSON(AlakarteRunner, "plasma-runner-alakarte.json")
|
||||
|
||||
static const QString kGameCenterService = QStringLiteral("org.kde.GameCenter1");
|
||||
static const QString kGameCenterPath = QStringLiteral("/org/kde/GameCenter1");
|
||||
static const QString kGameCenterPath = QStringLiteral("/org/kde/ALaKarte/GameCenter1");
|
||||
static const QString kGameCenterInterface = QStringLiteral("org.kde.GameCenter1");
|
||||
|
||||
static bool launchViaDaemon(QDBusConnection bus, const QVariantMap &launchSpec)
|
||||
|
|
@ -181,7 +181,7 @@ void AlakarteRunner::run(const KRunner::RunnerContext &context, const KRunner::Q
|
|||
{QStringLiteral("origin"), QStringLiteral("krunner")},
|
||||
};
|
||||
|
||||
if (launchViaDaemon(QDBusConnection::systemBus(), launchSpec) || launchViaDaemon(QDBusConnection::sessionBus(), launchSpec)) {
|
||||
if (launchViaDaemon(QDBusConnection::sessionBus(), launchSpec)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,6 +23,9 @@ int main(int argc, char *argv[])
|
|||
QGuiApplication app(argc, argv);
|
||||
QGuiApplication::setDesktopFileName(QStringLiteral("org.kde.alakarte"));
|
||||
|
||||
app.setOrganizationDomain(QStringLiteral("kde.org"));
|
||||
app.setApplicationName(QStringLiteral("alakarte"));
|
||||
|
||||
KLocalizedString::setApplicationDomain("alakarte");
|
||||
|
||||
KAboutData aboutData(QStringLiteral("alakarte"),
|
||||
|
|
|
|||
|
|
@ -7,10 +7,35 @@ add_executable(alakarte_runnerd
|
|||
runnermanagerdaemon.h
|
||||
)
|
||||
|
||||
set(alakarte_runnerd_dbus_sources)
|
||||
|
||||
qt_add_dbus_adaptor(alakarte_runnerd_dbus_sources
|
||||
dbus/org.kde.ALaKarte.Runner1.xml
|
||||
runnermanagerdaemon.h
|
||||
RunnerManagerDaemon
|
||||
runner1adaptor
|
||||
Runner1Adaptor
|
||||
)
|
||||
|
||||
qt_add_dbus_interface(alakarte_runnerd_dbus_sources
|
||||
dbus/org.kde.ALaKarte.Runner1.xml
|
||||
runner1interface
|
||||
)
|
||||
|
||||
target_sources(alakarte_runnerd PRIVATE
|
||||
${alakarte_runnerd_dbus_sources}
|
||||
)
|
||||
|
||||
target_include_directories(alakarte_runnerd PRIVATE
|
||||
${PROJECT_BINARY_DIR}/src
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(alakarte_runnerd PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::DBus
|
||||
Qt6::Network
|
||||
KF6::CoreAddons
|
||||
)
|
||||
|
||||
set_target_properties(alakarte_runnerd PROPERTIES
|
||||
|
|
|
|||
113
src/runner/dbus/org.kde.ALaKarte.Runner1.xml
Normal file
113
src/runner/dbus/org.kde.ALaKarte.Runner1.xml
Normal file
|
|
@ -0,0 +1,113 @@
|
|||
<!DOCTYPE node PUBLIC "-//freedesktop//DTD D-Bus Object Introspection 1.0//EN"
|
||||
"http://www.freedesktop.org/standards/dbus/1.0/introspect.dtd">
|
||||
<node>
|
||||
<interface name="org.kde.ALaKarte.Runner1">
|
||||
<method name="Ping">
|
||||
<arg name="out" type="s" direction="out"/>
|
||||
</method>
|
||||
<method name="Version">
|
||||
<arg name="major" type="u" direction="out"/>
|
||||
<arg name="minor" type="u" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="ListRunners">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantList"/>
|
||||
<arg name="runners" type="av" direction="out"/>
|
||||
</method>
|
||||
<method name="Rescan">
|
||||
<arg name="ok" type="b" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="RegisterRunner">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<arg name="runner" type="a{sv}" direction="in"/>
|
||||
<arg name="ok" type="b" direction="out"/>
|
||||
</method>
|
||||
<method name="UnregisterRunner">
|
||||
<arg name="runnerId" type="s" direction="in"/>
|
||||
<arg name="ok" type="b" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="InstallRunnerFromArchive">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="spec" type="a{sv}" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="InstallRunnerFromUrl">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="spec" type="a{sv}" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="UninstallRunner">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="runnerId" type="s" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="CancelInstall">
|
||||
<arg name="installId" type="s" direction="in"/>
|
||||
<arg name="ok" type="b" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="EnsurePrefix">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="spec" type="a{sv}" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="DeletePrefix">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="spec" type="a{sv}" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="ResolveLaunch">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="spec" type="a{sv}" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
|
||||
<method name="GetGameProfile">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="gameId" type="s" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="SetGameProfile">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="spec" type="a{sv}" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="ClearGameProfile">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantMap"/>
|
||||
<arg name="gameId" type="s" direction="in"/>
|
||||
<arg name="result" type="a{sv}" direction="out"/>
|
||||
</method>
|
||||
<method name="ListGameProfiles">
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out0" value="QVariantList"/>
|
||||
<arg name="profiles" type="av" direction="out"/>
|
||||
</method>
|
||||
|
||||
<signal name="InstallStarted">
|
||||
<arg name="installId" type="s"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
|
||||
<arg name="spec" type="a{sv}"/>
|
||||
</signal>
|
||||
<signal name="InstallProgress">
|
||||
<arg name="installId" type="s"/>
|
||||
<arg name="receivedBytes" type="x"/>
|
||||
<arg name="totalBytes" type="x"/>
|
||||
</signal>
|
||||
<signal name="InstallFinished">
|
||||
<arg name="installId" type="s"/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.Out1" value="QVariantMap"/>
|
||||
<arg name="result" type="a{sv}"/>
|
||||
</signal>
|
||||
|
||||
<signal name="GameProfilesChanged"/>
|
||||
</interface>
|
||||
</node>
|
||||
|
|
@ -1,16 +1,36 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusConnection>
|
||||
|
||||
#include <KAboutData>
|
||||
|
||||
#include "alakarte-version.h"
|
||||
|
||||
#include "runnermanagerdaemon.h"
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setOrganizationDomain(QStringLiteral("kde.org"));
|
||||
QCoreApplication::setApplicationName(QStringLiteral("alakarte"));
|
||||
|
||||
KAboutData aboutData(QStringLiteral("alakarte-runnerd"),
|
||||
QStringLiteral("A-La-Karte Runner Daemon"),
|
||||
QStringLiteral(ALAKARTE_VERSION_STRING),
|
||||
QStringLiteral("Runner management service for A-La-Karte"),
|
||||
KAboutLicense::GPL_V3,
|
||||
QStringLiteral("© 2026 A-La-Karte Contributors"));
|
||||
aboutData.addAuthor(QStringLiteral("A-La-Karte Contributors"), QStringLiteral("Developer"), QString());
|
||||
KAboutData::setApplicationData(aboutData);
|
||||
|
||||
QCommandLineParser parser;
|
||||
aboutData.setupCommandLine(&parser);
|
||||
parser.process(app);
|
||||
aboutData.processCommandLine(&parser);
|
||||
|
||||
RunnerManagerDaemon daemon;
|
||||
if (!daemon.init()) {
|
||||
return 1;
|
||||
|
|
|
|||
|
|
@ -3,6 +3,8 @@
|
|||
|
||||
#include "runnermanagerdaemon.h"
|
||||
|
||||
#include "runner1adaptor.h"
|
||||
|
||||
#include <QCryptographicHash>
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
|
|
@ -109,7 +111,9 @@ bool RunnerManagerDaemon::init()
|
|||
return false;
|
||||
}
|
||||
|
||||
if (!bus.registerObject(kRunnerPath, this, QDBusConnection::ExportAllSlots | QDBusConnection::ExportAllSignals)) {
|
||||
new Runner1Adaptor(this);
|
||||
|
||||
if (!bus.registerObject(kRunnerPath, this, QDBusConnection::ExportAdaptors)) {
|
||||
qWarning() << "RunnerManagerDaemon: failed to register object" << kRunnerPath << bus.lastError().message();
|
||||
return false;
|
||||
}
|
||||
|
|
@ -122,10 +126,10 @@ QString RunnerManagerDaemon::Ping() const
|
|||
return QStringLiteral("ok");
|
||||
}
|
||||
|
||||
void RunnerManagerDaemon::Version(uint &major, uint &minor) const
|
||||
uint RunnerManagerDaemon::Version(uint &minor) const
|
||||
{
|
||||
major = 1;
|
||||
minor = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
QVariantMap RunnerManagerDaemon::RunnerInfo::toVariantMap() const
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ public:
|
|||
|
||||
public Q_SLOTS:
|
||||
QString Ping() const;
|
||||
void Version(uint &major, uint &minor) const;
|
||||
uint Version(uint &minor) const;
|
||||
|
||||
QVariantList ListRunners();
|
||||
bool Rescan();
|
||||
|
|
|
|||
|
|
@ -3,12 +3,13 @@
|
|||
|
||||
#include "runnermanagerclient.h"
|
||||
|
||||
#include "runner1interface.h"
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QCryptographicHash>
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusInterface>
|
||||
#include <QDBusMetaType>
|
||||
#include <QDBusPendingCall>
|
||||
#include <QDBusPendingCallWatcher>
|
||||
|
|
@ -338,8 +339,8 @@ void RunnerManagerClient::installRunnerFromUrl(const QString &url, const QString
|
|||
auto attempts = QSharedPointer<int>::create(0);
|
||||
auto callPtr = QSharedPointer<std::function<void()>>::create();
|
||||
*callPtr = [this, spec, attempts, callPtr]() {
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("InstallRunnerFromUrl"), spec);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.InstallRunnerFromUrl(spec);
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, spec, attempts, callPtr](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantMap> reply = *w;
|
||||
|
|
@ -397,8 +398,8 @@ void RunnerManagerClient::deletePrefix(const QString &gameId, const QString &pre
|
|||
auto attempts = QSharedPointer<int>::create(0);
|
||||
auto callPtr = QSharedPointer<std::function<void()>>::create();
|
||||
*callPtr = [this, spec, id, attempts, callPtr]() {
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("DeletePrefix"), spec);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.DeletePrefix(spec);
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, id, attempts, callPtr](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantMap> reply = *w;
|
||||
|
|
@ -438,8 +439,8 @@ void RunnerManagerClient::cancelCurrentInstall()
|
|||
|
||||
setStatus(QStringLiteral("Cancelling..."));
|
||||
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
iface.asyncCall(QStringLiteral("CancelInstall"), m_installId);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
iface.CancelInstall(m_installId);
|
||||
}
|
||||
|
||||
void RunnerManagerClient::onInstallStarted(const QString &installId, const QVariantMap &)
|
||||
|
|
@ -489,8 +490,8 @@ void RunnerManagerClient::refreshRunners()
|
|||
{
|
||||
ensureRunnerDaemon();
|
||||
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("ListRunners"));
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.ListRunners();
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantList> reply = *w;
|
||||
|
|
@ -544,8 +545,8 @@ void RunnerManagerClient::refreshGameProfiles()
|
|||
{
|
||||
ensureRunnerDaemon();
|
||||
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("ListGameProfiles"));
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.ListGameProfiles();
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantList> reply = *w;
|
||||
|
|
@ -599,8 +600,8 @@ void RunnerManagerClient::requestGameProfile(const QString &gameId)
|
|||
auto attempts = QSharedPointer<int>::create(0);
|
||||
auto callPtr = QSharedPointer<std::function<void()>>::create();
|
||||
*callPtr = [this, id, attempts, callPtr]() {
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("GetGameProfile"), id);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.GetGameProfile(id);
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, id, attempts, callPtr](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantMap> reply = *w;
|
||||
|
|
@ -641,8 +642,8 @@ void RunnerManagerClient::setGameProfile(const QVariantMap &spec)
|
|||
auto attempts = QSharedPointer<int>::create(0);
|
||||
auto callPtr = QSharedPointer<std::function<void()>>::create();
|
||||
*callPtr = [this, spec, id, attempts, callPtr]() {
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("SetGameProfile"), spec);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.SetGameProfile(spec);
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, id, attempts, callPtr](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantMap> reply = *w;
|
||||
|
|
@ -683,8 +684,8 @@ void RunnerManagerClient::clearGameProfile(const QString &gameId)
|
|||
auto attempts = QSharedPointer<int>::create(0);
|
||||
auto callPtr = QSharedPointer<std::function<void()>>::create();
|
||||
*callPtr = [this, id, attempts, callPtr]() {
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("ClearGameProfile"), id);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.ClearGameProfile(id);
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, id, attempts, callPtr](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantMap> reply = *w;
|
||||
|
|
@ -720,8 +721,8 @@ void RunnerManagerClient::uninstallRunner(const QString &runnerId)
|
|||
return;
|
||||
}
|
||||
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("UninstallRunner"), id);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.UninstallRunner(id);
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantMap> reply = *w;
|
||||
|
|
@ -763,8 +764,8 @@ void RunnerManagerClient::ensurePrefix(const QString &gameId, const QString &run
|
|||
auto attempts = QSharedPointer<int>::create(0);
|
||||
auto callPtr = QSharedPointer<std::function<void()>>::create();
|
||||
*callPtr = [this, spec, id, attempts, callPtr]() {
|
||||
QDBusInterface iface(kRunnerService, kRunnerPath, kRunnerInterface, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.asyncCall(QStringLiteral("EnsurePrefix"), spec);
|
||||
org::kde::ALaKarte::Runner1 iface(kRunnerService, kRunnerPath, QDBusConnection::sessionBus());
|
||||
QDBusPendingCall call = iface.EnsurePrefix(spec);
|
||||
auto *watcher = new QDBusPendingCallWatcher(call, this);
|
||||
connect(watcher, &QDBusPendingCallWatcher::finished, this, [this, id, attempts, callPtr](QDBusPendingCallWatcher *w) {
|
||||
QDBusPendingReply<QVariantMap> reply = *w;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,31 @@ add_executable(alakarte_dbus_smoketest
|
|||
dbus_smoketest.cpp
|
||||
)
|
||||
|
||||
set(alakarte_dbus_smoketest_dbus_sources)
|
||||
|
||||
qt_add_dbus_interface(alakarte_dbus_smoketest_dbus_sources
|
||||
${CMAKE_SOURCE_DIR}/src/gamecenter/dbus/org.kde.GameCenter1.xml
|
||||
gamecenter1interface
|
||||
)
|
||||
|
||||
qt_add_dbus_interface(alakarte_dbus_smoketest_dbus_sources
|
||||
${CMAKE_SOURCE_DIR}/src/runner/dbus/org.kde.ALaKarte.Runner1.xml
|
||||
runner1interface
|
||||
)
|
||||
|
||||
qt_add_dbus_interface(alakarte_dbus_smoketest_dbus_sources
|
||||
${CMAKE_SOURCE_DIR}/src/input/dbus/org.kde.ALaKarte.Input1.xml
|
||||
input1interface
|
||||
)
|
||||
|
||||
target_sources(alakarte_dbus_smoketest PRIVATE
|
||||
${alakarte_dbus_smoketest_dbus_sources}
|
||||
)
|
||||
|
||||
target_include_directories(alakarte_dbus_smoketest PRIVATE
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
)
|
||||
|
||||
target_link_libraries(alakarte_dbus_smoketest PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::DBus
|
||||
|
|
@ -43,3 +68,27 @@ add_test(NAME alakarte_stop_launching_regression_test
|
|||
set_tests_properties(alakarte_stop_launching_regression_test PROPERTIES
|
||||
TIMEOUT 60
|
||||
)
|
||||
|
||||
add_executable(alakarte_processscanner_test
|
||||
processscanner_test.cpp
|
||||
../src/gamecenter/processscanner.cpp
|
||||
../src/gamecenter/processscanner.h
|
||||
)
|
||||
|
||||
target_include_directories(alakarte_processscanner_test PRIVATE
|
||||
${CMAKE_SOURCE_DIR}/src/gamecenter
|
||||
)
|
||||
|
||||
target_link_libraries(alakarte_processscanner_test PRIVATE
|
||||
Qt6::Core
|
||||
Qt6::Concurrent
|
||||
Qt6::Test
|
||||
)
|
||||
|
||||
add_test(NAME alakarte_processscanner_test
|
||||
COMMAND alakarte_processscanner_test
|
||||
)
|
||||
|
||||
set_tests_properties(alakarte_processscanner_test PROPERTIES
|
||||
TIMEOUT 20
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,11 +12,17 @@
|
|||
#include <QElapsedTimer>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QSet>
|
||||
#include <QSignalSpy>
|
||||
#include <QStandardPaths>
|
||||
#include <QStringList>
|
||||
#include <QUuid>
|
||||
#include <QtTest>
|
||||
|
||||
#include "gamecenter1interface.h"
|
||||
#include "input1interface.h"
|
||||
#include "runner1interface.h"
|
||||
|
||||
namespace
|
||||
{
|
||||
static QString takeArgValue(const QStringList &args, const QString &key)
|
||||
|
|
@ -79,8 +85,17 @@ private Q_SLOTS:
|
|||
void cleanupTestCase();
|
||||
|
||||
void pingGameCenter();
|
||||
void gameCenterUniqueness();
|
||||
void gameCenterPolicyAndCapabilities();
|
||||
void gameCenterLaunchFailureContract();
|
||||
void pingRunner();
|
||||
void runnerVersionAndListRunners();
|
||||
void runnerResolveLaunchMissingProgram();
|
||||
void runnerResolveLaunchUnknownRunnerId();
|
||||
void runnerResolveLaunchWineMissingPrefixOrGameId();
|
||||
void pingInput();
|
||||
void inputVersionAndCapabilities();
|
||||
void inputProfilesCrud();
|
||||
void runnerResolveLaunchNative();
|
||||
void runnerGameProfiles();
|
||||
|
||||
|
|
@ -234,49 +249,298 @@ void DbusSmokeTest::cleanupTestCase()
|
|||
|
||||
void DbusSmokeTest::pingGameCenter()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), m_bus);
|
||||
org::kde::GameCenter1 iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QDBusReply<QString> reply = iface.call(QStringLiteral("Ping"));
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QString> reply = iface.Ping();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QCOMPARE(reply.value(), QStringLiteral("ok"));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::gameCenterUniqueness()
|
||||
{
|
||||
pingGameCenter();
|
||||
|
||||
QVERIFY(m_bus.interface());
|
||||
const QDBusReply<QString> ownerBeforeReply = m_bus.interface()->serviceOwner(QStringLiteral("org.kde.GameCenter1"));
|
||||
QVERIFY(ownerBeforeReply.isValid());
|
||||
const QString ownerBefore = ownerBeforeReply.value();
|
||||
QVERIFY(!ownerBefore.isEmpty());
|
||||
|
||||
QProcess second;
|
||||
startDaemon(second, m_gamecenterPath);
|
||||
QVERIFY(second.waitForStarted(5000));
|
||||
|
||||
const bool finished = second.waitForFinished(5000);
|
||||
if (!finished) {
|
||||
stopProcess(second);
|
||||
}
|
||||
QVERIFY(finished);
|
||||
QCOMPARE(second.exitStatus(), QProcess::NormalExit);
|
||||
|
||||
const QDBusReply<QString> ownerAfterReply = m_bus.interface()->serviceOwner(QStringLiteral("org.kde.GameCenter1"));
|
||||
QVERIFY(ownerAfterReply.isValid());
|
||||
QCOMPARE(ownerAfterReply.value(), ownerBefore);
|
||||
|
||||
pingGameCenter();
|
||||
}
|
||||
|
||||
void DbusSmokeTest::gameCenterPolicyAndCapabilities()
|
||||
{
|
||||
org::kde::GameCenter1 iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
{
|
||||
QDBusPendingReply<uint, uint> reply = iface.Version();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QCOMPARE(reply.argumentAt<0>(), 1u);
|
||||
QCOMPARE(reply.argumentAt<1>(), 0u);
|
||||
}
|
||||
|
||||
{
|
||||
QDBusPendingReply<QVariantMap> reply = iface.GetCapabilities();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
const QVariantMap caps = reply.value();
|
||||
QVERIFY(caps.contains(QStringLiteral("supportsSystemd")));
|
||||
QVERIFY(caps.value(QStringLiteral("supportsSystemd")).canConvert<bool>());
|
||||
QVERIFY(caps.contains(QStringLiteral("supportsSystemBus")));
|
||||
QCOMPARE(caps.value(QStringLiteral("supportsSystemBus")).toBool(), false);
|
||||
QVERIFY(caps.contains(QStringLiteral("supportsPowerProfiles")));
|
||||
QVERIFY(caps.value(QStringLiteral("supportsPowerProfiles")).canConvert<bool>());
|
||||
}
|
||||
|
||||
QVariantMap originalPolicy;
|
||||
{
|
||||
QDBusPendingReply<QVariantMap> reply = iface.GetPolicy();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
originalPolicy = reply.value();
|
||||
}
|
||||
|
||||
{
|
||||
QVariantMap policy;
|
||||
policy.insert(QStringLiteral("maxConcurrent"), 0);
|
||||
QDBusPendingReply<> reply = iface.SetPolicy(policy);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
}
|
||||
|
||||
{
|
||||
QDBusPendingReply<QVariantMap> reply = iface.GetPolicy();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QCOMPARE(reply.value().value(QStringLiteral("maxConcurrent")).toInt(), 0);
|
||||
}
|
||||
|
||||
{
|
||||
const int originalMaxConcurrent = originalPolicy.value(QStringLiteral("maxConcurrent"), 0).toInt();
|
||||
QVariantMap policy;
|
||||
policy.insert(QStringLiteral("maxConcurrent"), originalMaxConcurrent);
|
||||
QDBusPendingReply<> reply = iface.SetPolicy(policy);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
}
|
||||
|
||||
{
|
||||
QDBusPendingReply<QVariantList> reply = iface.ListSessions();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
}
|
||||
|
||||
{
|
||||
QDBusPendingReply<> reply = iface.StopByGameId(QStringLiteral("alakarte-test-nonexistent"));
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
}
|
||||
}
|
||||
|
||||
void DbusSmokeTest::gameCenterLaunchFailureContract()
|
||||
{
|
||||
org::kde::GameCenter1 iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
QSignalSpy launchFailedSpy(&iface, &org::kde::GameCenter1::LaunchFailed);
|
||||
QVERIFY(launchFailedSpy.isValid());
|
||||
|
||||
QVariantMap launchSpec;
|
||||
launchSpec.insert(QStringLiteral("gameId"), QStringLiteral("alakarte-test-game"));
|
||||
launchSpec.insert(QStringLiteral("program"), QStringLiteral("/bin/true"));
|
||||
launchSpec.insert(QStringLiteral("args"), QStringList{});
|
||||
|
||||
QDBusPendingReply<QString> reply = iface.Launch(launchSpec);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QVERIFY(reply.value().isEmpty());
|
||||
|
||||
QTRY_COMPARE_WITH_TIMEOUT(launchFailedSpy.count(), 1, 2000);
|
||||
const QList<QVariant> args = launchFailedSpy.takeFirst();
|
||||
QCOMPARE(args.size(), 1);
|
||||
const QVariantMap error = unwrapVariantMap(args.first());
|
||||
QVERIFY(error.contains(QStringLiteral("error")));
|
||||
QVERIFY(error.value(QStringLiteral("error")).toString().contains(QStringLiteral("systemd"), Qt::CaseInsensitive));
|
||||
QCOMPARE(error.value(QStringLiteral("gameId")).toString(), QStringLiteral("alakarte-test-game"));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::pingRunner()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
QStringLiteral("/org/kde/ALaKarte/Runner1"),
|
||||
QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
m_bus);
|
||||
org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QDBusReply<QString> reply = iface.call(QStringLiteral("Ping"));
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QString> reply = iface.Ping();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QCOMPARE(reply.value(), QStringLiteral("ok"));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::runnerVersionAndListRunners()
|
||||
{
|
||||
org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(5000);
|
||||
|
||||
{
|
||||
QDBusPendingReply<uint, uint> reply = iface.Version();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QCOMPARE(reply.argumentAt<0>(), 1u);
|
||||
QCOMPARE(reply.argumentAt<1>(), 0u);
|
||||
}
|
||||
|
||||
QDBusPendingReply<QVariantList> reply = iface.ListRunners();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
|
||||
const QVariantList list = reply.value();
|
||||
QSet<QString> ids;
|
||||
for (const QVariant &v : list) {
|
||||
const QVariantMap m = unwrapVariantMap(v);
|
||||
const QString id = m.value(QStringLiteral("id")).toString();
|
||||
const QString type = m.value(QStringLiteral("type")).toString();
|
||||
const QString path = m.value(QStringLiteral("path")).toString();
|
||||
|
||||
QVERIFY2(!id.isEmpty(), "runner entry missing id");
|
||||
QVERIFY2(!type.isEmpty(), qPrintable(QStringLiteral("runner '%1' missing type").arg(id)));
|
||||
QVERIFY2(!path.isEmpty(), qPrintable(QStringLiteral("runner '%1' missing path").arg(id)));
|
||||
QVERIFY2(!ids.contains(id), qPrintable(QStringLiteral("duplicate runner id '%1'").arg(id)));
|
||||
ids.insert(id);
|
||||
}
|
||||
}
|
||||
|
||||
void DbusSmokeTest::pingInput()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.ALaKarte.Input1"),
|
||||
QStringLiteral("/org/kde/ALaKarte/Input1"),
|
||||
QStringLiteral("org.kde.ALaKarte.Input1"),
|
||||
m_bus);
|
||||
org::kde::ALaKarte::Input1 iface(QStringLiteral("org.kde.ALaKarte.Input1"), QStringLiteral("/org/kde/ALaKarte/Input1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QDBusReply<QString> reply = iface.call(QStringLiteral("Ping"));
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QString> reply = iface.Ping();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QCOMPARE(reply.value(), QStringLiteral("ok"));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::inputVersionAndCapabilities()
|
||||
{
|
||||
org::kde::ALaKarte::Input1 iface(QStringLiteral("org.kde.ALaKarte.Input1"), QStringLiteral("/org/kde/ALaKarte/Input1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
{
|
||||
QDBusPendingReply<uint, uint> reply = iface.Version();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QCOMPARE(reply.argumentAt<0>(), 1u);
|
||||
QCOMPARE(reply.argumentAt<1>(), 0u);
|
||||
}
|
||||
|
||||
{
|
||||
QDBusPendingReply<QVariantMap> reply = iface.GetCapabilities();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
|
||||
const QVariantMap caps = reply.value();
|
||||
QVERIFY(caps.contains(QStringLiteral("supportsBattery")));
|
||||
QVERIFY(caps.value(QStringLiteral("supportsBattery")).canConvert<bool>());
|
||||
QCOMPARE(caps.value(QStringLiteral("supportsBattery")).toBool(), true);
|
||||
|
||||
QVERIFY(caps.contains(QStringLiteral("supportsHotplug")));
|
||||
QVERIFY(caps.value(QStringLiteral("supportsHotplug")).canConvert<bool>());
|
||||
QCOMPARE(caps.value(QStringLiteral("supportsHotplug")).toBool(), true);
|
||||
|
||||
QVERIFY(caps.contains(QStringLiteral("supportsProfiles")));
|
||||
QVERIFY(caps.value(QStringLiteral("supportsProfiles")).canConvert<bool>());
|
||||
QCOMPARE(caps.value(QStringLiteral("supportsProfiles")).toBool(), true);
|
||||
}
|
||||
}
|
||||
|
||||
void DbusSmokeTest::inputProfilesCrud()
|
||||
{
|
||||
org::kde::ALaKarte::Input1 iface(QStringLiteral("org.kde.ALaKarte.Input1"), QStringLiteral("/org/kde/ALaKarte/Input1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
QDBusPendingReply<QString> createReply = iface.CreateProfile(QStringLiteral("Test Profile"));
|
||||
createReply.waitForFinished();
|
||||
QVERIFY2(!createReply.isError(), qPrintable(createReply.error().message()));
|
||||
const QString profileId = createReply.value();
|
||||
QVERIFY(!profileId.isEmpty());
|
||||
|
||||
{
|
||||
QDBusPendingReply<QVariantList> listReply = iface.ListProfiles();
|
||||
listReply.waitForFinished();
|
||||
QVERIFY2(!listReply.isError(), qPrintable(listReply.error().message()));
|
||||
const QVariantList list = listReply.value();
|
||||
|
||||
bool found = false;
|
||||
for (const QVariant &v : list) {
|
||||
const QVariantMap m = unwrapVariantMap(v);
|
||||
if (m.value(QStringLiteral("id")).toString() == profileId) {
|
||||
found = true;
|
||||
QCOMPARE(m.value(QStringLiteral("name")).toString(), QStringLiteral("Test Profile"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
QVERIFY(found);
|
||||
}
|
||||
|
||||
{
|
||||
QDBusPendingReply<QVariantMap> getReply = iface.GetProfile(profileId);
|
||||
getReply.waitForFinished();
|
||||
QVERIFY2(!getReply.isError(), qPrintable(getReply.error().message()));
|
||||
const QVariantMap m = getReply.value();
|
||||
QCOMPARE(m.value(QStringLiteral("id")).toString(), profileId);
|
||||
QCOMPARE(m.value(QStringLiteral("name")).toString(), QStringLiteral("Test Profile"));
|
||||
}
|
||||
|
||||
{
|
||||
QDBusPendingReply<bool> delReply = iface.DeleteProfile(profileId);
|
||||
delReply.waitForFinished();
|
||||
QVERIFY2(!delReply.isError(), qPrintable(delReply.error().message()));
|
||||
QVERIFY(delReply.value());
|
||||
}
|
||||
|
||||
{
|
||||
QDBusPendingReply<QVariantList> listReply = iface.ListProfiles();
|
||||
listReply.waitForFinished();
|
||||
QVERIFY2(!listReply.isError(), qPrintable(listReply.error().message()));
|
||||
const QVariantList list = listReply.value();
|
||||
|
||||
for (const QVariant &v : list) {
|
||||
const QVariantMap m = unwrapVariantMap(v);
|
||||
QVERIFY(m.value(QStringLiteral("id")).toString() != profileId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DbusSmokeTest::runnerResolveLaunchNative()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
QStringLiteral("/org/kde/ALaKarte/Runner1"),
|
||||
QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
m_bus);
|
||||
org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
|
|
@ -284,8 +548,9 @@ void DbusSmokeTest::runnerResolveLaunchNative()
|
|||
spec.insert(QStringLiteral("program"), QStringLiteral("/bin/true"));
|
||||
spec.insert(QStringLiteral("args"), QStringList{});
|
||||
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ResolveLaunch"), spec);
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QVariantMap> reply = iface.ResolveLaunch(spec);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
|
||||
const QVariantMap out = reply.value();
|
||||
QVERIFY(out.value(QStringLiteral("ok")).toBool());
|
||||
|
|
@ -293,20 +558,79 @@ void DbusSmokeTest::runnerResolveLaunchNative()
|
|||
QCOMPARE(out.value(QStringLiteral("finalArgs")).toStringList(), QStringList{});
|
||||
}
|
||||
|
||||
void DbusSmokeTest::runnerResolveLaunchMissingProgram()
|
||||
{
|
||||
org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("args"), QStringList{});
|
||||
|
||||
QDBusPendingReply<QVariantMap> reply = iface.ResolveLaunch(spec);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
|
||||
const QVariantMap out = reply.value();
|
||||
QCOMPARE(out.value(QStringLiteral("ok")).toBool(), false);
|
||||
QVERIFY(out.contains(QStringLiteral("error")));
|
||||
QVERIFY(out.value(QStringLiteral("error")).toString().contains(QStringLiteral("missing program"), Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::runnerResolveLaunchUnknownRunnerId()
|
||||
{
|
||||
org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("runnerId"), QStringLiteral("alakarte-test-nonexistent"));
|
||||
spec.insert(QStringLiteral("program"), QStringLiteral("/bin/true"));
|
||||
spec.insert(QStringLiteral("args"), QStringList{});
|
||||
|
||||
QDBusPendingReply<QVariantMap> reply = iface.ResolveLaunch(spec);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
|
||||
const QVariantMap out = reply.value();
|
||||
QCOMPARE(out.value(QStringLiteral("ok")).toBool(), false);
|
||||
QVERIFY(out.contains(QStringLiteral("error")));
|
||||
QVERIFY(out.value(QStringLiteral("error")).toString().contains(QStringLiteral("unknown runnerId"), Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::runnerResolveLaunchWineMissingPrefixOrGameId()
|
||||
{
|
||||
org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("runner"), QStringLiteral("wine"));
|
||||
spec.insert(QStringLiteral("program"), QStringLiteral("/bin/true"));
|
||||
spec.insert(QStringLiteral("args"), QStringList{});
|
||||
|
||||
QDBusPendingReply<QVariantMap> reply = iface.ResolveLaunch(spec);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
|
||||
const QVariantMap out = reply.value();
|
||||
QCOMPARE(out.value(QStringLiteral("ok")).toBool(), false);
|
||||
QVERIFY(out.contains(QStringLiteral("error")));
|
||||
QVERIFY(out.value(QStringLiteral("error")).toString().contains(QStringLiteral("missing prefixPath or gameId"), Qt::CaseInsensitive));
|
||||
}
|
||||
|
||||
void DbusSmokeTest::runnerGameProfiles()
|
||||
{
|
||||
QDBusInterface iface(QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
QStringLiteral("/org/kde/ALaKarte/Runner1"),
|
||||
QStringLiteral("org.kde.ALaKarte.Runner1"),
|
||||
m_bus);
|
||||
org::kde::ALaKarte::Runner1 iface(QStringLiteral("org.kde.ALaKarte.Runner1"), QStringLiteral("/org/kde/ALaKarte/Runner1"), m_bus);
|
||||
QVERIFY(iface.isValid());
|
||||
iface.setTimeout(2000);
|
||||
|
||||
const QString gameId = QStringLiteral("alakarte-test-game");
|
||||
|
||||
{
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ClearGameProfile"), gameId);
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QVariantMap> reply = iface.ClearGameProfile(gameId);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QVERIFY(reply.value().value(QStringLiteral("ok")).toBool());
|
||||
}
|
||||
|
||||
|
|
@ -320,8 +644,9 @@ void DbusSmokeTest::runnerGameProfiles()
|
|||
spec.insert(QStringLiteral("dllOverrides"),
|
||||
QVariantMap{{QStringLiteral("d3d11"), QStringLiteral("native,builtin")}, {QStringLiteral("dxgi"), QStringLiteral("native,builtin")}});
|
||||
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("SetGameProfile"), spec);
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QVariantMap> reply = iface.SetGameProfile(spec);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
const QVariantMap out = reply.value();
|
||||
QVERIFY(out.value(QStringLiteral("ok")).toBool());
|
||||
|
||||
|
|
@ -338,8 +663,9 @@ void DbusSmokeTest::runnerGameProfiles()
|
|||
}
|
||||
|
||||
{
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("GetGameProfile"), gameId);
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QVariantMap> reply = iface.GetGameProfile(gameId);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
const QVariantMap out = reply.value();
|
||||
QVERIFY(out.value(QStringLiteral("ok")).toBool());
|
||||
const QVariantMap profile = unwrapVariantMap(out.value(QStringLiteral("profile")));
|
||||
|
|
@ -360,8 +686,9 @@ void DbusSmokeTest::runnerGameProfiles()
|
|||
spec.insert(QStringLiteral("args"), QStringList{QStringLiteral("--base-arg")});
|
||||
spec.insert(QStringLiteral("extraArgs"), QStringList{QStringLiteral("--spec-arg")});
|
||||
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ResolveLaunch"), spec);
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QVariantMap> reply = iface.ResolveLaunch(spec);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
const QVariantMap out = reply.value();
|
||||
QVERIFY(out.value(QStringLiteral("ok")).toBool());
|
||||
QCOMPARE(out.value(QStringLiteral("finalProgram")).toString(), QStringLiteral("/bin/true"));
|
||||
|
|
@ -379,8 +706,9 @@ void DbusSmokeTest::runnerGameProfiles()
|
|||
}
|
||||
|
||||
{
|
||||
const QDBusReply<QVariantList> reply = iface.call(QStringLiteral("ListGameProfiles"));
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QVariantList> reply = iface.ListGameProfiles();
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
const QVariantList list = reply.value();
|
||||
bool found = false;
|
||||
for (const QVariant &v : list) {
|
||||
|
|
@ -394,8 +722,9 @@ void DbusSmokeTest::runnerGameProfiles()
|
|||
}
|
||||
|
||||
{
|
||||
const QDBusReply<QVariantMap> reply = iface.call(QStringLiteral("ClearGameProfile"), gameId);
|
||||
QVERIFY(reply.isValid());
|
||||
QDBusPendingReply<QVariantMap> reply = iface.ClearGameProfile(gameId);
|
||||
reply.waitForFinished();
|
||||
QVERIFY2(!reply.isError(), qPrintable(reply.error().message()));
|
||||
QVERIFY(reply.value().value(QStringLiteral("ok")).toBool());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
202
tests/processscanner_test.cpp
Normal file
202
tests/processscanner_test.cpp
Normal file
|
|
@ -0,0 +1,202 @@
|
|||
// SPDX-License-Identifier: GPL-3.0-or-later
|
||||
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
||||
|
||||
#include "processscanner.h"
|
||||
|
||||
#include <QElapsedTimer>
|
||||
#include <QFileInfo>
|
||||
#include <QProcess>
|
||||
#include <QProcessEnvironment>
|
||||
#include <QStandardPaths>
|
||||
#include <QString>
|
||||
#include <QUuid>
|
||||
#include <QtTest>
|
||||
|
||||
#include <atomic>
|
||||
#include <utility>
|
||||
|
||||
class ProcessScannerTest : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
private Q_SLOTS:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
void scanFindsEnvAndCmdline();
|
||||
void preferEnvironmentMatchesFilters();
|
||||
void cancelledScanReturnsEmpty();
|
||||
|
||||
private:
|
||||
QString m_shell;
|
||||
QString m_marker;
|
||||
QProcess m_envProc;
|
||||
QProcess m_cmdProc;
|
||||
|
||||
static bool containsPid(const QList<ProcessScanner::Match> &matches, qint64 pid)
|
||||
{
|
||||
for (const auto &m : matches) {
|
||||
if (m.pid == static_cast<uint>(pid)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static const ProcessScanner::Match *findByPid(const QList<ProcessScanner::Match> &matches, qint64 pid)
|
||||
{
|
||||
for (const auto &m : matches) {
|
||||
if (m.pid == static_cast<uint>(pid)) {
|
||||
return &m;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
static bool waitForScan(std::function<bool()> predicate, int timeoutMs)
|
||||
{
|
||||
QElapsedTimer t;
|
||||
t.start();
|
||||
while (t.elapsed() < timeoutMs) {
|
||||
if (predicate()) {
|
||||
return true;
|
||||
}
|
||||
QTest::qWait(50);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void startProcesses();
|
||||
void stopProcess(QProcess &p);
|
||||
};
|
||||
|
||||
void ProcessScannerTest::initTestCase()
|
||||
{
|
||||
if (!QFileInfo(QStringLiteral("/proc")).isDir()) {
|
||||
QSKIP("/proc not available");
|
||||
}
|
||||
|
||||
m_shell = QStandardPaths::findExecutable(QStringLiteral("sh"));
|
||||
if (m_shell.isEmpty()) {
|
||||
QSKIP("sh not found");
|
||||
}
|
||||
|
||||
m_marker = QUuid::createUuid().toString(QUuid::WithoutBraces);
|
||||
startProcesses();
|
||||
}
|
||||
|
||||
void ProcessScannerTest::cleanupTestCase()
|
||||
{
|
||||
stopProcess(m_envProc);
|
||||
stopProcess(m_cmdProc);
|
||||
}
|
||||
|
||||
void ProcessScannerTest::startProcesses()
|
||||
{
|
||||
const QString script = QStringLiteral("while true; do sleep 60; done # %1").arg(m_marker);
|
||||
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
env.insert(QStringLiteral("ALAKARTE_PROCESSSCANNER_TEST_MARKER"), m_marker);
|
||||
m_envProc.setProcessEnvironment(env);
|
||||
m_envProc.start(m_shell, {QStringLiteral("-c"), script});
|
||||
QVERIFY2(m_envProc.waitForStarted(5000), qPrintable(m_envProc.errorString()));
|
||||
QVERIFY(m_envProc.processId() > 0);
|
||||
}
|
||||
|
||||
{
|
||||
QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
||||
m_cmdProc.setProcessEnvironment(env);
|
||||
m_cmdProc.start(m_shell, {QStringLiteral("-c"), script});
|
||||
QVERIFY2(m_cmdProc.waitForStarted(5000), qPrintable(m_cmdProc.errorString()));
|
||||
QVERIFY(m_cmdProc.processId() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessScannerTest::stopProcess(QProcess &p)
|
||||
{
|
||||
if (p.state() == QProcess::NotRunning) {
|
||||
return;
|
||||
}
|
||||
|
||||
p.terminate();
|
||||
if (!p.waitForFinished(3000)) {
|
||||
p.kill();
|
||||
p.waitForFinished(3000);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessScannerTest::scanFindsEnvAndCmdline()
|
||||
{
|
||||
QVERIFY(m_envProc.processId() > 0);
|
||||
QVERIFY(m_cmdProc.processId() > 0);
|
||||
|
||||
ProcessScanner::ScanOptions opts;
|
||||
opts.envKeys = {QStringLiteral("ALAKARTE_PROCESSSCANNER_TEST_MARKER")};
|
||||
opts.envValue = m_marker;
|
||||
opts.cmdlineSubstring = m_marker;
|
||||
opts.preferEnvironmentMatches = false;
|
||||
|
||||
QList<ProcessScanner::Match> results;
|
||||
const bool ok = waitForScan(
|
||||
[&]() {
|
||||
results = ProcessScanner::scan(opts);
|
||||
return containsPid(results, m_envProc.processId()) && containsPid(results, m_cmdProc.processId());
|
||||
},
|
||||
5000);
|
||||
|
||||
QVERIFY(ok);
|
||||
|
||||
const auto *envMatch = findByPid(results, m_envProc.processId());
|
||||
QVERIFY(envMatch);
|
||||
QVERIFY(envMatch->envMatched);
|
||||
|
||||
const auto *cmdMatch = findByPid(results, m_cmdProc.processId());
|
||||
QVERIFY(cmdMatch);
|
||||
QVERIFY(!cmdMatch->envMatched);
|
||||
}
|
||||
|
||||
void ProcessScannerTest::preferEnvironmentMatchesFilters()
|
||||
{
|
||||
QVERIFY(m_envProc.processId() > 0);
|
||||
QVERIFY(m_cmdProc.processId() > 0);
|
||||
|
||||
ProcessScanner::ScanOptions opts;
|
||||
opts.envKeys = {QStringLiteral("ALAKARTE_PROCESSSCANNER_TEST_MARKER")};
|
||||
opts.envValue = m_marker;
|
||||
opts.cmdlineSubstring = m_marker;
|
||||
opts.preferEnvironmentMatches = true;
|
||||
|
||||
QList<ProcessScanner::Match> results;
|
||||
const bool ok = waitForScan(
|
||||
[&]() {
|
||||
results = ProcessScanner::scan(opts);
|
||||
return containsPid(results, m_envProc.processId());
|
||||
},
|
||||
5000);
|
||||
|
||||
QVERIFY(ok);
|
||||
QVERIFY(containsPid(results, m_envProc.processId()));
|
||||
QVERIFY(!containsPid(results, m_cmdProc.processId()));
|
||||
|
||||
for (const auto &m : std::as_const(results)) {
|
||||
QVERIFY(m.envMatched);
|
||||
}
|
||||
}
|
||||
|
||||
void ProcessScannerTest::cancelledScanReturnsEmpty()
|
||||
{
|
||||
ProcessScanner::ScanOptions opts;
|
||||
opts.envKeys = {QStringLiteral("ALAKARTE_PROCESSSCANNER_TEST_MARKER")};
|
||||
opts.envValue = m_marker;
|
||||
opts.cmdlineSubstring = m_marker;
|
||||
opts.preferEnvironmentMatches = false;
|
||||
|
||||
const std::atomic_bool cancelled(true);
|
||||
const QList<ProcessScanner::Match> results = ProcessScanner::scan(opts, cancelled);
|
||||
QVERIFY(results.isEmpty());
|
||||
}
|
||||
|
||||
QTEST_MAIN(ProcessScannerTest)
|
||||
|
||||
#include "processscanner_test.moc"
|
||||
|
|
@ -2,6 +2,7 @@
|
|||
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
||||
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusConnectionInterface>
|
||||
#include <QDBusInterface>
|
||||
|
|
@ -53,6 +54,27 @@ static bool waitForService(QDBusConnection &bus, const QString &service, int tim
|
|||
return false;
|
||||
}
|
||||
|
||||
static QVariant unwrapDbusVariant(QVariant v)
|
||||
{
|
||||
if (v.canConvert<QDBusVariant>()) {
|
||||
v = v.value<QDBusVariant>().variant();
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
static QVariantMap unwrapVariantMap(QVariant v)
|
||||
{
|
||||
v = unwrapDbusVariant(v);
|
||||
if (v.canConvert<QDBusArgument>()) {
|
||||
const QDBusArgument arg = v.value<QDBusArgument>();
|
||||
return qdbus_cast<QVariantMap>(arg);
|
||||
}
|
||||
if (v.canConvert<QVariantMap>()) {
|
||||
return v.toMap();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
static QByteArray readProcFile(const QString &path, qint64 maxSize = 65536)
|
||||
{
|
||||
QFile f(path);
|
||||
|
|
@ -226,6 +248,8 @@ private Q_SLOTS:
|
|||
void stopDirectLaunch();
|
||||
void stopByGameIdDirectLaunch();
|
||||
|
||||
void recoverSessionsAfterDaemonRestart();
|
||||
|
||||
private:
|
||||
QString m_gamecenterPath;
|
||||
QProcess m_gamecenter;
|
||||
|
|
@ -351,6 +375,78 @@ void StopLaunchingRegressionTest::cleanupTestCase()
|
|||
}
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::recoverSessionsAfterDaemonRestart()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
if (!bus.isConnected()) {
|
||||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
|
||||
const QString envExe = QStandardPaths::findExecutable(QStringLiteral("env"));
|
||||
if (envExe.isEmpty()) {
|
||||
QSKIP("env executable not found");
|
||||
}
|
||||
|
||||
const QString gameId = QStringLiteral("test-reattach-%1").arg(QUuid::createUuid().toString(QUuid::WithoutBraces));
|
||||
|
||||
QVariantMap spec;
|
||||
spec.insert(QStringLiteral("provider"), QStringLiteral("manual"));
|
||||
spec.insert(QStringLiteral("program"), envExe);
|
||||
spec.insert(QStringLiteral("args"),
|
||||
QStringList{QStringLiteral("ALAKARTE_TEST_MARKER=%1").arg(m_markerValue), QStringLiteral("/bin/sleep"), QStringLiteral("60")});
|
||||
spec.insert(QStringLiteral("gameId"), gameId);
|
||||
|
||||
const QDBusReply<QString> launchReply = iface.call(QStringLiteral("Launch"), spec);
|
||||
QVERIFY2(launchReply.isValid(), qPrintable(launchReply.error().message()));
|
||||
const QString sessionId = launchReply.value();
|
||||
QVERIFY2(!sessionId.isEmpty(), "Launch returned empty sessionId");
|
||||
const QString unitName = QStringLiteral("alakarte-game-%1.scope").arg(sessionId);
|
||||
|
||||
QVERIFY2(waitForAnyEnvironEntry(markerNeedle, 5000), "direct launch marker process did not appear");
|
||||
|
||||
m_gamecenter.kill();
|
||||
QVERIFY(m_gamecenter.waitForFinished(5000));
|
||||
QVERIFY2(waitForNoEnvironEntry(markerNeedle, 1000) == false, "marker process unexpectedly disappeared after daemon kill");
|
||||
|
||||
m_gamecenter.start();
|
||||
QVERIFY(m_gamecenter.waitForStarted(5000));
|
||||
QVERIFY2(waitForService(bus, QStringLiteral("org.kde.GameCenter1"), 5000), "GameCenter1 service did not re-appear after restart");
|
||||
|
||||
QDBusInterface iface2(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface2.isValid(), "GameCenter1 DBus interface not valid after restart");
|
||||
|
||||
const QDBusReply<QVariantList> listReply = iface2.call(QStringLiteral("ListSessions"));
|
||||
QVERIFY2(listReply.isValid(), qPrintable(listReply.error().message()));
|
||||
|
||||
bool found = false;
|
||||
for (const QVariant &v : listReply.value()) {
|
||||
const QVariantMap m = unwrapVariantMap(v);
|
||||
if (m.value(QStringLiteral("sessionId")).toString() == sessionId) {
|
||||
found = true;
|
||||
QCOMPARE(m.value(QStringLiteral("gameId")).toString(), gameId);
|
||||
QCOMPARE(m.value(QStringLiteral("state")).toString(), QStringLiteral("Running"));
|
||||
break;
|
||||
}
|
||||
}
|
||||
QVERIFY2(found, "expected recovered session not found in ListSessions() after restart");
|
||||
|
||||
const QDBusReply<void> stopReply = iface2.call(QStringLiteral("Stop"), sessionId);
|
||||
QVERIFY2(stopReply.isValid(), qPrintable(stopReply.error().message()));
|
||||
|
||||
const bool cleaned = waitForNoEnvironEntry(markerNeedle, 15000);
|
||||
if (!cleaned) {
|
||||
killPids(pidsWithEnvironEntry(markerNeedle));
|
||||
}
|
||||
QVERIFY2(cleaned, "marker process still alive after Stop() on recovered session");
|
||||
QVERIFY2(waitForUnitNotActive(unitName, 15000), "systemd unit still active after Stop() on recovered session");
|
||||
}
|
||||
|
||||
void StopLaunchingRegressionTest::stopWhileLaunchingSteam()
|
||||
{
|
||||
QDBusConnection bus = QDBusConnection::sessionBus();
|
||||
|
|
@ -358,7 +454,7 @@ void StopLaunchingRegressionTest::stopWhileLaunchingSteam()
|
|||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QString appId = QStringLiteral("987654");
|
||||
|
|
@ -396,7 +492,7 @@ void StopLaunchingRegressionTest::stopByGameIdWhileLaunchingSteam()
|
|||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
|
|
@ -438,7 +534,7 @@ void StopLaunchingRegressionTest::stopWhileLaunchingLutris()
|
|||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
|
|
@ -476,7 +572,7 @@ void StopLaunchingRegressionTest::stopByGameIdWhileLaunchingLutris()
|
|||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
|
|
@ -514,7 +610,7 @@ void StopLaunchingRegressionTest::stopDirectLaunch()
|
|||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
|
|
@ -561,7 +657,7 @@ void StopLaunchingRegressionTest::stopByGameIdDirectLaunch()
|
|||
QSKIP("session bus not available");
|
||||
}
|
||||
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QDBusInterface iface(QStringLiteral("org.kde.GameCenter1"), QStringLiteral("/org/kde/ALaKarte/GameCenter1"), QStringLiteral("org.kde.GameCenter1"), bus);
|
||||
QVERIFY2(iface.isValid(), "GameCenter1 DBus interface not valid");
|
||||
|
||||
const QByteArray markerNeedle = (QStringLiteral("ALAKARTE_TEST_MARKER=") + m_markerValue).toUtf8();
|
||||
|
|
|
|||
Loading…
Reference in a new issue