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(c: 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 }
36 if (flags & KIO::ApplicationLauncherJob::DeleteTemporaryFiles) {
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(a: m_desktopName).replace(before: QLatin1Char('.'), after: QLatin1Char('/')).replace(before: QLatin1Char('-'), after: 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(destination: m_desktopName, path: objectPath, interface, QStringLiteral("Activate"));
62 } else {
63 message = QDBusMessage::createMethodCall(destination: m_desktopName, path: objectPath, interface, QStringLiteral("ActivateAction"));
64 message << m_actionName << QVariantList();
65 }
66 } else {
67 message = QDBusMessage::createMethodCall(destination: m_desktopName, path: objectPath, interface, QStringLiteral("Open"));
68 message << QUrl::toStringList(uris: m_urls);
69 }
70 if (KWindowSystem::isPlatformX11()) {
71#if HAVE_X11
72 message << QVariantMap{{QStringLiteral("desktop-startup-id"), m_startupId.id()}};
73#endif
74 } else if (KWindowSystem::isPlatformWayland()) {
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(sender: activationWatcher, signal: &QDBusPendingCallWatcher::finished, context: this, slot: [this](QDBusPendingCallWatcher *watcher) {
80 watcher->deleteLater();
81 if (watcher->isError()) {
82 Q_EMIT error(errorString: watcher->error().message());
83 terminateStartupNotification();
84 m_finished = true;
85 deleteLater();
86 return;
87 }
88 auto call = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("GetConnectionUnixProcessID"), args&: m_desktopName);
89 auto pidWatcher = new QDBusPendingCallWatcher(call, this);
90 connect(sender: pidWatcher, signal: &QDBusPendingCallWatcher::finished, context: this, slot: [this](QDBusPendingCallWatcher *watcher) {
91 m_finished = true;
92 QDBusPendingReply<uint> reply = *watcher;
93 if (reply.isError()) {
94 Q_EMIT error(errorString: watcher->error().message());
95 terminateStartupNotification();
96 } else {
97 Q_EMIT processStarted(pid: 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(sender: this, signal: &KProcessRunner::processStarted, slot: [&loop, &success]() {
115 loop.quit();
116 success = true;
117 });
118 connect(sender: this, signal: &KProcessRunner::error, context: &loop, slot: &QEventLoop::quit);
119 QTimer::singleShot(interval: timeout, receiver: &loop, slot: &QEventLoop::quit);
120 loop.exec();
121 return success;
122#else
123 return false;
124#endif
125}
126
127#include "moc_dbusactivationrunner_p.cpp"
128

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