File indexing completed on 2025-01-19 03:41:33
0001 /* 0002 This file is part of the KDE libraries 0003 SPDX-FileCopyrightText: 2020-2021 David Redondo <kde@david-redondo.de> 0004 0005 SPDX-License-Identifier: LGPL-2.0-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL 0006 */ 0007 0008 #include "dbusactivationrunner_p.h" 0009 0010 #include "kiogui_debug.h" 0011 #include <KWindowSystem> 0012 0013 #ifndef Q_OS_ANDROID 0014 #include <QDBusConnection> 0015 #include <QDBusConnectionInterface> 0016 #include <QDBusMessage> 0017 #include <QDBusPendingCallWatcher> 0018 #endif 0019 #include <QTimer> 0020 0021 bool DBusActivationRunner::activationPossible(const KService::Ptr service, KIO::ApplicationLauncherJob::RunFlags flags, const QString &suggestedFileName) 0022 { 0023 #if defined Q_OS_UNIX && !defined Q_OS_ANDROID 0024 if (!service->isApplication()) { 0025 return false; 0026 } 0027 if (service->property<bool>(QStringLiteral("DBusActivatable"))) { 0028 if (service->desktopEntryName().count(QLatin1Char('.')) < 2) { 0029 qCWarning(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "doesn't have enough '.' for a well-formed service name"; 0030 return false; 0031 } 0032 if (!suggestedFileName.isEmpty()) { 0033 qCDebug(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "because suggestedFileName is set"; 0034 return false; 0035 } 0036 if (flags & KIO::ApplicationLauncherJob::DeleteTemporaryFiles) { 0037 qCDebug(KIO_GUI) << "Cannot activate" << service->desktopEntryName() << "because DeleteTemporaryFiles is set"; 0038 return false; 0039 } 0040 return true; 0041 } 0042 #endif 0043 return false; 0044 } 0045 0046 DBusActivationRunner::DBusActivationRunner(const QString &action) 0047 : KProcessRunner() 0048 , m_actionName(action) 0049 { 0050 } 0051 0052 void DBusActivationRunner::startProcess() 0053 { 0054 #ifndef Q_OS_ANDROID 0055 // DBusActivatable as per https://specifications.freedesktop.org/desktop-entry-spec/desktop-entry-spec-latest.html#dbus 0056 const QString objectPath = QStringLiteral("/%1").arg(m_desktopName).replace(QLatin1Char('.'), QLatin1Char('/')).replace(QLatin1Char('-'), QLatin1Char('_')); 0057 const QString interface = QStringLiteral("org.freedesktop.Application"); 0058 QDBusMessage message; 0059 if (m_urls.isEmpty()) { 0060 if (m_actionName.isEmpty()) { 0061 message = QDBusMessage::createMethodCall(m_desktopName, objectPath, interface, QStringLiteral("Activate")); 0062 } else { 0063 message = QDBusMessage::createMethodCall(m_desktopName, objectPath, interface, QStringLiteral("ActivateAction")); 0064 message << m_actionName << QVariantList(); 0065 } 0066 } else { 0067 message = QDBusMessage::createMethodCall(m_desktopName, objectPath, interface, QStringLiteral("Open")); 0068 message << QUrl::toStringList(m_urls); 0069 } 0070 if (KWindowSystem::isPlatformX11()) { 0071 #if HAVE_X11 0072 message << QVariantMap{{QStringLiteral("desktop-startup-id"), m_startupId.id()}}; 0073 #endif 0074 } else if (KWindowSystem::isPlatformWayland()) { 0075 message << QVariantMap{{QStringLiteral("activation-token"), m_process->processEnvironment().value(QStringLiteral("XDG_ACTIVATION_TOKEN"))}}; 0076 } 0077 auto call = QDBusConnection::sessionBus().asyncCall(message); 0078 auto activationWatcher = new QDBusPendingCallWatcher(call, this); 0079 connect(activationWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0080 watcher->deleteLater(); 0081 if (watcher->isError()) { 0082 Q_EMIT error(watcher->error().message()); 0083 terminateStartupNotification(); 0084 m_finished = true; 0085 deleteLater(); 0086 return; 0087 } 0088 auto call = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("GetConnectionUnixProcessID"), m_desktopName); 0089 auto pidWatcher = new QDBusPendingCallWatcher(call, this); 0090 connect(pidWatcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { 0091 m_finished = true; 0092 QDBusPendingReply<uint> reply = *watcher; 0093 if (reply.isError()) { 0094 Q_EMIT error(watcher->error().message()); 0095 terminateStartupNotification(); 0096 } else { 0097 Q_EMIT processStarted(reply.value()); 0098 } 0099 deleteLater(); 0100 }); 0101 }); 0102 #endif 0103 } 0104 0105 bool DBusActivationRunner::waitForStarted(int timeout) 0106 { 0107 #ifndef Q_OS_ANDROID 0108 if (m_finished) { 0109 return m_pid != 0; 0110 } 0111 0112 QEventLoop loop; 0113 bool success = false; 0114 connect(this, &KProcessRunner::processStarted, [&loop, &success]() { 0115 loop.quit(); 0116 success = true; 0117 }); 0118 connect(this, &KProcessRunner::error, &loop, &QEventLoop::quit); 0119 QTimer::singleShot(timeout, &loop, &QEventLoop::quit); 0120 loop.exec(); 0121 return success; 0122 #else 0123 return false; 0124 #endif 0125 } 0126 0127 #include "moc_dbusactivationrunner_p.cpp"