Advance Wayland and KDE package bring-up

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-14 10:51:06 +01:00
parent 51f3c21121
commit cf12defd28
15214 changed files with 20594243 additions and 269 deletions
@@ -0,0 +1,347 @@
/*
This file is part of libkdbusaddons
SPDX-FileCopyrightText: 2011 David Faure <faure@kde.org>
SPDX-FileCopyrightText: 2011 Kevin Ottens <ervin@kde.org>
SPDX-FileCopyrightText: 2019 Harald Sitter <sitter@kde.org>
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*/
#include "kdbusservice.h"
#include <QCoreApplication>
#include <QDebug>
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusReply>
#include "FreeDesktopApplpicationIface.h"
#include "KDBusServiceIface.h"
#include "config-kdbusaddons.h"
#if 0
#include <private/qtx11extras_p.h>
#endif
#include "kdbusaddons_debug.h"
#include "kdbusservice_adaptor.h"
#include "kdbusserviceextensions_adaptor.h"
class KDBusServicePrivate
{
public:
KDBusServicePrivate()
: registered(false)
, exitValue(0)
{
}
QString generateServiceName()
{
const QCoreApplication *app = QCoreApplication::instance();
const QString domain = app->organizationDomain();
const QStringList parts = domain.split(QLatin1Char('.'), Qt::SkipEmptyParts);
QString reversedDomain;
if (parts.isEmpty()) {
reversedDomain = QStringLiteral("local.");
} else {
for (const QString &part : parts) {
reversedDomain.prepend(QLatin1Char('.'));
reversedDomain.prepend(part);
}
}
return reversedDomain + app->applicationName();
}
static void handlePlatformData(const QVariantMap &platformData)
{
#if 0
if (QX11Info::isPlatformX11()) {
QByteArray desktopStartupId = platformData.value(QStringLiteral("desktop-startup-id")).toByteArray();
if (!desktopStartupId.isEmpty()) {
QX11Info::setNextStartupId(desktopStartupId);
}
}
#endif
const auto xdgActivationToken = platformData.value(QLatin1String("activation-token")).toByteArray();
if (!xdgActivationToken.isEmpty()) {
qputenv("XDG_ACTIVATION_TOKEN", xdgActivationToken);
}
}
bool registered;
QString serviceName;
QString errorMessage;
int exitValue;
};
// Wraps a serviceName registration.
class Registration : public QObject
{
Q_OBJECT
public:
enum class Register {
RegisterWitoutQueue,
RegisterWithQueue,
};
Registration(KDBusService *s_, KDBusServicePrivate *d_, KDBusService::StartupOptions options_)
: s(s_)
, d(d_)
, options(options_)
{
if (!QDBusConnection::sessionBus().isConnected() || !(bus = QDBusConnection::sessionBus().interface())) {
d->errorMessage = QLatin1String(
"DBus session bus not found. To circumvent this problem try the following command (with bash):\n"
" export $(dbus-launch)");
} else {
generateServiceName();
}
}
void run()
{
if (bus) {
registerOnBus();
}
if (!d->registered && ((options & KDBusService::NoExitOnFailure) == 0)) {
qCCritical(KDBUSADDONS_LOG) << qPrintable(d->errorMessage);
exit(1);
}
}
private:
void generateServiceName()
{
d->serviceName = d->generateServiceName();
objectPath = QLatin1Char('/') + d->serviceName;
objectPath.replace(QLatin1Char('.'), QLatin1Char('/'));
objectPath.replace(QLatin1Char('-'), QLatin1Char('_')); // see spec change at https://bugs.freedesktop.org/show_bug.cgi?id=95129
if (options & KDBusService::Multiple) {
const bool inSandbox = QFileInfo::exists(QStringLiteral("/.flatpak-info"));
if (inSandbox) {
d->serviceName += QStringLiteral(".kdbus-")
+ QDBusConnection::sessionBus().baseService().replace(QRegularExpression(QStringLiteral("[\\.:]")), QStringLiteral("_"));
} else {
d->serviceName += QLatin1Char('-') + QString::number(QCoreApplication::applicationPid());
}
}
}
void registerOnBus()
{
auto bus = QDBusConnection::sessionBus();
bool objectRegistered = false;
objectRegistered = bus.registerObject(QStringLiteral("/MainApplication"),
QCoreApplication::instance(),
QDBusConnection::ExportAllSlots //
| QDBusConnection::ExportScriptableProperties //
| QDBusConnection::ExportAdaptors);
if (!objectRegistered) {
qCWarning(KDBUSADDONS_LOG) << "Failed to register /MainApplication on DBus";
return;
}
objectRegistered = bus.registerObject(objectPath, s, QDBusConnection::ExportAdaptors);
if (!objectRegistered) {
qCWarning(KDBUSADDONS_LOG) << "Failed to register" << objectPath << "on DBus";
return;
}
attemptRegistration();
}
void attemptRegistration()
{
Q_ASSERT(!d->registered);
auto queueOption = QDBusConnectionInterface::DontQueueService;
if (options & KDBusService::Unique) {
// When a process crashes and gets auto-restarted by KCrash we may
// be in this code path "too early". There is a bit of a delay
// between the restart and the previous process dropping off of the
// bus and thus releasing its registered names. As a result there
// is a good chance that if we wait a bit the name will shortly
// become registered.
queueOption = QDBusConnectionInterface::QueueService;
connect(bus, &QDBusConnectionInterface::serviceRegistered, this, [this](const QString &service) {
if (service != d->serviceName) {
return;
}
d->registered = true;
registrationLoop.quit();
});
}
d->registered = (bus->registerService(d->serviceName, queueOption) == QDBusConnectionInterface::ServiceRegistered);
if (d->registered) {
return;
}
if (options & KDBusService::Replace) {
auto message = QDBusMessage::createMethodCall(d->serviceName,
QStringLiteral("/MainApplication"),
QStringLiteral("org.qtproject.Qt.QCoreApplication"),
QStringLiteral("quit"));
QDBusConnection::sessionBus().asyncCall(message);
waitForRegistration();
} else if (options & KDBusService::Unique) {
// Already running so it's ok!
QVariantMap platform_data;
#if 0
if (QX11Info::isPlatformX11()) {
QString startupId = QString::fromUtf8(qgetenv("DESKTOP_STARTUP_ID"));
if (startupId.isEmpty()) {
startupId = QString::fromUtf8(QX11Info::nextStartupId());
}
if (!startupId.isEmpty()) {
platform_data.insert(QStringLiteral("desktop-startup-id"), startupId);
}
}
#endif
if (qEnvironmentVariableIsSet("XDG_ACTIVATION_TOKEN")) {
platform_data.insert(QStringLiteral("activation-token"), qgetenv("XDG_ACTIVATION_TOKEN"));
}
if (QCoreApplication::arguments().count() > 1) {
OrgKdeKDBusServiceInterface iface(d->serviceName, objectPath, QDBusConnection::sessionBus());
iface.setTimeout(5 * 60 * 1000); // Application can take time to answer
QDBusReply<int> reply = iface.CommandLine(QCoreApplication::arguments(), QDir::currentPath(), platform_data);
if (reply.isValid()) {
exit(reply.value());
} else {
d->errorMessage = reply.error().message();
}
} else {
OrgFreedesktopApplicationInterface iface(d->serviceName, objectPath, QDBusConnection::sessionBus());
iface.setTimeout(5 * 60 * 1000); // Application can take time to answer
QDBusReply<void> reply = iface.Activate(platform_data);
if (reply.isValid()) {
exit(0);
} else {
d->errorMessage = reply.error().message();
}
}
// service did not respond in a valid way....
// let's wait to see if our queued registration finishes perhaps.
waitForRegistration();
}
if (!d->registered) { // either multi service or failed to reclaim name
d->errorMessage = QLatin1String("Failed to register name '") + d->serviceName + QLatin1String("' with DBUS - does this process have permission to use the name, and do no other processes own it already?");
}
}
void waitForRegistration()
{
QTimer quitTimer;
// We have to wait for the other application to quit completely which could take a while
quitTimer.start(8000);
connect(&quitTimer, &QTimer::timeout, &registrationLoop, &QEventLoop::quit);
registrationLoop.exec();
}
QDBusConnectionInterface *bus = nullptr;
KDBusService *s = nullptr;
KDBusServicePrivate *d = nullptr;
KDBusService::StartupOptions options;
QEventLoop registrationLoop;
QString objectPath;
};
KDBusService::KDBusService(StartupOptions options, QObject *parent)
: QObject(parent)
, d(new KDBusServicePrivate)
{
new KDBusServiceAdaptor(this);
new KDBusServiceExtensionsAdaptor(this);
Registration registration(this, d.get(), options);
registration.run();
}
KDBusService::~KDBusService() = default;
bool KDBusService::isRegistered() const
{
return d->registered;
}
QString KDBusService::errorMessage() const
{
return d->errorMessage;
}
void KDBusService::setExitValue(int value)
{
d->exitValue = value;
}
QString KDBusService::serviceName() const
{
return d->serviceName;
}
void KDBusService::unregister()
{
QDBusConnectionInterface *bus = nullptr;
if (!d->registered || !QDBusConnection::sessionBus().isConnected() || !(bus = QDBusConnection::sessionBus().interface())) {
return;
}
bus->unregisterService(d->serviceName);
}
void KDBusService::Activate(const QVariantMap &platform_data)
{
d->handlePlatformData(platform_data);
Q_EMIT activateRequested(QStringList(QCoreApplication::arguments()[0]), QDir::currentPath());
qunsetenv("XDG_ACTIVATION_TOKEN");
}
void KDBusService::Open(const QStringList &uris, const QVariantMap &platform_data)
{
d->handlePlatformData(platform_data);
Q_EMIT openRequested(QUrl::fromStringList(uris));
qunsetenv("XDG_ACTIVATION_TOKEN");
}
void KDBusService::ActivateAction(const QString &action_name, const QVariantList &maybeParameter, const QVariantMap &platform_data)
{
d->handlePlatformData(platform_data);
// This is a workaround for D-Bus not supporting null variants.
const QVariant param = maybeParameter.count() == 1 ? maybeParameter.first() : QVariant();
Q_EMIT activateActionRequested(action_name, param);
qunsetenv("XDG_ACTIVATION_TOKEN");
}
int KDBusService::CommandLine(const QStringList &arguments, const QString &workingDirectory, const QVariantMap &platform_data)
{
d->exitValue = 0;
d->handlePlatformData(platform_data);
// The TODOs here only make sense if this method can be called from the GUI.
// If it's for pure "usage in the terminal" then no startup notification got started.
// But maybe one day the workspace wants to call this for the Exec key of a .desktop file?
Q_EMIT activateRequested(arguments, workingDirectory);
qunsetenv("XDG_ACTIVATION_TOKEN");
return d->exitValue;
}
#include "kdbusservice.moc"
#include "moc_kdbusservice.cpp"