2026-02-12 13:51:20 +00:00
|
|
|
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
|
|
|
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
|
|
|
|
|
|
|
|
|
#include <KAuth/ActionReply>
|
|
|
|
|
#include <KAuth/HelperSupport>
|
|
|
|
|
|
2026-02-14 13:08:45 +00:00
|
|
|
#include <QDBusArgument>
|
2026-02-12 13:51:20 +00:00
|
|
|
#include <QDBusConnection>
|
2026-02-14 13:08:45 +00:00
|
|
|
#include <QDBusConnectionInterface>
|
2026-02-12 13:51:20 +00:00
|
|
|
#include <QDBusError>
|
|
|
|
|
#include <QDBusInterface>
|
|
|
|
|
#include <QDBusMessage>
|
|
|
|
|
#include <QDBusReply>
|
|
|
|
|
#include <QDBusVariant>
|
|
|
|
|
#include <QObject>
|
|
|
|
|
#include <QVariant>
|
|
|
|
|
#include <QVariantMap>
|
|
|
|
|
|
|
|
|
|
using namespace KAuth;
|
|
|
|
|
|
|
|
|
|
static const QString kPowerProfilesService = QStringLiteral("net.hadess.PowerProfiles");
|
|
|
|
|
static const QString kPowerProfilesPath = QStringLiteral("/net/hadess/PowerProfiles");
|
|
|
|
|
static const QString kPowerProfilesInterface = QStringLiteral("net.hadess.PowerProfiles");
|
|
|
|
|
|
Add Plasma tray, notification inhibition, session control, and flatpak runner
Expand TrayController with daemon health polling every 10 seconds via
D-Bus Ping calls to org.kde.GameCenter1, org.kde.ALaKarte.Runner1, and
org.kde.ALaKarte.Input1. Add menu actions to toggle console behaviors,
notification mirroring, and to restart each daemon via systemctl --user.
Add NotificationInhibitor, owned by App, which calls
org.freedesktop.Notifications Inhibit/UnInhibit on the session bus
whenever the consoleBehaviors config setting changes. The cookie is
released on application quit.
Add consoleBehaviors and mirrorNotifications properties to Config with
KConfig persistence under the [Console] group.
Ship org.kde.alakarte.notifyrc defining GameLaunched, GameExited, and
LaunchFailed notification events so Plasma attributes them correctly in
the notification history.
Extend RunnerManagerDaemon::ResolveLaunch with a flatpak runner branch
that constructs a flatpak run command, translates environment overrides
to --env= arguments, and respects flatpakAppId, flatpakBranch,
flatpakArch, and flatpakArgs from the launch spec.
Add activate_session, switch_to_vt, and terminate_session actions to
the gamecenter KAuth helper and its polkit policy. Each action calls
the corresponding method on org.freedesktop.login1 over the system bus.
Add CouchSidebar.qml, a horizontal source tab bar shown in couch mode
above the library view, exposing the same sourceSelected, settingsRequested,
importRequested, and aboutRequested signals as SidebarView.
Fix duplicate adaptiveFocusRingWidth property in GameCard.qml.
2026-03-22 15:53:09 +00:00
|
|
|
static const QString kLogin1Service = QStringLiteral("org.freedesktop.login1");
|
|
|
|
|
static const QString kLogin1ManagerPath = QStringLiteral("/org/freedesktop/login1");
|
|
|
|
|
static const QString kLogin1ManagerIface = QStringLiteral("org.freedesktop.login1.Manager");
|
|
|
|
|
static const QString kLogin1SeatPath = QStringLiteral("/org/freedesktop/login1/seat/seat0");
|
|
|
|
|
static const QString kLogin1SeatIface = QStringLiteral("org.freedesktop.login1.Seat");
|
|
|
|
|
|
2026-02-12 13:51:20 +00:00
|
|
|
class GameCenterHelper : public QObject
|
|
|
|
|
{
|
|
|
|
|
Q_OBJECT
|
|
|
|
|
|
|
|
|
|
public Q_SLOTS:
|
|
|
|
|
ActionReply setpowerprofile(const QVariantMap &args);
|
Add Plasma tray, notification inhibition, session control, and flatpak runner
Expand TrayController with daemon health polling every 10 seconds via
D-Bus Ping calls to org.kde.GameCenter1, org.kde.ALaKarte.Runner1, and
org.kde.ALaKarte.Input1. Add menu actions to toggle console behaviors,
notification mirroring, and to restart each daemon via systemctl --user.
Add NotificationInhibitor, owned by App, which calls
org.freedesktop.Notifications Inhibit/UnInhibit on the session bus
whenever the consoleBehaviors config setting changes. The cookie is
released on application quit.
Add consoleBehaviors and mirrorNotifications properties to Config with
KConfig persistence under the [Console] group.
Ship org.kde.alakarte.notifyrc defining GameLaunched, GameExited, and
LaunchFailed notification events so Plasma attributes them correctly in
the notification history.
Extend RunnerManagerDaemon::ResolveLaunch with a flatpak runner branch
that constructs a flatpak run command, translates environment overrides
to --env= arguments, and respects flatpakAppId, flatpakBranch,
flatpakArch, and flatpakArgs from the launch spec.
Add activate_session, switch_to_vt, and terminate_session actions to
the gamecenter KAuth helper and its polkit policy. Each action calls
the corresponding method on org.freedesktop.login1 over the system bus.
Add CouchSidebar.qml, a horizontal source tab bar shown in couch mode
above the library view, exposing the same sourceSelected, settingsRequested,
importRequested, and aboutRequested signals as SidebarView.
Fix duplicate adaptiveFocusRingWidth property in GameCard.qml.
2026-03-22 15:53:09 +00:00
|
|
|
ActionReply activate_session(const QVariantMap &args);
|
|
|
|
|
ActionReply switch_to_vt(const QVariantMap &args);
|
|
|
|
|
ActionReply terminate_session(const QVariantMap &args);
|
2026-02-12 13:51:20 +00:00
|
|
|
};
|
|
|
|
|
|
2026-02-14 13:08:45 +00:00
|
|
|
static QVariant unwrapDbusVariant(QVariant v)
|
|
|
|
|
{
|
|
|
|
|
if (v.canConvert<QDBusVariant>()) {
|
|
|
|
|
v = v.value<QDBusVariant>().variant();
|
|
|
|
|
}
|
|
|
|
|
return v;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 13:51:20 +00:00
|
|
|
static QString unwrapStringArg(const QVariantMap &args, const QString &key)
|
|
|
|
|
{
|
|
|
|
|
QVariant v = args.value(key);
|
|
|
|
|
if (v.canConvert<QDBusVariant>()) {
|
|
|
|
|
v = v.value<QDBusVariant>().variant();
|
|
|
|
|
}
|
|
|
|
|
return v.toString();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ActionReply GameCenterHelper::setpowerprofile(const QVariantMap &args)
|
|
|
|
|
{
|
|
|
|
|
const QString profile = unwrapStringArg(args, QStringLiteral("profile"));
|
|
|
|
|
if (profile.isEmpty()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("missing profile"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDBusConnection bus = QDBusConnection::systemBus();
|
|
|
|
|
if (!bus.isConnected()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("system bus not connected"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 13:08:45 +00:00
|
|
|
if (bus.interface() && !bus.interface()->isServiceRegistered(kPowerProfilesService)) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("power-profiles-daemon service not available"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 13:51:20 +00:00
|
|
|
QDBusInterface props(kPowerProfilesService, kPowerProfilesPath, QStringLiteral("org.freedesktop.DBus.Properties"), bus);
|
|
|
|
|
if (!props.isValid()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("power-profiles-daemon D-Bus interface not available"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-14 13:08:45 +00:00
|
|
|
{
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 13:51:20 +00:00
|
|
|
QDBusMessage msg =
|
|
|
|
|
QDBusMessage::createMethodCall(kPowerProfilesService, kPowerProfilesPath, QStringLiteral("org.freedesktop.DBus.Properties"), QStringLiteral("Set"));
|
|
|
|
|
msg.setArguments({kPowerProfilesInterface, QStringLiteral("ActiveProfile"), QVariant::fromValue(QDBusVariant(profile))});
|
|
|
|
|
|
|
|
|
|
const QDBusMessage replyMsg = bus.call(msg, QDBus::Block, 5000);
|
|
|
|
|
if (replyMsg.type() == QDBusMessage::ErrorMessage) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(replyMsg.errorName() + QStringLiteral(": ") + replyMsg.errorMessage());
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ActionReply::SuccessReply();
|
|
|
|
|
}
|
|
|
|
|
|
Add Plasma tray, notification inhibition, session control, and flatpak runner
Expand TrayController with daemon health polling every 10 seconds via
D-Bus Ping calls to org.kde.GameCenter1, org.kde.ALaKarte.Runner1, and
org.kde.ALaKarte.Input1. Add menu actions to toggle console behaviors,
notification mirroring, and to restart each daemon via systemctl --user.
Add NotificationInhibitor, owned by App, which calls
org.freedesktop.Notifications Inhibit/UnInhibit on the session bus
whenever the consoleBehaviors config setting changes. The cookie is
released on application quit.
Add consoleBehaviors and mirrorNotifications properties to Config with
KConfig persistence under the [Console] group.
Ship org.kde.alakarte.notifyrc defining GameLaunched, GameExited, and
LaunchFailed notification events so Plasma attributes them correctly in
the notification history.
Extend RunnerManagerDaemon::ResolveLaunch with a flatpak runner branch
that constructs a flatpak run command, translates environment overrides
to --env= arguments, and respects flatpakAppId, flatpakBranch,
flatpakArch, and flatpakArgs from the launch spec.
Add activate_session, switch_to_vt, and terminate_session actions to
the gamecenter KAuth helper and its polkit policy. Each action calls
the corresponding method on org.freedesktop.login1 over the system bus.
Add CouchSidebar.qml, a horizontal source tab bar shown in couch mode
above the library view, exposing the same sourceSelected, settingsRequested,
importRequested, and aboutRequested signals as SidebarView.
Fix duplicate adaptiveFocusRingWidth property in GameCard.qml.
2026-03-22 15:53:09 +00:00
|
|
|
ActionReply GameCenterHelper::activate_session(const QVariantMap &args)
|
|
|
|
|
{
|
|
|
|
|
const QString sessionId = unwrapStringArg(args, QStringLiteral("sessionId"));
|
|
|
|
|
if (sessionId.isEmpty()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("missing sessionId"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDBusConnection bus = QDBusConnection::systemBus();
|
|
|
|
|
if (!bus.isConnected()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("system bus not connected"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDBusInterface mgr(kLogin1Service, kLogin1ManagerPath, kLogin1ManagerIface, bus);
|
|
|
|
|
if (!mgr.isValid()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("login1 manager interface not available"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QDBusMessage replyMsg = mgr.call(QStringLiteral("ActivateSession"), sessionId);
|
|
|
|
|
if (replyMsg.type() == QDBusMessage::ErrorMessage) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(replyMsg.errorName() + QStringLiteral(": ") + replyMsg.errorMessage());
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ActionReply::SuccessReply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ActionReply GameCenterHelper::switch_to_vt(const QVariantMap &args)
|
|
|
|
|
{
|
|
|
|
|
QVariant vtArg = args.value(QStringLiteral("vtnr"));
|
|
|
|
|
if (vtArg.canConvert<QDBusVariant>()) {
|
|
|
|
|
vtArg = vtArg.value<QDBusVariant>().variant();
|
|
|
|
|
}
|
|
|
|
|
bool ok = false;
|
|
|
|
|
const uint vtnr = vtArg.toUInt(&ok);
|
|
|
|
|
if (!ok || vtnr == 0) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("missing or invalid vtnr"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDBusConnection bus = QDBusConnection::systemBus();
|
|
|
|
|
if (!bus.isConnected()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("system bus not connected"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDBusInterface seat(kLogin1Service, kLogin1SeatPath, kLogin1SeatIface, bus);
|
|
|
|
|
if (!seat.isValid()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("login1 seat interface not available"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QDBusMessage replyMsg = seat.call(QStringLiteral("SwitchTo"), vtnr);
|
|
|
|
|
if (replyMsg.type() == QDBusMessage::ErrorMessage) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(replyMsg.errorName() + QStringLiteral(": ") + replyMsg.errorMessage());
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ActionReply::SuccessReply();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ActionReply GameCenterHelper::terminate_session(const QVariantMap &args)
|
|
|
|
|
{
|
|
|
|
|
const QString sessionId = unwrapStringArg(args, QStringLiteral("sessionId"));
|
|
|
|
|
if (sessionId.isEmpty()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("missing sessionId"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDBusConnection bus = QDBusConnection::systemBus();
|
|
|
|
|
if (!bus.isConnected()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("system bus not connected"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
QDBusInterface mgr(kLogin1Service, kLogin1ManagerPath, kLogin1ManagerIface, bus);
|
|
|
|
|
if (!mgr.isValid()) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(QStringLiteral("login1 manager interface not available"));
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const QDBusMessage replyMsg = mgr.call(QStringLiteral("TerminateSession"), sessionId);
|
|
|
|
|
if (replyMsg.type() == QDBusMessage::ErrorMessage) {
|
|
|
|
|
ActionReply reply = ActionReply::HelperErrorReply();
|
|
|
|
|
reply.setErrorDescription(replyMsg.errorName() + QStringLiteral(": ") + replyMsg.errorMessage());
|
|
|
|
|
return reply;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ActionReply::SuccessReply();
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-12 13:51:20 +00:00
|
|
|
KAUTH_HELPER_MAIN("org.kde.alakarte.gamecenter.helper", GameCenterHelper)
|
|
|
|
|
|
|
|
|
|
#include "gamecenterkauthhelper.moc"
|