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:
@@ -0,0 +1,118 @@
|
||||
add_subdirectory(tools/kquitapp)
|
||||
|
||||
add_library(KF6DBusAddons)
|
||||
add_library(KF6::DBusAddons ALIAS KF6DBusAddons)
|
||||
|
||||
set_target_properties(KF6DBusAddons PROPERTIES
|
||||
VERSION ${KDBUSADDONS_VERSION}
|
||||
SOVERSION ${KDBUSADDONS_SOVERSION}
|
||||
EXPORT_NAME DBusAddons
|
||||
)
|
||||
|
||||
ecm_create_qm_loader(KF6DBusAddons kdbusaddons6_qt)
|
||||
|
||||
target_sources(KF6DBusAddons PRIVATE
|
||||
kdbusservice.cpp
|
||||
kdbusservice.h
|
||||
kdedmodule.cpp
|
||||
kdedmodule.h
|
||||
kupdatelaunchenvironmentjob.cpp
|
||||
kupdatelaunchenvironmentjob.h
|
||||
)
|
||||
|
||||
ecm_qt_declare_logging_category(KF6DBusAddons
|
||||
HEADER kdbusaddons_debug.h
|
||||
IDENTIFIER KDBUSADDONS_LOG
|
||||
CATEGORY_NAME kf.dbusaddons
|
||||
OLD_CATEGORY_NAMES kf5.kdbusaddons
|
||||
DESCRIPTION "KDBusAddons"
|
||||
EXPORT KDBUSADDONS
|
||||
)
|
||||
|
||||
set(libkdbusaddons_dbus_SRCS)
|
||||
qt_add_dbus_interface(libkdbusaddons_dbus_SRCS org.freedesktop.Application.xml FreeDesktopApplpicationIface)
|
||||
qt_add_dbus_interface(libkdbusaddons_dbus_SRCS org.kde.KDBusService.xml KDBusServiceIface)
|
||||
|
||||
qt_add_dbus_adaptor(libkdbusaddons_dbus_SRCS
|
||||
org.freedesktop.Application.xml
|
||||
kdbusservice.h
|
||||
KDBusService
|
||||
kdbusservice_adaptor
|
||||
KDBusServiceAdaptor)
|
||||
qt_add_dbus_adaptor(libkdbusaddons_dbus_SRCS
|
||||
org.kde.KDBusService.xml
|
||||
kdbusservice.h
|
||||
KDBusService
|
||||
kdbusserviceextensions_adaptor
|
||||
KDBusServiceExtensionsAdaptor)
|
||||
|
||||
target_sources(KF6DBusAddons PRIVATE
|
||||
${libkdbusaddons_dbus_SRCS}
|
||||
)
|
||||
|
||||
ecm_generate_export_header(KF6DBusAddons
|
||||
BASE_NAME KDBusAddons
|
||||
GROUP_BASE_NAME KF
|
||||
VERSION ${KF_VERSION}
|
||||
USE_VERSION_HEADER
|
||||
DEPRECATED_BASE_VERSION 0
|
||||
DEPRECATION_VERSIONS
|
||||
EXCLUDE_DEPRECATED_BEFORE_AND_AT ${EXCLUDE_DEPRECATED_BEFORE_AND_AT}
|
||||
)
|
||||
|
||||
target_link_libraries(KF6DBusAddons PUBLIC Qt6::DBus)
|
||||
|
||||
if(FALSE)
|
||||
target_link_libraries(KF6DBusAddons PRIVATE Qt6::GuiPrivate) # qtx11extras_p.h
|
||||
endif()
|
||||
|
||||
target_include_directories(KF6DBusAddons INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF}/KDBusAddons>")
|
||||
|
||||
configure_file(config-kdbusaddons.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kdbusaddons.h )
|
||||
|
||||
ecm_generate_headers(KDBusAddons_HEADERS
|
||||
HEADER_NAMES
|
||||
KDBusService
|
||||
KDEDModule
|
||||
KUpdateLaunchEnvironmentJob
|
||||
REQUIRED_HEADERS KDBusAddons_HEADERS
|
||||
)
|
||||
|
||||
install(TARGETS KF6DBusAddons EXPORT KF6DBusAddonsTargets ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
|
||||
install(FILES
|
||||
${KDBusAddons_HEADERS}
|
||||
${CMAKE_CURRENT_BINARY_DIR}/kdbusaddons_export.h
|
||||
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF}/KDBusAddons COMPONENT Devel
|
||||
)
|
||||
|
||||
ecm_qt_install_logging_categories(
|
||||
EXPORT KDBUSADDONS
|
||||
FILE kdbusaddons.categories
|
||||
DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
|
||||
)
|
||||
|
||||
if(BUILD_QCH)
|
||||
ecm_add_qch(
|
||||
KF6DBusAddons_QCH
|
||||
NAME KDBusAddons
|
||||
BASE_NAME KF6DBusAddons
|
||||
VERSION ${KF_VERSION}
|
||||
ORG_DOMAIN org.kde
|
||||
SOURCES # using only public headers, to cover only public API
|
||||
${KDBusAddons_HEADERS}
|
||||
MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
|
||||
LINK_QCHS
|
||||
Qt6DBus_QCH
|
||||
Qt6Core_QCH
|
||||
INCLUDE_DIRS
|
||||
${CMAKE_CURRENT_BINARY_DIR}
|
||||
BLANK_MACROS
|
||||
KDBUSADDONS_EXPORT
|
||||
KDBUSADDONS_DEPRECATED
|
||||
TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
|
||||
COMPONENT Devel
|
||||
)
|
||||
endif()
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
#!/bin/sh
|
||||
|
||||
# Extract strings from all source files.
|
||||
# EXTRACT_TR_STRINGS extracts strings with lupdate and convert them to .pot with
|
||||
# lconvert.
|
||||
$EXTRACT_TR_STRINGS `find . -name \*.cpp -o -name \*.h -o -name \*.ui -o -name \*.qml` -o $podir/kdbusaddons6_qt.pot
|
||||
@@ -0,0 +1 @@
|
||||
#cmakedefine01 HAVE_X11
|
||||
@@ -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, ®istrationLoop, &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"
|
||||
@@ -0,0 +1,297 @@
|
||||
/*
|
||||
This file is part of libkdbusaddons
|
||||
|
||||
SPDX-FileCopyrightText: 2011 David Faure <faure@kde.org>
|
||||
SPDX-FileCopyrightText: 2011 Kevin Ottens <ervin@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
|
||||
*/
|
||||
|
||||
#ifndef KDBUSSERVICE_H
|
||||
#define KDBUSSERVICE_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QUrl>
|
||||
#include <memory>
|
||||
|
||||
#include <kdbusaddons_export.h>
|
||||
|
||||
class KDBusServicePrivate;
|
||||
|
||||
/**
|
||||
* @class KDBusService kdbusservice.h <KDBusService>
|
||||
*
|
||||
* KDBusService takes care of registering the current process with D-Bus.
|
||||
*
|
||||
* This registers the application at a predictable location on D-Bus, registers
|
||||
* the QCoreApplication (or subclass) object at /MainApplication, and
|
||||
* assists in implementing the application side of D-Bus activation from
|
||||
* the <a
|
||||
* href="http://standards.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html">Desktop
|
||||
* Entry Specification</a>.
|
||||
*
|
||||
* An application can either work in Multiple mode or Unique mode.
|
||||
*
|
||||
* In Multiple mode, the application can be launched many times. The service
|
||||
* name in the D-Bus registration will contain the PID to distinguish the
|
||||
* various instances; for example: <tt>org.kde.konqueror-12345</tt>.
|
||||
*
|
||||
* In Unique mode, only one instance of this application can ever run.
|
||||
* The first instance of the application registers with D-Bus without the PID,
|
||||
* and any attempt to run the application again will cause the
|
||||
* activateRequested() signal to be emitted in the already-running instance; the
|
||||
* duplicate instance will then quit. The exit value can be set by the already
|
||||
* running instance with setExitValue(), the default value is @c 0.
|
||||
*
|
||||
* Unique-mode applications should usually delay parsing command-line arguments
|
||||
* until after creating a KDBusService object; that way they know they are the
|
||||
* original instance of the application.
|
||||
*
|
||||
* Applications that set the D-Bus activation entry (DBusActivatable=true) in
|
||||
* their desktop files will use Unique mode and connect to the signals emitted
|
||||
* by this class.
|
||||
* Note that the D-Bus interface is exported for Multiple-mode applications as
|
||||
* well, so it also makes sense for such applications to connect to the signals
|
||||
* emitted by this class.
|
||||
*
|
||||
* @note In order to avoid a race, the application should export its objects to
|
||||
* D-Bus before allowing the event loop to run (for example, by calling
|
||||
* QCoreApplication::exec()). Otherwise, the application will appear on the bus
|
||||
* before its objects are accessible via D-Bus, which could be a problem for
|
||||
* other applications or scripts which start the application in order to talk
|
||||
* D-Bus to it immediately.
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* @code
|
||||
QApplication app(argc, argv);
|
||||
app.setApplicationName("kuiserver");
|
||||
app.setOrganizationDomain("kde.org");
|
||||
// Create your D-Bus objects here
|
||||
// ...
|
||||
KDBusService service(KDBusService::Unique);
|
||||
// If this point is reached, this is the only running instance
|
||||
// i.e. org.kde.kuiserver has been registered
|
||||
return app.exec();
|
||||
* @endcode
|
||||
*
|
||||
* @since 5.0
|
||||
*/
|
||||
class KDBUSADDONS_EXPORT KDBusService : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
/**
|
||||
* Options to control the behaviour of KDBusService
|
||||
* @see StartupOptions
|
||||
*/
|
||||
enum StartupOption {
|
||||
/**
|
||||
* Indicates that only one instance of this application should ever exist.
|
||||
*
|
||||
* Cannot be combined with @c Multiple.
|
||||
*/
|
||||
Unique = 1,
|
||||
/**
|
||||
* Indicates that multiple instances of the application may exist.
|
||||
*
|
||||
* Cannot be combined with @c Unique. This is the default.
|
||||
*/
|
||||
Multiple = 2,
|
||||
/** Indicates that the application should not exit if it failed to
|
||||
* register with D-Bus.
|
||||
*
|
||||
* If not set, KDBusService will quit the application if it failed to
|
||||
* register the service with D-Bus or a @c Unique instance can not be
|
||||
* activated. A @c Multiple instance will exit with error code @c 1.
|
||||
* The exit value of a @c Unique instance can be set from the running
|
||||
* instance with setExitValue(), the default value is @c 0.
|
||||
*/
|
||||
NoExitOnFailure = 4,
|
||||
/**
|
||||
* Indicates that if there's already a unique service running, to be quit and replaced
|
||||
* with our own.
|
||||
*
|
||||
* If exported, it will try first quitting the service calling @c org.qtproject.Qt.QCoreApplication.quit,
|
||||
* which is exported by KDBusService by default.
|
||||
*
|
||||
* @since 5.65
|
||||
*/
|
||||
Replace = 8
|
||||
};
|
||||
Q_ENUM(StartupOption)
|
||||
|
||||
/**
|
||||
* Stores a combination of #StartupOption values.
|
||||
*/
|
||||
Q_DECLARE_FLAGS(StartupOptions, StartupOption)
|
||||
Q_FLAG(StartupOptions)
|
||||
|
||||
/**
|
||||
* Tries to register the current process to D-Bus at an address based on the
|
||||
* application name and organization domain.
|
||||
*
|
||||
* The D-Bus service name is the reversed organization domain, followed by
|
||||
* the application name. If @p options includes the @c Multiple flag, the
|
||||
* application PID will be appended. For example,
|
||||
* @code
|
||||
* app.setApplicationName("kuiserver");
|
||||
* app.setOrganizationDomain("kde.org");
|
||||
* @endcode
|
||||
* will make KDBusService register as @c org.kde.kuiserver in @c Unique
|
||||
* mode, and @c org.kde.kuiserver-1234 (if the process has PID @c 1234) in
|
||||
* @c Multiple mode.
|
||||
*/
|
||||
explicit KDBusService(StartupOptions options = Multiple, QObject *parent = nullptr);
|
||||
|
||||
/**
|
||||
* Destroys this object (but does not unregister the application).
|
||||
*
|
||||
* Deleting this object before unregister() could confuse
|
||||
* clients, who will see the service on the bus but will be unable to use
|
||||
* the activation methods.
|
||||
*/
|
||||
~KDBusService() override;
|
||||
|
||||
/**
|
||||
* Returns true if the D-Bus registration succeeded.
|
||||
*
|
||||
* Note that this is only useful when specifying the option NoExitOnFailure.
|
||||
* Otherwise, the simple fact that this process is still running indicates
|
||||
* that the registration succeeded.
|
||||
*/
|
||||
bool isRegistered() const;
|
||||
|
||||
/**
|
||||
* Returns the name of the D-Bus service registered by this class.
|
||||
* Mostly useful when using the option Multiple.
|
||||
* @since 5.33
|
||||
*/
|
||||
QString serviceName() const;
|
||||
|
||||
/**
|
||||
* Returns the error message from the D-Bus registration if it failed.
|
||||
*
|
||||
* Note that this is only useful when specifying the option NoExitOnFailure.
|
||||
* Otherwise the process has quit by the time you can get a chance to call this.
|
||||
*/
|
||||
QString errorMessage() const;
|
||||
|
||||
/**
|
||||
* Sets the exit value to be used for a duplicate instance.
|
||||
*
|
||||
* If this is a @c Unique application, a slot connected to
|
||||
* activateRequested() can use this to specify a non-zero exit value for the
|
||||
* duplicate instance. This would typically be done if invalid command-line
|
||||
* arguments are passed.
|
||||
*
|
||||
* Note that this will only work if the signal-slot connection type is
|
||||
* Qt::DirectConnection.
|
||||
*
|
||||
* @param value The exit value for the duplicate instance.
|
||||
*/
|
||||
void setExitValue(int value);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Signals that the application is to be activated.
|
||||
*
|
||||
* If this is a @c Unique application, when KDBusService is constructed in
|
||||
* subsequent instances of the application (ie: when the executable is run
|
||||
* when an instance is already running), it will cause this signal to be
|
||||
* emitted in the already-running instance (with the arguments passed to the
|
||||
* duplicate instance), and the duplicate instance will then exit.
|
||||
*
|
||||
* If this application's desktop file indicates that it supports D-Bus
|
||||
* activation (DBusActivatable=true), a command launcher may also call the Activate()
|
||||
* D-Bus method to trigger this signal. In this case, @p args will be empty.
|
||||
*
|
||||
* In single-window applications, the connected signal should typically
|
||||
* raise the window.
|
||||
*
|
||||
* @param arguments The arguments the executable was called with, starting with the executable file name.
|
||||
* See QCoreApplication::arguments().
|
||||
* This can also be empty.
|
||||
*
|
||||
* A typical implementation of the signal handler would be:
|
||||
* @code
|
||||
* if (!arguments.isEmpty()) {
|
||||
* commandLineParser->parse(arguments); // same QCommandLineParser instance as the one used in main()
|
||||
* handleCmdLine(workingDirectory); // shared method with main(), which uses commandLineParser to handle options and positional arguments
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* For GUI applications, the handler also needs to deal with any platform-specific startup ids
|
||||
* and make sure the mainwindow is shown as well as request its activation from the window manager.
|
||||
* For X11, KDBusService makes the id for the Startup Notification protocol available
|
||||
* from QX11Info::nextStartupId(), if there is one.
|
||||
* For Wayland, KDBusService provides the token for the XDG Activation protocol in the
|
||||
* "XDG_ACTIVATION_TOKEN" environment variable and unsets it again after the signal, if there is one.
|
||||
* The util method @c KWindowSystem::updateStartupId(QWindow *window) (since KF 5.91) takes care of that.
|
||||
* A typical implementation in the signal handler would be:
|
||||
* @code
|
||||
* mainWindow->show();
|
||||
* KWindowSystem::updateStartupId(mainWindow->windowHandle());
|
||||
* mainWindow->raise();
|
||||
* KWindowSystem::activateWindow(mainWindow->windowHandle());
|
||||
* @endcode
|
||||
*
|
||||
* If you're using the builtin handling of @c --help and @c --version in QCommandLineParser,
|
||||
* you should call parser.process(arguments) before creating the KDBusService instance,
|
||||
* since parse() doesn't handle those (and exiting the already-running instance would be wrong anyway).
|
||||
*
|
||||
* @see setExitValue()
|
||||
*/
|
||||
void activateRequested(const QStringList &arguments, const QString &workingDirectory);
|
||||
|
||||
/**
|
||||
* Signals that one or more files should be opened in the application.
|
||||
*
|
||||
* This signal is emitted to request handling of the respective method of the D-Bus activation.
|
||||
* For GUI applications, the signal handler also needs to deal with any platform-specific startup ids
|
||||
* and make sure the mainwindow is shown as well as request its activation from the window manager.
|
||||
* See documentation of activateRequested(const QStringList &arguments, const QString &)
|
||||
* for details.
|
||||
*
|
||||
* @param uris The URLs of the files to open.
|
||||
*/
|
||||
void openRequested(const QList<QUrl> &uris);
|
||||
|
||||
/**
|
||||
* Signals that an application action should be triggered.
|
||||
*
|
||||
* This signal is emitted to request handling of the respective method of the D-Bus activation.
|
||||
* For GUI applications, the signal handler also needs to deal with any platform-specific startup ids
|
||||
* and make sure the mainwindow is shown as well as request its activation from the window manager.
|
||||
* See documentation of activateRequested(const QStringList &arguments, const QString &)
|
||||
* for details.
|
||||
*
|
||||
* See the desktop entry specification for more information about action activation.
|
||||
*/
|
||||
void activateActionRequested(const QString &actionName, const QVariant ¶meter);
|
||||
|
||||
public Q_SLOTS:
|
||||
/**
|
||||
* Manually unregister the given serviceName from D-Bus.
|
||||
*/
|
||||
void unregister();
|
||||
|
||||
private:
|
||||
// fdo.Application spec
|
||||
KDBUSADDONS_NO_EXPORT void Activate(const QVariantMap &platform_data);
|
||||
KDBUSADDONS_NO_EXPORT void Open(const QStringList &uris, const QVariantMap &platform_data);
|
||||
KDBUSADDONS_NO_EXPORT void ActivateAction(const QString &action_name, const QVariantList &maybeParameter, const QVariantMap &platform_data);
|
||||
friend class KDBusServiceAdaptor;
|
||||
|
||||
// org.kde.KDBusService
|
||||
KDBUSADDONS_NO_EXPORT int CommandLine(const QStringList &arguments, const QString &workingDirectory, const QVariantMap &platform_data);
|
||||
friend class KDBusServiceExtensionsAdaptor;
|
||||
|
||||
private:
|
||||
std::unique_ptr<KDBusServicePrivate> const d;
|
||||
};
|
||||
|
||||
Q_DECLARE_OPERATORS_FOR_FLAGS(KDBusService::StartupOptions)
|
||||
|
||||
#endif /* KDBUSSERVICE_H */
|
||||
@@ -0,0 +1,104 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kdedmodule.h"
|
||||
#include "kdbusaddons_debug.h"
|
||||
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMessage>
|
||||
#include <QDBusObjectPath>
|
||||
|
||||
class KDEDModulePrivate
|
||||
{
|
||||
public:
|
||||
QString moduleName;
|
||||
};
|
||||
|
||||
KDEDModule::KDEDModule(QObject *parent)
|
||||
: QObject(parent)
|
||||
, d(new KDEDModulePrivate)
|
||||
{
|
||||
}
|
||||
|
||||
KDEDModule::~KDEDModule()
|
||||
{
|
||||
}
|
||||
|
||||
void KDEDModule::setModuleName(const QString &name)
|
||||
{
|
||||
d->moduleName = name;
|
||||
QDBusObjectPath realPath(QLatin1String("/modules/") + d->moduleName);
|
||||
|
||||
if (realPath.path().isEmpty()) {
|
||||
qCWarning(KDBUSADDONS_LOG) << "The kded module name" << name << "is invalid!";
|
||||
return;
|
||||
}
|
||||
|
||||
QDBusConnection::RegisterOptions regOptions;
|
||||
|
||||
if (this->metaObject()->indexOfClassInfo("D-Bus Interface") != -1) {
|
||||
// 1. There are kded modules that don't have a D-Bus interface.
|
||||
// 2. qt 4.4.3 crashes when trying to emit signals on class without
|
||||
// Q_CLASSINFO("D-Bus Interface", "<your interface>") but
|
||||
// ExportSignal set.
|
||||
// We try to solve that for now with just registering Properties and
|
||||
// Adaptors. But we should investigate where the sense is in registering
|
||||
// the module at all. Just for autoload? Is there a better solution?
|
||||
regOptions = QDBusConnection::ExportScriptableContents | QDBusConnection::ExportAdaptors;
|
||||
} else {
|
||||
// Full functional module. Register everything.
|
||||
regOptions = QDBusConnection::ExportScriptableSlots //
|
||||
| QDBusConnection::ExportScriptableProperties //
|
||||
| QDBusConnection::ExportAdaptors;
|
||||
qCDebug(KDBUSADDONS_LOG) << "Registration of kded module" << d->moduleName << "without D-Bus interface.";
|
||||
}
|
||||
|
||||
if (!QDBusConnection::sessionBus().registerObject(realPath.path(), this, regOptions)) {
|
||||
// Happens for khotkeys but the module works. Need some time to investigate.
|
||||
qCDebug(KDBUSADDONS_LOG) << "registerObject() returned false for" << d->moduleName;
|
||||
} else {
|
||||
// qCDebug(KDBUSADDONS_LOG) << "registerObject() successful for" << d->moduleName;
|
||||
// Fix deadlock with Qt 5.6: this has to be delayed until the dbus thread is unlocked
|
||||
auto registeredSignal = [this, realPath]() {
|
||||
moduleRegistered(realPath);
|
||||
};
|
||||
QMetaObject::invokeMethod(this, registeredSignal, Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
QString KDEDModule::moduleName() const
|
||||
{
|
||||
return d->moduleName;
|
||||
}
|
||||
|
||||
static const char s_modules_path[] = "/modules/";
|
||||
|
||||
QString KDEDModule::moduleForMessage(const QDBusMessage &message)
|
||||
{
|
||||
if (message.type() != QDBusMessage::MethodCallMessage) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
QString obj = message.path();
|
||||
if (!obj.startsWith(QLatin1String(s_modules_path))) {
|
||||
return QString();
|
||||
}
|
||||
|
||||
// Remove the <s_modules_path> part
|
||||
obj = obj.mid(strlen(s_modules_path));
|
||||
|
||||
// Remove the part after the modules name
|
||||
const int index = obj.indexOf(QLatin1Char('/'));
|
||||
if (index != -1) {
|
||||
obj = obj.left(index);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
#include "moc_kdedmodule.cpp"
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
This file is part of the KDE libraries
|
||||
|
||||
SPDX-FileCopyrightText: 2001 Waldo Bastian <bastian@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
|
||||
*/
|
||||
#ifndef __KDEDMODULE_H__
|
||||
#define __KDEDMODULE_H__
|
||||
|
||||
#include <kdbusaddons_export.h>
|
||||
|
||||
#include <QObject>
|
||||
#include <memory>
|
||||
|
||||
class KDEDModulePrivate;
|
||||
class Kded;
|
||||
|
||||
class QDBusObjectPath;
|
||||
class QDBusMessage;
|
||||
|
||||
/**
|
||||
* \class KDEDModule kdedmodule.h <KDEDModule>
|
||||
*
|
||||
* The base class for KDED modules.
|
||||
*
|
||||
* KDED modules are constructed as shared libraries that are loaded on-demand
|
||||
* into the kded daemon at runtime.
|
||||
*
|
||||
* See https://invent.kde.org/frameworks/kded/-/blob/master/docs/HOWTO
|
||||
* for documentation about writing kded modules.
|
||||
*
|
||||
* @author Waldo Bastian <bastian@kde.org>
|
||||
*/
|
||||
class KDBUSADDONS_EXPORT KDEDModule : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_CLASSINFO("D-Bus Interface", "org.kde.KDEDModule")
|
||||
|
||||
friend class Kded;
|
||||
|
||||
public:
|
||||
/**
|
||||
* Constructor
|
||||
*/
|
||||
explicit KDEDModule(QObject *parent = nullptr);
|
||||
|
||||
~KDEDModule() override;
|
||||
|
||||
/**
|
||||
* Sets the name of the module, and uses it to register the module to D-Bus.
|
||||
*
|
||||
* For modules loaded as plugins by a daemon, this is called automatically
|
||||
* by the daemon after loading the module. Module authors should NOT call this.
|
||||
*/
|
||||
void setModuleName(const QString &name);
|
||||
|
||||
QString moduleName() const;
|
||||
|
||||
/**
|
||||
* Returns the module being called by this D-Bus message.
|
||||
* Useful for autoloading modules in kded and similar daemons.
|
||||
* @since 5.7
|
||||
*/
|
||||
static QString moduleForMessage(const QDBusMessage &message);
|
||||
|
||||
Q_SIGNALS:
|
||||
/**
|
||||
* Emitted when a mainwindow registers itself.
|
||||
*/
|
||||
void windowRegistered(qlonglong windowId);
|
||||
|
||||
/**
|
||||
* Emitted when a mainwindow unregisters itself.
|
||||
*/
|
||||
void windowUnregistered(qlonglong windowId);
|
||||
|
||||
/**
|
||||
* Emitted after the module is registered successfully with D-Bus
|
||||
*
|
||||
* @since 4.2
|
||||
*/
|
||||
void moduleRegistered(const QDBusObjectPath &path);
|
||||
|
||||
private:
|
||||
std::unique_ptr<KDEDModulePrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,158 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>
|
||||
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include "kupdatelaunchenvironmentjob.h"
|
||||
|
||||
#include <QDBusArgument>
|
||||
#include <QDBusConnection>
|
||||
#include <QDBusMetaType>
|
||||
#include <QDBusPendingReply>
|
||||
|
||||
#include <QTimer>
|
||||
|
||||
#include "kdbusaddons_debug.h"
|
||||
|
||||
class KUpdateLaunchEnvironmentJobPrivate
|
||||
{
|
||||
public:
|
||||
explicit KUpdateLaunchEnvironmentJobPrivate(KUpdateLaunchEnvironmentJob *q);
|
||||
void monitorReply(const QDBusPendingReply<> &reply);
|
||||
|
||||
static bool isPosixName(const QString &name);
|
||||
static bool isSystemdApprovedValue(const QString &value);
|
||||
|
||||
KUpdateLaunchEnvironmentJob *q;
|
||||
QProcessEnvironment environment;
|
||||
int pendingReplies = 0;
|
||||
};
|
||||
|
||||
KUpdateLaunchEnvironmentJobPrivate::KUpdateLaunchEnvironmentJobPrivate(KUpdateLaunchEnvironmentJob *q)
|
||||
: q(q)
|
||||
{
|
||||
}
|
||||
|
||||
void KUpdateLaunchEnvironmentJobPrivate::monitorReply(const QDBusPendingReply<> &reply)
|
||||
{
|
||||
++pendingReplies;
|
||||
|
||||
auto *watcher = new QDBusPendingCallWatcher(reply, q);
|
||||
QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, [this](QDBusPendingCallWatcher *watcher) {
|
||||
watcher->deleteLater();
|
||||
--pendingReplies;
|
||||
|
||||
if (pendingReplies == 0) {
|
||||
Q_EMIT q->finished();
|
||||
q->deleteLater();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
KUpdateLaunchEnvironmentJob::KUpdateLaunchEnvironmentJob(const QProcessEnvironment &environment)
|
||||
: d(new KUpdateLaunchEnvironmentJobPrivate(this))
|
||||
{
|
||||
d->environment = environment;
|
||||
QTimer::singleShot(0, this, &KUpdateLaunchEnvironmentJob::start);
|
||||
}
|
||||
|
||||
KUpdateLaunchEnvironmentJob::~KUpdateLaunchEnvironmentJob() = default;
|
||||
|
||||
void KUpdateLaunchEnvironmentJob::start()
|
||||
{
|
||||
qDBusRegisterMetaType<QMap<QString, QString>>();
|
||||
QMap<QString, QString> dbusActivationEnv;
|
||||
QStringList systemdUpdates;
|
||||
|
||||
for (const auto &varName : d->environment.keys()) {
|
||||
if (!KUpdateLaunchEnvironmentJobPrivate::isPosixName(varName)) {
|
||||
qCWarning(KDBUSADDONS_LOG) << "Skipping syncing of environment variable " << varName << "as name contains unsupported characters";
|
||||
continue;
|
||||
}
|
||||
const QString value = d->environment.value(varName);
|
||||
|
||||
// plasma-session
|
||||
QDBusMessage plasmaSessionMsg = QDBusMessage::createMethodCall(QStringLiteral("org.kde.Startup"),
|
||||
QStringLiteral("/Startup"),
|
||||
QStringLiteral("org.kde.Startup"),
|
||||
QStringLiteral("updateLaunchEnv"));
|
||||
plasmaSessionMsg.setArguments({QVariant::fromValue(varName), QVariant::fromValue(value)});
|
||||
auto plasmaSessionReply = QDBusConnection::sessionBus().asyncCall(plasmaSessionMsg);
|
||||
d->monitorReply(plasmaSessionReply);
|
||||
|
||||
// DBus-activation environment
|
||||
dbusActivationEnv.insert(varName, value);
|
||||
|
||||
// _user_ systemd env
|
||||
// Systemd has stricter parsing of valid environment variables
|
||||
// https://github.com/systemd/systemd/issues/16704
|
||||
// validate here
|
||||
if (!KUpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(value)) {
|
||||
qCWarning(KDBUSADDONS_LOG) << "Skipping syncing of environment variable " << varName << "as value contains unsupported characters";
|
||||
continue;
|
||||
}
|
||||
const QString updateString = varName + QStringLiteral("=") + value;
|
||||
systemdUpdates.append(updateString);
|
||||
}
|
||||
|
||||
// DBus-activation environment
|
||||
QDBusMessage dbusActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.DBus"),
|
||||
QStringLiteral("/org/freedesktop/DBus"),
|
||||
QStringLiteral("org.freedesktop.DBus"),
|
||||
QStringLiteral("UpdateActivationEnvironment"));
|
||||
dbusActivationMsg.setArguments({QVariant::fromValue(dbusActivationEnv)});
|
||||
|
||||
auto dbusActivationReply = QDBusConnection::sessionBus().asyncCall(dbusActivationMsg);
|
||||
d->monitorReply(dbusActivationReply);
|
||||
|
||||
// _user_ systemd env
|
||||
QDBusMessage systemdActivationMsg = QDBusMessage::createMethodCall(QStringLiteral("org.freedesktop.systemd1"),
|
||||
QStringLiteral("/org/freedesktop/systemd1"),
|
||||
QStringLiteral("org.freedesktop.systemd1.Manager"),
|
||||
QStringLiteral("SetEnvironment"));
|
||||
systemdActivationMsg.setArguments({systemdUpdates});
|
||||
|
||||
auto systemdActivationReply = QDBusConnection::sessionBus().asyncCall(systemdActivationMsg);
|
||||
d->monitorReply(systemdActivationReply);
|
||||
}
|
||||
|
||||
bool KUpdateLaunchEnvironmentJobPrivate::isPosixName(const QString &name)
|
||||
{
|
||||
// Posix says characters like % should be 'tolerated', but it gives issues in practice.
|
||||
// https://bugzilla.redhat.com/show_bug.cgi?id=1754395
|
||||
// https://bugzilla.redhat.com/show_bug.cgi?id=1879216
|
||||
// Ensure systemd compat by only allowing alphanumerics and _ in names.
|
||||
bool first = true;
|
||||
for (const QChar c : name) {
|
||||
if (first && !c.isLetter() && c != QLatin1Char('_')) {
|
||||
return false;
|
||||
} else if (first) {
|
||||
first = false;
|
||||
} else if (!c.isLetterOrNumber() && c != QLatin1Char('_')) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return !first;
|
||||
}
|
||||
|
||||
bool KUpdateLaunchEnvironmentJobPrivate::isSystemdApprovedValue(const QString &value)
|
||||
{
|
||||
// systemd code checks that a value contains no control characters except \n \t
|
||||
// effectively copied from systemd's string_has_cc
|
||||
for (const char &it : value.toLatin1()) {
|
||||
if (it == QLatin1Char('\n') || it == QLatin1Char('\t')) {
|
||||
continue;
|
||||
}
|
||||
if (it > 0 && it < ' ') {
|
||||
return false;
|
||||
}
|
||||
if (it == 127) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#include "moc_kupdatelaunchenvironmentjob.cpp"
|
||||
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2020 Kai Uwe Broulik <kde@broulik.de>
|
||||
SPDX-FileCopyrightText: 2021 David Edmundson <davidedmundson@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#ifndef KUPDATELAUNCHENVIRONMENTJOB_H
|
||||
#define KUPDATELAUNCHENVIRONMENTJOB_H
|
||||
|
||||
#include <kdbusaddons_export.h>
|
||||
|
||||
#include <QProcessEnvironment>
|
||||
|
||||
#include <memory>
|
||||
|
||||
class QString;
|
||||
class KUpdateLaunchEnvironmentJobPrivate;
|
||||
|
||||
/**
|
||||
* @class KUpdateLaunchEnvironmentJob updatelaunchenvironmentjob.h <KUpdateLaunchEnvironmentJob>
|
||||
*
|
||||
* Job for updating the launch environment.
|
||||
*
|
||||
* This job adds or updates an environment variable in process environment that will be used
|
||||
* when a process is launched:
|
||||
* This includes:
|
||||
* - DBus activation
|
||||
* - Systemd units
|
||||
* - Plasma-session
|
||||
*
|
||||
* Environment variables are sanitized before uploading.
|
||||
*
|
||||
* This object deletes itself after completion, similar to KJobs
|
||||
*
|
||||
* Porting from KF5 to KF6:
|
||||
*
|
||||
* The class UpdateLaunchEnvironmentJob was renamed to KUpdateLaunchEnvironmentJob.
|
||||
*
|
||||
* @since 6.0
|
||||
*/
|
||||
class KDBUSADDONS_EXPORT KUpdateLaunchEnvironmentJob : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit KUpdateLaunchEnvironmentJob(const QProcessEnvironment &environment);
|
||||
~KUpdateLaunchEnvironmentJob() override;
|
||||
|
||||
Q_SIGNALS:
|
||||
void finished();
|
||||
|
||||
private:
|
||||
KDBUSADDONS_NO_EXPORT void start();
|
||||
|
||||
private:
|
||||
std::unique_ptr<KUpdateLaunchEnvironmentJobPrivate> const d;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,20 @@
|
||||
<!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.freedesktop.Application'>
|
||||
<method name='Activate'>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In0" value="QVariantMap"/>
|
||||
<arg type='a{sv}' name='platform-data' direction='in'/>
|
||||
</method>
|
||||
<method name='Open'>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In1" value="QVariantMap"/>
|
||||
<arg type='as' name='uris' direction='in'/>
|
||||
<arg type='a{sv}' name='platform-data' direction='in'/>
|
||||
</method>
|
||||
<method name='ActivateAction'>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/>
|
||||
<arg type='s' name='action_name' direction='in'/>
|
||||
<arg type='av' name='parameter' direction='in'/>
|
||||
<arg type='a{sv}' name='platform-data' direction='in'/>
|
||||
</method>
|
||||
</interface>
|
||||
</node>
|
||||
@@ -0,0 +1,15 @@
|
||||
<!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.KDBusService'>
|
||||
<method name='CommandLine'>
|
||||
<arg type='as' name='arguments' direction='in'/>
|
||||
<arg type='s' name='working-dir' direction='in'/>
|
||||
<arg type='a{sv}' name='platform-data' direction='in' />
|
||||
<arg type='i' name='exit-status' direction='out'/>
|
||||
<annotation name="org.qtproject.QtDBus.QtTypeName.In2" value="QVariantMap"/>
|
||||
</method>
|
||||
<!--
|
||||
<property name='Busy' type='b' access='read'/>
|
||||
-->
|
||||
</interface>
|
||||
</node>
|
||||
@@ -0,0 +1,4 @@
|
||||
add_executable(kquitapp6 kquitapp.cpp)
|
||||
ecm_mark_nongui_executable(kquitapp6)
|
||||
target_link_libraries(kquitapp6 Qt6::DBus)
|
||||
install(TARGETS kquitapp6 ${KF_INSTALL_TARGETS_DEFAULT_ARGS})
|
||||
@@ -0,0 +1,57 @@
|
||||
/*
|
||||
SPDX-FileCopyrightText: 2006 Aaron Seigo <aseigo@kde.org>
|
||||
|
||||
SPDX-License-Identifier: LGPL-2.0-or-later
|
||||
*/
|
||||
|
||||
#include <QCommandLineParser>
|
||||
#include <QCoreApplication>
|
||||
#include <QDBusInterface>
|
||||
#include <QDebug>
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
QCoreApplication app(argc, argv);
|
||||
app.setApplicationName(QStringLiteral("kquitapp"));
|
||||
app.setApplicationVersion(QStringLiteral("2.0"));
|
||||
|
||||
QCommandLineParser parser;
|
||||
parser.setApplicationDescription(QCoreApplication::translate("main", "Quit a D-Bus enabled application easily"));
|
||||
parser.addOption(QCommandLineOption(QStringLiteral("service"),
|
||||
QCoreApplication::translate("main", "Full service name, overrides application name provided"),
|
||||
QStringLiteral("service")));
|
||||
parser.addOption(QCommandLineOption(QStringLiteral("path"),
|
||||
QCoreApplication::translate("main", "Path in the D-Bus interface to use"),
|
||||
QStringLiteral("path"),
|
||||
QStringLiteral("/MainApplication")));
|
||||
parser.addPositionalArgument(QStringLiteral("[application]"), QCoreApplication::translate("main", "The name of the application to quit"));
|
||||
parser.addHelpOption();
|
||||
parser.addVersionOption();
|
||||
parser.process(app);
|
||||
|
||||
QString service;
|
||||
if (parser.isSet(QStringLiteral("service"))) {
|
||||
service = parser.value(QStringLiteral("service"));
|
||||
} else if (!parser.positionalArguments().isEmpty()) {
|
||||
service = QStringLiteral("org.kde.%1").arg(parser.positionalArguments().at(0));
|
||||
} else {
|
||||
parser.showHelp(1);
|
||||
}
|
||||
|
||||
QString path(parser.value(QStringLiteral("path")));
|
||||
|
||||
QDBusInterface interface(service, path);
|
||||
if (!interface.isValid()) {
|
||||
qWarning() << QCoreApplication::translate("main", "Application %1 could not be found using service %2 and path %3.")
|
||||
.arg(parser.positionalArguments().at(0), service, path);
|
||||
return 1;
|
||||
}
|
||||
interface.call(QStringLiteral("quit"));
|
||||
QDBusError error = interface.lastError();
|
||||
if (error.type() != QDBusError::NoError) {
|
||||
qWarning() << QCoreApplication::translate("main", "Quitting application %1 failed. Error reported was:\n\n %2 : %3")
|
||||
.arg(parser.positionalArguments().join(QStringLiteral(" ")), error.name(), error.message());
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user