diff --git a/src/knotification.cpp b/src/knotification.cpp index 25f1871..7481261 100644 --- a/src/knotification.cpp +++ b/src/knotification.cpp @@ -1,577 +1,568 @@ /* This file is part of the KDE libraries Copyright (C) 2005-2006 Olivier Goffart Copyright (C) 2013-2014 Martin Klapetek code from KNotify/KNotifyClient Copyright (c) 1997 Christian Esken (esken@kde.org) 2000 Charles Samuels (charles@kde.org) 2000 Stefan Schimanski (1Stein@gmx.de) 2000 Matthias Ettrich (ettrich@kde.org) 2000 Waldo Bastian 2000-2003 Carsten Pfeiffer 2005 Allan Sandfeld Jensen 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 "knotification.h" #include "knotificationmanager_p.h" #include #include #include #include #include #include struct Q_DECL_HIDDEN KNotification::Private { QString eventId; int id; int ref; QWidget *widget; QString title; QString text; QString iconName; QString defaultAction; QStringList actions; QPixmap pixmap; ContextList contexts; NotificationFlags flags; QString componentName; - QList urls; KNotification::Urgency urgency; QVariantMap hints; QTimer updateTimer; bool needUpdate; Private() : id(-1), ref(0), widget(nullptr), urgency(KNotification::DefaultUrgency), needUpdate(false) {} #if KNOTIFICATIONS_BUILD_DEPRECATED_SINCE(5, 67) /** * recursive function that raise the widget. @p w * * @see raiseWidget() */ static void raiseWidget(QWidget *w); #endif }; KNotification::KNotification(const QString &eventId, QWidget *parent, const NotificationFlags &flags) : QObject(parent), d(new Private) { d->eventId = eventId; d->flags = flags; setWidget(parent); connect(&d->updateTimer, &QTimer::timeout, this, &KNotification::update); d->updateTimer.setSingleShot(true); d->updateTimer.setInterval(100); } KNotification::KNotification( const QString &eventId, const NotificationFlags &flags, QObject *parent) : QObject(parent), d(new Private) { d->eventId = eventId; d->flags = flags; connect(&d->updateTimer, &QTimer::timeout, this, &KNotification::update); d->updateTimer.setSingleShot(true); d->updateTimer.setInterval(100); d->widget = nullptr; } KNotification::~KNotification() { if (d->id >= 0) { KNotificationManager::self()->close(d->id); } delete d; } QString KNotification::eventId() const { return d->eventId; } QString KNotification::title() const { return d->title; } QString KNotification::text() const { return d->text; } QWidget *KNotification::widget() const { return d->widget; } void KNotification::setWidget(QWidget *wid) { d->widget = wid; // setParent(wid); if (wid && d->flags & CloseWhenWidgetActivated) { wid->installEventFilter(this); } } void KNotification::setTitle(const QString &title) { if (title == d->title) { return; } d->needUpdate = true; d->title = title; if (d->id >= 0) { d->updateTimer.start(); } } void KNotification::setText(const QString &text) { if (text == d->text) { return; } d->needUpdate = true; d->text = text; if (d->id >= 0) { d->updateTimer.start(); } } void KNotification::setIconName(const QString &icon) { if (icon == d->iconName) { return; } d->needUpdate = true; d->iconName = icon; if (d->id >= 0) { d->updateTimer.start(); } } QString KNotification::iconName() const { return d->iconName; } QPixmap KNotification::pixmap() const { return d->pixmap; } void KNotification::setPixmap(const QPixmap &pix) { d->needUpdate = true; d->pixmap = pix; if (d->id >= 0) { d->updateTimer.start(); } } QStringList KNotification::actions() const { return d->actions; } void KNotification::setActions(const QStringList &as) { if (as == d->actions) { return; } d->needUpdate = true; d->actions = as; if (d->id >= 0) { d->updateTimer.start(); } } void KNotification::setDefaultAction(const QString &defaultAction) { if (defaultAction == d->defaultAction) { return; } d->needUpdate = true; d->defaultAction = defaultAction; if (d->id >= 0) { d->updateTimer.start(); } } QString KNotification::defaultAction() const { return d->defaultAction; } KNotification::ContextList KNotification::contexts() const { return d->contexts; } void KNotification::setContexts(const KNotification::ContextList &contexts) { d->contexts = contexts; } void KNotification::addContext(const KNotification::Context &context) { d->contexts << context; } void KNotification::addContext(const QString &context_key, const QString &context_value) { d->contexts << qMakePair(context_key, context_value); } KNotification::NotificationFlags KNotification::flags() const { return d->flags; } void KNotification::setFlags(const NotificationFlags &flags) { if (d->flags == flags) { return; } d->needUpdate = true; d->flags = flags; if (d->id >= 0) { d->updateTimer.start(); } } void KNotification::setComponentName(const QString &c) { d->componentName = c; } QList KNotification::urls() const { - return d->urls; + return QUrl::fromStringList(d->hints[QStringLiteral("x-kde-urls")].toStringList()); } void KNotification::setUrls(const QList &urls) { - if (d->urls == urls) { - return; - } - - d->needUpdate = true; - d->urls = urls; - if (d->id >= 0) { - d->updateTimer.start(); - } + setHint(QStringLiteral("x-kde-urls"), QUrl::toStringList(urls)); } KNotification::Urgency KNotification::urgency() const { return d->urgency; } void KNotification::setUrgency(Urgency urgency) { if (d->urgency == urgency) { return; } d->needUpdate = true; d->urgency = urgency; if (d->id >= 0) { d->updateTimer.start(); } } void KNotification::activate(unsigned int action) { switch (action) { case 0: emit activated(); emit defaultActivated(); break; case 1: emit action1Activated(); break; case 2: emit action2Activated(); break; case 3: emit action3Activated(); break; } // emitting activated() makes the Manager close all the active plugins // which will deref() the KNotification object, which will result // in closing the notification emit activated(action); } void KNotification::close() { if (d->id >= 0) { KNotificationManager::self()->close(d->id); } if (d->id == -1) { d->id = -2; emit closed(); deleteLater(); } } #if KNOTIFICATIONS_BUILD_DEPRECATED_SINCE(5, 67) void KNotification::raiseWidget() { if (!d->widget) { return; } Private::raiseWidget(d->widget); } #endif #if KNOTIFICATIONS_BUILD_DEPRECATED_SINCE(5, 67) void KNotification::Private::raiseWidget(QWidget *w) { //TODO this function is far from finished. if (w->isTopLevel()) { w->raise(); w->activateWindow(); } else { QWidget *pw = w->parentWidget(); raiseWidget(pw); if (QTabWidget *tab_widget = qobject_cast(pw)) { tab_widget->setCurrentIndex(tab_widget->indexOf(w)); } } } #endif static QString defaultComponentName() { #if defined(Q_OS_ANDROID) return QStringLiteral("android_defaults"); #else return QStringLiteral("plasma_workspace"); #endif } KNotification *KNotification::event(const QString &eventid, const QString &title, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags, const QString &componentName) { KNotification *notify = new KNotification(eventid, widget, flags); notify->setTitle(title); notify->setText(text); notify->setPixmap(pixmap); notify->setComponentName((flags & DefaultEvent) ? defaultComponentName() : componentName); QTimer::singleShot(0, notify, &KNotification::sendEvent); return notify; } KNotification *KNotification::event(const QString &eventid, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags, const QString &componentName) { return event(eventid, QString(), text, pixmap, widget, flags, componentName); } KNotification *KNotification::event(StandardEvent eventid, const QString &title, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags) { return event(standardEventToEventId(eventid), title, text, pixmap, widget, flags | DefaultEvent); } KNotification *KNotification::event(StandardEvent eventid, const QString &text, const QPixmap &pixmap, QWidget *widget, const NotificationFlags &flags) { return event(eventid, QString(), text, pixmap, widget, flags); } KNotification *KNotification::event(const QString &eventid, const QString &title, const QString &text, const QString &iconName, QWidget *widget, const NotificationFlags &flags, const QString &componentName) { KNotification *notify = new KNotification(eventid, widget, flags); notify->setTitle(title); notify->setText(text); notify->setIconName(iconName); notify->setComponentName((flags & DefaultEvent) ? defaultComponentName() : componentName); QTimer::singleShot(0, notify, &KNotification::sendEvent); return notify; } KNotification *KNotification::event(StandardEvent eventid, const QString &title, const QString &text, const QString &iconName, QWidget *widget, const NotificationFlags &flags) { return event(standardEventToEventId(eventid), title, text, iconName, widget, flags | DefaultEvent); } KNotification* KNotification::event(StandardEvent eventid, const QString &title, const QString &text, QWidget *widget, const NotificationFlags &flags) { return event(standardEventToEventId(eventid), title, text, standardEventToIconName(eventid), widget, flags | DefaultEvent); } void KNotification::ref() { d->ref++; } void KNotification::deref() { Q_ASSERT(d->ref > 0); d->ref--; if (d->ref == 0) { d->id = -1; close(); } } void KNotification::beep(const QString &reason, QWidget *widget) { event(QStringLiteral("beep"), reason, QPixmap(), widget, CloseOnTimeout | DefaultEvent); } void KNotification::sendEvent() { d->needUpdate = false; if (d->id == -1) { d->id = KNotificationManager::self()->notify(this); } else if (d->id >= 0) { KNotificationManager::self()->reemit(this); } } int KNotification::id() { if (!d) { return -1; } return d->id; } QString KNotification::appName() const { QString appname; if (d->flags & DefaultEvent) { appname = defaultComponentName(); } else if (!d->componentName.isEmpty()) { appname = d->componentName; } else { appname = QCoreApplication::applicationName(); } return appname; } void KNotification::update() { if (d->needUpdate) { KNotificationManager::self()->update(this); } } bool KNotification::eventFilter(QObject *watched, QEvent *event) { if (watched == d->widget) { if (event->type() == QEvent::WindowActivate) { if (d->flags & CloseWhenWidgetActivated) { QTimer::singleShot(500, this, &KNotification::close); } } } return false; } QString KNotification::standardEventToEventId(KNotification::StandardEvent event) { QString eventId; switch (event) { case Warning: eventId = QStringLiteral("warning"); break; case Error: eventId = QStringLiteral("fatalerror"); break; case Catastrophe: eventId = QStringLiteral("catastrophe"); break; case Notification: // fall through default: eventId = QStringLiteral("notification"); break; } return eventId; } QString KNotification::standardEventToIconName(KNotification::StandardEvent event) { QString iconName; switch (event) { case Warning: iconName = QStringLiteral("dialog-warning"); break; case Error: iconName = QStringLiteral("dialog-error"); break; case Catastrophe: iconName = QStringLiteral("dialog-error"); break; case Notification: // fall through default: iconName = QStringLiteral("dialog-information"); break; } return iconName; } void KNotification::setHint(const QString &hint, const QVariant &value) { if (value == d->hints.value(hint)) { return; } d->needUpdate = true; d->hints[hint] = value; if (d->id >= 0) { d->updateTimer.start(); } } QVariantMap KNotification::hints() const { return d->hints; } diff --git a/src/notifybypopup.cpp b/src/notifybypopup.cpp index 043b9f5..9735917 100644 --- a/src/notifybypopup.cpp +++ b/src/notifybypopup.cpp @@ -1,462 +1,458 @@ /* Copyright (C) 2005-2009 by Olivier Goffart Copyright (C) 2008 by Dmitry Suzdalev Copyright (C) 2014 by Martin Klapetek This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) version 3, or any later version accepted by the membership of KDE e.V. (or its successor approved by the membership of KDE e.V.), which shall act as a proxy defined in Section 6 of version 3 of the license. 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library. If not, see . */ #include "notifybypopup.h" #include "imageconverter.h" #include "knotifyconfig.h" #include "knotification.h" #include "debug_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include static const char dbusServiceName[] = "org.freedesktop.Notifications"; static const char dbusInterfaceName[] = "org.freedesktop.Notifications"; static const char dbusPath[] = "/org/freedesktop/Notifications"; class NotifyByPopupPrivate { public: NotifyByPopupPrivate(NotifyByPopup *parent) : q(parent) {} /** * Sends notification to DBus "org.freedesktop.notifications" interface. * @param id knotify-sid identifier of notification * @param config notification data * @param update If true, will request the DBus service to update the notification with new data from \c notification * Otherwise will put new notification on screen * @return true for success or false if there was an error. */ bool sendNotificationToServer(KNotification *notification, const KNotifyConfig &config, bool update = false); /** * Find the caption and the icon name of the application */ void getAppCaptionAndIconName(const KNotifyConfig &config, QString *appCaption, QString *iconName); /* * Query the dbus server for notification capabilities * If no DBus server is present, use fallback capabilities for KPassivePopup */ void queryPopupServerCapabilities(); /** * DBus notification daemon capabilities cache. * Do not use this variable. Use #popupServerCapabilities() instead. * @see popupServerCapabilities */ QStringList popupServerCapabilities; /** * In case we still don't know notification server capabilities, * we need to query those first. That's done in an async way * so we queue all notifications while waiting for the capabilities * to return, then process them from this queue */ QList > notificationQueue; /** * Whether the DBus notification daemon capability cache is up-to-date. */ bool dbusServiceCapCacheDirty; /* * As we communicate with the notification server over dbus * we use only ids, this is for fast KNotifications lookup */ QHash> notifications; NotifyByPopup * const q; }; //--------------------------------------------------------------------------------------- NotifyByPopup::NotifyByPopup(QObject *parent) : KNotificationPlugin(parent), d(new NotifyByPopupPrivate(this)) { d->dbusServiceCapCacheDirty = true; bool connected = QDBusConnection::sessionBus().connect(QString(), // from any service QString::fromLatin1(dbusPath), QString::fromLatin1(dbusInterfaceName), QStringLiteral("ActionInvoked"), this, SLOT(onNotificationActionInvoked(uint,QString))); if (!connected) { qCWarning(LOG_KNOTIFICATIONS) << "warning: failed to connect to ActionInvoked dbus signal"; } connected = QDBusConnection::sessionBus().connect(QString(), // from any service QString::fromLatin1(dbusPath), QString::fromLatin1(dbusInterfaceName), QStringLiteral("NotificationClosed"), this, SLOT(onNotificationClosed(uint,uint))); if (!connected) { qCWarning(LOG_KNOTIFICATIONS) << "warning: failed to connect to NotificationClosed dbus signal"; } } NotifyByPopup::~NotifyByPopup() { delete d; } void NotifyByPopup::notify(KNotification *notification, KNotifyConfig *notifyConfig) { notify(notification, *notifyConfig); } void NotifyByPopup::notify(KNotification *notification, const KNotifyConfig ¬ifyConfig) { if (d->notifications.contains(notification->id())) { // notification is already on the screen, do nothing finish(notification); return; } if (d->dbusServiceCapCacheDirty) { // if we don't have the server capabilities yet, we need to query for them first; // as that is an async dbus operation, we enqueue the notification and process them // when we receive dbus reply with the server capabilities d->notificationQueue.append(qMakePair(notification, notifyConfig)); d->queryPopupServerCapabilities(); } else { if (!d->sendNotificationToServer(notification, notifyConfig)) { finish(notification); //an error occurred. } } } void NotifyByPopup::update(KNotification *notification, KNotifyConfig *notifyConfig) { update(notification, *notifyConfig); } void NotifyByPopup::update(KNotification *notification, const KNotifyConfig ¬ifyConfig) { d->sendNotificationToServer(notification, notifyConfig, true); } void NotifyByPopup::close(KNotification *notification) { uint id = d->notifications.key(notification, 0); if (id == 0) { qCDebug(LOG_KNOTIFICATIONS) << "not found dbus id to close" << notification->id(); return; } QDBusMessage m = QDBusMessage::createMethodCall(QString::fromLatin1(dbusServiceName), QString::fromLatin1(dbusPath), QString::fromLatin1(dbusInterfaceName), QStringLiteral("CloseNotification")); QList args; args.append(id); m.setArguments(args); // send(..) does not block bool queued = QDBusConnection::sessionBus().send(m); if (!queued) { qCWarning(LOG_KNOTIFICATIONS) << "Failed to queue dbus message for closing a notification"; } QMutableListIterator > iter(d->notificationQueue); while (iter.hasNext()) { auto &item = iter.next(); if (item.first == notification) { iter.remove(); } } } void NotifyByPopup::onNotificationActionInvoked(uint notificationId, const QString &actionKey) { auto iter = d->notifications.find(notificationId); if (iter == d->notifications.end()) { return; } KNotification *n = *iter; if (n) { if (actionKey == QLatin1String("default")) { emit actionInvoked(n->id(), 0); } else { emit actionInvoked(n->id(), actionKey.toUInt()); } } else { d->notifications.erase(iter); } } void NotifyByPopup::onNotificationClosed(uint dbus_id, uint reason) { auto iter = d->notifications.find(dbus_id); if (iter == d->notifications.end()) { return; } KNotification *n = *iter; d->notifications.remove(dbus_id); if (n) { emit finished(n); // The popup bubble is the only user facing part of a notification, // if the user closes the popup, it means he wants to get rid // of the notification completely, including playing sound etc // Therefore we close the KNotification completely after closing // the popup, but only if the reason is 2, which means "user closed" if (reason == 2) { n->close(); } } } void NotifyByPopup::onServerReply(QDBusPendingCallWatcher *watcher) { // call deleteLater first, since we might return in the middle of the function watcher->deleteLater(); KNotification *notification = watcher->property("notificationObject").value(); if (!notification) { qCWarning(LOG_KNOTIFICATIONS) << "Invalid notification object passed in DBus reply watcher; notification will probably break"; return; } QDBusPendingReply reply = *watcher; d->notifications.insert(reply.argumentAt<0>(), notification); } void NotifyByPopup::onServerCapabilitiesReceived(const QStringList &capabilities) { d->popupServerCapabilities = capabilities; d->dbusServiceCapCacheDirty = false; // re-run notify() on all enqueued notifications for (int i = 0, total = d->notificationQueue.size(); i < total; ++i) { notify(d->notificationQueue.at(i).first, d->notificationQueue.at(i).second); } d->notificationQueue.clear(); } void NotifyByPopupPrivate::getAppCaptionAndIconName(const KNotifyConfig ¬ifyConfig, QString *appCaption, QString *iconName) { KConfigGroup globalgroup(&(*notifyConfig.eventsfile), QStringLiteral("Global")); *appCaption = globalgroup.readEntry("Name", globalgroup.readEntry("Comment", notifyConfig.appname)); KConfigGroup eventGroup(&(*notifyConfig.eventsfile), QStringLiteral("Event/%1").arg(notifyConfig.eventid)); if (eventGroup.hasKey("IconName")) { *iconName = eventGroup.readEntry("IconName", notifyConfig.appname); } else { *iconName = globalgroup.readEntry("IconName", notifyConfig.appname); } } bool NotifyByPopupPrivate::sendNotificationToServer(KNotification *notification, const KNotifyConfig ¬ifyConfig_nocheck, bool update) { uint updateId = notifications.key(notification, 0); if (update) { if (updateId == 0) { // we have nothing to update; the notification we're trying to update // has been already closed return false; } } QDBusMessage dbusNotificationMessage = QDBusMessage::createMethodCall(QString::fromLatin1(dbusServiceName), QString::fromLatin1(dbusPath), QString::fromLatin1(dbusInterfaceName), QStringLiteral("Notify")); QList args; QString appCaption; QString iconName; getAppCaptionAndIconName(notifyConfig_nocheck, &appCaption, &iconName); //did the user override the icon name? if (!notification->iconName().isEmpty()) { iconName = notification->iconName(); } args.append(appCaption); // app_name args.append(updateId); // notification to update args.append(iconName); // app_icon QString title = notification->title().isEmpty() ? appCaption : notification->title(); QString text = notification->text(); if (!popupServerCapabilities.contains(QLatin1String("body-markup"))) { title = q->stripRichText(title); text = q->stripRichText(text); } args.append(title); // summary args.append(text); // body // freedesktop.org spec defines action list to be list like // (act_id1, action1, act_id2, action2, ...) // // assign id's to actions like it's done in fillPopup() method // (i.e. starting from 1) QStringList actionList; if (popupServerCapabilities.contains(QLatin1String("actions"))) { QString defaultAction = notification->defaultAction(); if (!defaultAction.isEmpty()) { actionList.append(QStringLiteral("default")); actionList.append(defaultAction); } int actId = 0; const auto listActions = notification->actions(); for (const QString &actionName : listActions) { actId++; actionList.append(QString::number(actId)); actionList.append(actionName); } } args.append(actionList); // actions QVariantMap hintsMap; // Add the application name to the hints. // According to freedesktop.org spec, the app_name is supposed to be the application's "pretty name" // but in some places it's handy to know the application name itself if (!notification->appName().isEmpty()) { hintsMap[QStringLiteral("x-kde-appname")] = notification->appName(); } if (!notification->eventId().isEmpty()) { hintsMap[QStringLiteral("x-kde-eventId")] = notification->eventId(); } if (notification->flags() & KNotification::SkipGrouping) { hintsMap[QStringLiteral("x-kde-skipGrouping")] = 1; } - if (!notification->urls().isEmpty()) { - hintsMap[QStringLiteral("x-kde-urls")] = QUrl::toStringList(notification->urls()); - } - if (!(notification->flags() & KNotification::Persistent)) { hintsMap[QStringLiteral("transient")] = true; } QString desktopFileName = QGuiApplication::desktopFileName(); if (!desktopFileName.isEmpty()) { // handle apps which set the desktopFileName property with filename suffix, // due to unclear API dox (https://bugreports.qt.io/browse/QTBUG-75521) if (desktopFileName.endsWith(QLatin1String(".desktop"))) { desktopFileName.chop(8); } hintsMap[QStringLiteral("desktop-entry")] = desktopFileName; } int urgency = -1; switch (notification->urgency()) { case KNotification::DefaultUrgency: break; case KNotification::LowUrgency: urgency = 0; break; case KNotification::NormalUrgency: Q_FALLTHROUGH(); // freedesktop.org notifications only know low, normal, critical case KNotification::HighUrgency: urgency = 1; break; case KNotification::CriticalUrgency: urgency = 2; break; } if (urgency > -1) { hintsMap[QStringLiteral("urgency")] = urgency; } const QVariantMap hints = notification->hints(); for (auto it = hints.constBegin(); it != hints.constEnd(); ++it) { hintsMap[it.key()] = it.value(); } //FIXME - reenable/fix // let's see if we've got an image, and store the image in the hints map if (!notification->pixmap().isNull()) { QByteArray pixmapData; QBuffer buffer(&pixmapData); buffer.open(QIODevice::WriteOnly); notification->pixmap().save(&buffer, "PNG"); buffer.close(); hintsMap[QStringLiteral("image_data")] = ImageConverter::variantForImage(QImage::fromData(pixmapData)); } args.append(hintsMap); // hints // Persistent => 0 == infinite timeout // CloseOnTimeout => -1 == let the server decide int timeout = (notification->flags() & KNotification::Persistent) ? 0 : -1; args.append(timeout); // expire timeout dbusNotificationMessage.setArguments(args); QDBusPendingCall notificationCall = QDBusConnection::sessionBus().asyncCall(dbusNotificationMessage, -1); //parent is set to the notification so that no-one ever accesses a dangling pointer on the notificationObject property QDBusPendingCallWatcher *watcher = new QDBusPendingCallWatcher(notificationCall, notification); watcher->setProperty("notificationObject", QVariant::fromValue(notification)); QObject::connect(watcher, &QDBusPendingCallWatcher::finished, q, &NotifyByPopup::onServerReply); return true; } void NotifyByPopupPrivate::queryPopupServerCapabilities() { if (dbusServiceCapCacheDirty) { QDBusMessage m = QDBusMessage::createMethodCall(QString::fromLatin1(dbusServiceName), QString::fromLatin1(dbusPath), QString::fromLatin1(dbusInterfaceName), QStringLiteral("GetCapabilities")); QDBusConnection::sessionBus().callWithCallback(m, q, SLOT(onServerCapabilitiesReceived(QStringList)), nullptr, -1); } }