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#include <QDBusConnection>
14#include <QDBusConnectionInterface>
15#include <QDBusMessage>
16#include <QDBusPendingCallWatcher>
17#include <QTimer>
18
19bool DBusActivationRunner::activationPossible(const KService::Ptr service, KIO::ApplicationLauncherJob::RunFlags flags, const QString &suggestedFileName)
20{
21 if (!service->isApplication()) {
22 return false;
23 }
24 if (service->property<bool>(QStringLiteral("DBusActivatable"))) {
25 if (service->desktopEntryName().count(c: QLatin1Char('.')) < 2) {
26 qCWarning(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "doesn't have enough '.' for a well-formed service name";
27 return false;
28 }
29 if (!suggestedFileName.isEmpty()) {
30 qCDebug(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "because suggestedFileName is set";
31 return false;
32 }
33 if (flags & KIO::ApplicationLauncherJob::DeleteTemporaryFiles) {
34 qCDebug(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "because DeleteTemporaryFiles is set";
35 return false;
36 }
37 return true;
38 }
39 return false;
40}
41
42DBusActivationRunner::DBusActivationRunner(const QString &action)
43 : KProcessRunner()
44 , m_actionName(action)
45{
46}
47
48void DBusActivationRunner::startProcess()
49{
50 // DBusActivatable as per https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus
51 const QString objectPath = QStringLiteral("/%1").arg(a: m_desktopName).replace(before: QLatin1Char('.'), after: QLatin1Char('/')).replace(before: QLatin1Char('-'), after: QLatin1Char('_'));
52 const QString interface = QStringLiteral("org.freedesktop.Application");
53 QDBusMessage message;
54 if (m_urls.isEmpty()) {
55 if (m_actionName.isEmpty()) {
56 message = QDBusMessage::createMethodCall(destination: m_desktopName, path: objectPath, interface, QStringLiteral("Activate"));
57 } else {
58 message = QDBusMessage::createMethodCall(destination: m_desktopName, path: objectPath, interface, QStringLiteral("ActivateAction"));
59 message << m_actionName << QVariantList();
60 }
61 } else {
62 message = QDBusMessage::createMethodCall(destination: m_desktopName, path: objectPath, interface, QStringLiteral("Open"));
63 message << QUrl::toStringList(uris: m_urls);
64 }
65 if (KWindowSystem::isPlatformX11()) {
66#if HAVE_X11
67 message << QVariantMap{{QStringLiteral("desktop-startup-id"), m_startupId.id()}};
68#endif
69 } else if (KWindowSystem::isPlatformWayland()) {
70 message << QVariantMap{{QStringLiteral("activation-token"), m_process->processEnvironment().value(QStringLiteral("XDG_ACTIVATION_TOKEN"))}};
71 }
72 auto call = QDBusConnection::sessionBus().asyncCall(message);
73 auto activationWatcher = new QDBusPendingCallWatcher(call, this);
74 connect(sender: activationWatcher, signal: &QDBusPendingCallWatcher::finished, context: this, slot: [this](QDBusPendingCallWatcher *watcher) {
75 watcher->deleteLater();
76 if (watcher->isError()) {
77 Q_EMIT error(errorString: watcher->error().message());
78 terminateStartupNotification();
79 m_finished = true;
80 deleteLater();
81 return;
82 }
83 auto call = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("GetConnectionUnixProcessID"), args&: m_desktopName);
84 auto pidWatcher = new QDBusPendingCallWatcher(call, this);
85 connect(sender: pidWatcher, signal: &QDBusPendingCallWatcher::finished, context: this, slot: [this](QDBusPendingCallWatcher *watcher) {
86 m_finished = true;
87 QDBusPendingReply<uint> reply = *watcher;
88 if (reply.isError()) {
89 Q_EMIT error(errorString: watcher->error().message());
90 terminateStartupNotification();
91 } else {
92 Q_EMIT processStarted(pid: reply.value());
93 }
94 deleteLater();
95 });
96 });
97}
98
99bool DBusActivationRunner::waitForStarted(int timeout)
100{
101 if (m_finished) {
102 return m_pid != 0;
103 }
104
105 QEventLoop loop;
106 bool success = false;
107 connect(sender: this, signal: &KProcessRunner::processStarted, slot: [&loop, &success]() {
108 loop.quit();
109 success = true;
110 });
111 connect(sender: this, signal: &KProcessRunner::error, context: &loop, slot: &QEventLoop::quit);
112 QTimer::singleShot(interval: timeout, receiver: &loop, slot: &QEventLoop::quit);
113 loop.exec();
114 return success;
115}
116
117#include "moc_dbusactivationrunner_p.cpp"
118

source code of kio/src/gui/dbusactivationrunner.cpp