mirror of
https://invent.kde.org/marcoa/a-la-karte.git
synced 2026-03-27 09:13:09 +00:00
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.
246 lines
9.3 KiB
C++
246 lines
9.3 KiB
C++
// SPDX-License-Identifier: GPL-3.0-or-later
|
|
// SPDX-FileCopyrightText: 2026 A-La-Karte Contributors
|
|
|
|
#include <KAuth/ActionReply>
|
|
#include <KAuth/HelperSupport>
|
|
|
|
#include <QDBusArgument>
|
|
#include <QDBusConnection>
|
|
#include <QDBusConnectionInterface>
|
|
#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");
|
|
|
|
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");
|
|
|
|
class GameCenterHelper : public QObject
|
|
{
|
|
Q_OBJECT
|
|
|
|
public Q_SLOTS:
|
|
ActionReply setpowerprofile(const QVariantMap &args);
|
|
ActionReply activate_session(const QVariantMap &args);
|
|
ActionReply switch_to_vt(const QVariantMap &args);
|
|
ActionReply terminate_session(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);
|
|
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;
|
|
}
|
|
|
|
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();
|
|
reply.setErrorDescription(QStringLiteral("power-profiles-daemon D-Bus interface not available"));
|
|
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))});
|
|
|
|
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();
|
|
}
|
|
|
|
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();
|
|
}
|
|
|
|
KAUTH_HELPER_MAIN("org.kde.alakarte.gamecenter.helper", GameCenterHelper)
|
|
|
|
#include "gamecenterkauthhelper.moc"
|