KIO

dbusactivationrunner.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: 2020-2021 David Redondo <kde@david-redondo.de>
4
5 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
6*/
7
8#include "dbusactivationrunner_p.h"
9
10#include "kiogui_debug.h"
11#include <KWindowSystem>
12
13#ifndef Q_OS_ANDROID
14#include <QDBusConnection>
15#include <QDBusConnectionInterface>
16#include <QDBusMessage>
17#include <QDBusPendingCallWatcher>
18#endif
19#include <QTimer>
20
21bool DBusActivationRunner::activationPossible(const KService::Ptr service, KIO::ApplicationLauncherJob::RunFlags flags, const QString &suggestedFileName)
22{
23#if defined Q_OS_UNIX && !defined Q_OS_ANDROID
24 if (!service->isApplication()) {
25 return false;
26 }
27 if (service->property<bool>(QStringLiteral("DBusActivatable"))) {
28 if (service->desktopEntryName().count(QLatin1Char('.')) < 2) {
29 qCWarning(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "doesn't have enough '.' for a well-formed service name";
30 return false;
31 }
32 if (!suggestedFileName.isEmpty()) {
33 qCDebug(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "because suggestedFileName is set";
34 return false;
35 }
37 qCDebug(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "because DeleteTemporaryFiles is set";
38 return false;
39 }
40 return true;
41 }
42#endif
43 return false;
44}
45
46DBusActivationRunner::DBusActivationRunner(const QString &action)
47 : KProcessRunner()
48 , m_actionName(action)
49{
50}
51
52void DBusActivationRunner::startProcess()
53{
54#ifndef Q_OS_ANDROID
55 // DBusActivatable as per https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus
56 const QString objectPath = QStringLiteral("/%1").arg(m_desktopName).replace(QLatin1Char('.'), QLatin1Char('/')).replace(QLatin1Char('-'), QLatin1Char('_'));
57 const QString interface = QStringLiteral("org.freedesktop.Application");
58 QDBusMessage message;
59 if (m_urls.isEmpty()) {
60 if (m_actionName.isEmpty()) {
61 message = QDBusMessage::createMethodCall(m_desktopName, objectPath, interface, QStringLiteral("Activate"));
62 } else {
63 message = QDBusMessage::createMethodCall(m_desktopName, objectPath, interface, QStringLiteral("ActivateAction"));
64 message << m_actionName << QVariantList();
65 }
66 } else {
67 message = QDBusMessage::createMethodCall(m_desktopName, objectPath, interface, QStringLiteral("Open"));
68 message << QUrl::toStringList(m_urls);
69 }
71#if HAVE_X11
72 message << QVariantMap{{QStringLiteral("desktop-startup-id"), m_startupId.id()}};
73#endif
75 message << QVariantMap{{QStringLiteral("activation-token"), m_process->processEnvironment().value(QStringLiteral("XDG_ACTIVATION_TOKEN"))}};
76 }
77 auto call = QDBusConnection::sessionBus().asyncCall(message);
78 auto activationWatcher = new QDBusPendingCallWatcher(call, this);
79 connect(activationWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
80 watcher->deleteLater();
81 if (watcher->isError()) {
82 Q_EMIT error(watcher->error().message());
83 terminateStartupNotification();
84 m_finished = true;
85 deleteLater();
86 return;
87 }
88 auto call = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("GetConnectionUnixProcessID"), m_desktopName);
89 auto pidWatcher = new QDBusPendingCallWatcher(call, this);
90 connect(pidWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) {
91 m_finished = true;
92 QDBusPendingReply<uint> reply = *watcher;
93 if (reply.isError()) {
94 Q_EMIT error(watcher->error().message());
95 terminateStartupNotification();
96 } else {
97 Q_EMIT processStarted(reply.value());
98 }
99 deleteLater();
100 });
101 });
102#endif
103}
104
105bool DBusActivationRunner::waitForStarted(int timeout)
106{
107#ifndef Q_OS_ANDROID
108 if (m_finished) {
109 return m_pid != 0;
110 }
111
112 QEventLoop loop;
113 bool success = false;
114 connect(this, &KProcessRunner::processStarted, [&loop, &success]() {
115 loop.quit();
116 success = true;
117 });
118 connect(this, &KProcessRunner::error, &loop, &QEventLoop::quit);
119 QTimer::singleShot(timeout, &loop, &QEventLoop::quit);
120 loop.exec();
121 return success;
122#else
123 return false;
124#endif
125}
126
127#include "moc_dbusactivationrunner_p.cpp"
@ DeleteTemporaryFiles
the URLs passed to the service will be deleted when it exits (if the URLs are local files)
QString desktopEntryName() const
T property(const QString &name) const
bool isApplication() const
static bool isPlatformX11()
static bool isPlatformWayland()
QDBusPendingCall asyncCall(const QString &method, Args &&... args)
QDBusPendingCall asyncCall(const QDBusMessage &message, int timeout) const const
QDBusConnectionInterface * interface() const const
QDBusConnection sessionBus()
QDBusMessage createMethodCall(const QString &service, const QString &path, const QString &interface, const QString &method)
void finished(QDBusPendingCallWatcher *self)
bool isError() const const
int exec(ProcessEventsFlags flags)
void quit()
void deleteLater()
qsizetype count() const const
QString arg(Args &&... args) const const
bool isEmpty() const const
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
QStringList toStringList(const QList< QUrl > &urls, FormattingOptions options)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:52 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.