diff --git a/src/kuiserverjobtracker.cpp b/src/kuiserverjobtracker.cpp index 0960e4d..eaca6af 100644 --- a/src/kuiserverjobtracker.cpp +++ b/src/kuiserverjobtracker.cpp @@ -1,378 +1,387 @@ /* This file is part of the KDE project Copyright (C) 2008 Rafael Fernández López Copyright (C) 2007 Kevin Ottens This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "kuiserverjobtracker.h" #include "kuiserverjobtracker_p.h" #include "jobviewiface.h" #include "debug.h" #include #include #include #include Q_GLOBAL_STATIC(KSharedUiServerProxy, serverProxy) class Q_DECL_HIDDEN KUiServerJobTracker::Private { public: Private(KUiServerJobTracker *parent) : q(parent) { } KUiServerJobTracker *const q; void _k_killJob(); static void updateDestUrl(KJob *job, org::kde::JobViewV2 *jobView); QHash progressJobView; QMetaObject::Connection serverRegisteredConnection; }; void KUiServerJobTracker::Private::_k_killJob() { org::kde::JobViewV2 *jobView = qobject_cast(q->sender()); if (jobView) { KJob *job = progressJobView.key(jobView); if (job) { job->kill(KJob::EmitResult); } } } void KUiServerJobTracker::Private::updateDestUrl(KJob *job, org::kde::JobViewV2 *jobView) { const QVariant destUrl = job->property("destUrl"); if (destUrl.isValid()) { jobView->setDestUrl(QDBusVariant(destUrl)); } } KUiServerJobTracker::KUiServerJobTracker(QObject *parent) : KJobTrackerInterface(parent), d(new Private(this)) { } KUiServerJobTracker::~KUiServerJobTracker() { if (!d->progressJobView.isEmpty()) { qWarning() << "A KUiServerJobTracker instance contains" << d->progressJobView.size() << "stalled jobs"; } + qDeleteAll(d->progressJobView); + delete d; } void KUiServerJobTracker::registerJob(KJob *job) { // Already registered job? if (d->progressJobView.contains(job)) { return; } // Watch the server registering/unregistering and re-register the jobs as needed if (!d->serverRegisteredConnection) { d->serverRegisteredConnection = connect(serverProxy(), &KSharedUiServerProxy::serverRegistered, this, [this]() { // Remember the list of jobs to re-register and then delete the old ones const QList staleJobs = d->progressJobView.keys(); qDeleteAll(d->progressJobView); d->progressJobView.clear(); for (KJob *job : staleJobs) { registerJob(job); } }); } const QString appName = QCoreApplication::applicationName(); // This will only work if main() used QIcon::fromTheme. QString programIconName = QApplication::windowIcon().name(); if (programIconName.isEmpty()) { programIconName = appName; } QPointer jobWatch = job; - QDBusReply reply = serverProxy()->uiserver().requestView(appName, + QDBusReply reply = serverProxy()->uiserver()->requestView(appName, programIconName, job->capabilities()); // If we got a valid reply, register the interface for later usage. if (reply.isValid()) { org::kde::JobViewV2 *jobView = new org::kde::JobViewV2(QStringLiteral("org.kde.JobViewServer"), reply.value().path(), QDBusConnection::sessionBus()); if (!jobWatch) { //qCDebug(KJOBWIDGETS) << "deleted out from under us when asking the server proxy for the view"; jobView->terminate(QString()); delete jobView; return; } QObject::connect(jobView, SIGNAL(cancelRequested()), this, SLOT(_k_killJob())); QObject::connect(jobView, &org::kde::JobViewV2::suspendRequested, job, &KJob::suspend); QObject::connect(jobView, &org::kde::JobViewV2::resumeRequested, job, &KJob::resume); d->updateDestUrl(job, jobView); if (!jobWatch) { //qCDebug(KJOBWIDGETS) << "deleted out from under us when creating the dbus interface"; jobView->terminate(QString()); delete jobView; return; } d->progressJobView.insert(job, jobView); } else if (!jobWatch) { qWarning() << "Uh-oh...KUiServerJobTracker was trying to forward a job, but it was deleted from under us." << "kuiserver *may* have a stranded job. we can't do anything about it because the returned objectPath is invalid."; return; } KJobTrackerInterface::registerJob(job); } void KUiServerJobTracker::unregisterJob(KJob *job) { KJobTrackerInterface::unregisterJob(job); if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView.take(job); d->updateDestUrl(job, jobView); jobView->setError(job->error()); if (job->error()) { jobView->terminate(job->errorText()); } else { jobView->terminate(QString()); } delete jobView; } void KUiServerJobTracker::finished(KJob *job) { if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView.take(job); d->updateDestUrl(job, jobView); jobView->setError(job->error()); if (job->error()) { jobView->terminate(job->errorText()); } else { jobView->terminate(QString()); } } void KUiServerJobTracker::suspended(KJob *job) { if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView[job]; jobView->setSuspended(true); } void KUiServerJobTracker::resumed(KJob *job) { if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView[job]; jobView->setSuspended(false); } void KUiServerJobTracker::description(KJob *job, const QString &title, const QPair &field1, const QPair &field2) { if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView[job]; jobView->setInfoMessage(title); if (field1.first.isNull() || field1.second.isNull()) { jobView->clearDescriptionField(0); } else { jobView->setDescriptionField(0, field1.first, field1.second); } if (field2.first.isNull() || field2.second.isNull()) { jobView->clearDescriptionField(1); } else { jobView->setDescriptionField(1, field2.first, field2.second); } } void KUiServerJobTracker::infoMessage(KJob *job, const QString &plain, const QString &rich) { Q_UNUSED(rich) if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView[job]; jobView->setInfoMessage(plain); } void KUiServerJobTracker::totalAmount(KJob *job, KJob::Unit unit, qulonglong amount) { if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView[job]; switch (unit) { case KJob::Bytes: jobView->setTotalAmount(amount, QStringLiteral("bytes")); break; case KJob::Files: jobView->setTotalAmount(amount, QStringLiteral("files")); break; case KJob::Directories: jobView->setTotalAmount(amount, QStringLiteral("dirs")); break; } } void KUiServerJobTracker::processedAmount(KJob *job, KJob::Unit unit, qulonglong amount) { if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView[job]; switch (unit) { case KJob::Bytes: jobView->setProcessedAmount(amount, QStringLiteral("bytes")); break; case KJob::Files: jobView->setProcessedAmount(amount, QStringLiteral("files")); break; case KJob::Directories: jobView->setProcessedAmount(amount, QStringLiteral("dirs")); break; } } void KUiServerJobTracker::percent(KJob *job, unsigned long percent) { if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView[job]; jobView->setPercent(percent); } void KUiServerJobTracker::speed(KJob *job, unsigned long value) { if (!d->progressJobView.contains(job)) { return; } org::kde::JobViewV2 *jobView = d->progressJobView[job]; jobView->setSpeed(value); } KSharedUiServerProxy::KSharedUiServerProxy() - : m_uiserver(QStringLiteral("org.kde.JobViewServer"), QStringLiteral("/JobViewServer"), QDBusConnection::sessionBus()) - , m_watcher(QStringLiteral("org.kde.JobViewServer"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange) + : m_uiserver(new org::kde::JobViewServer(QStringLiteral("org.kde.JobViewServer"), QStringLiteral("/JobViewServer"), QDBusConnection::sessionBus())) + , m_watcher(new QDBusServiceWatcher(QStringLiteral("org.kde.JobViewServer"), QDBusConnection::sessionBus(), QDBusServiceWatcher::WatchForOwnerChange)) { QDBusConnectionInterface *bus = QDBusConnection::sessionBus().interface(); if (!bus->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) { QDBusReply reply = bus->startService(QStringLiteral("org.kde.kuiserver")); if (!reply.isValid()) { qCritical() << "Couldn't start kuiserver from org.kde.kuiserver.service:" << reply.error(); return; } if (!bus->isServiceRegistered(QStringLiteral("org.kde.JobViewServer"))) { qCDebug(KJOBWIDGETS) << "The dbus name org.kde.JobViewServer is STILL NOT REGISTERED, even after starting kuiserver. Should not happen."; return; } qCDebug(KJOBWIDGETS) << "kuiserver registered"; } else { qCDebug(KJOBWIDGETS) << "kuiserver found"; } - connect(&m_watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, &KSharedUiServerProxy::uiserverOwnerChanged); + connect(m_watcher.get(), &QDBusServiceWatcher::serviceOwnerChanged, this, &KSharedUiServerProxy::uiserverOwnerChanged); + + // cleanup early enough to avoid issues with dbus at application exit + // see e.g. https://phabricator.kde.org/D2545 + qAddPostRoutine([]() { + serverProxy->m_uiserver.reset(); + serverProxy->m_watcher.reset(); + }); } KSharedUiServerProxy::~KSharedUiServerProxy() { } -org::kde::JobViewServer &KSharedUiServerProxy::uiserver() +org::kde::JobViewServer *KSharedUiServerProxy::uiserver() { - return m_uiserver; + return m_uiserver.get(); } void KSharedUiServerProxy::uiserverOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner) { Q_UNUSED(serviceName); Q_UNUSED(oldOwner); if (!newOwner.isEmpty()) { // registered emit serverRegistered(); } else if (newOwner.isEmpty()) { // unregistered emit serverUnregistered(); } } #include "moc_kuiserverjobtracker.cpp" #include "moc_kuiserverjobtracker_p.cpp" diff --git a/src/kuiserverjobtracker_p.h b/src/kuiserverjobtracker_p.h index 30f3f7f..587767e 100644 --- a/src/kuiserverjobtracker_p.h +++ b/src/kuiserverjobtracker_p.h @@ -1,52 +1,54 @@ /* This file is part of the KDE libraries Copyright (C) 2006-2008 Rafael Fernández López Kevin Ottens Copyright (C) 2000 Matej Koss David Faure Stephan Kulow This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #ifndef KUISERVERJOBTRACKER_P_H #define KUISERVERJOBTRACKER_P_H +#include + #include #include "jobviewserverinterface.h" class Q_DECL_HIDDEN KSharedUiServerProxy : public QObject { Q_OBJECT public: KSharedUiServerProxy(); ~KSharedUiServerProxy(); - org::kde::JobViewServer &uiserver(); + org::kde::JobViewServer *uiserver(); Q_SIGNALS: void serverRegistered(); void serverUnregistered(); private: void uiserverOwnerChanged(const QString &serviceName, const QString &oldOwner, const QString &newOwner); - org::kde::JobViewServer m_uiserver; - QDBusServiceWatcher m_watcher; + std::unique_ptr m_uiserver; + std::unique_ptr m_watcher; }; #endif