diff --git a/kcms/notifications/CMakeLists.txt b/kcms/notifications/CMakeLists.txt --- a/kcms/notifications/CMakeLists.txt +++ b/kcms/notifications/CMakeLists.txt @@ -9,6 +9,7 @@ add_library(kcm_notifications MODULE ${kcm_notifications_SRCS}) target_link_libraries(kcm_notifications + Qt5::DBus KF5::KCMUtils KF5::CoreAddons KF5::Declarative diff --git a/kcms/notifications/kcm.h b/kcms/notifications/kcm.h --- a/kcms/notifications/kcm.h +++ b/kcms/notifications/kcm.h @@ -38,6 +38,12 @@ Q_PROPERTY(NotificationManager::Settings *settings READ settings CONSTANT) + Q_PROPERTY(ServerStatus serverStatus READ serverStatus NOTIFY serverStatusChanged) + Q_PROPERTY(QString serverVendor READ serverVendor NOTIFY serverVendorChanged) + // Name of the notification server, as provided by the service, e.g. "Plasma" + // This is *not* the binary name + Q_PROPERTY(QString serverName READ serverName NOTIFY serverNameChanged) + // So it can show the respective settings module right away Q_PROPERTY(QString initialDesktopEntry READ initialDesktopEntry WRITE setInitialDesktopEntry NOTIFY initialDesktopEntryChanged) Q_PROPERTY(QString initialNotifyRcName READ initialNotifyRcName WRITE setInitialNotifyRcName NOTIFY initialNotifyRcNameChanged) @@ -47,11 +53,25 @@ KCMNotifications(QObject *parent, const QVariantList &args); ~KCMNotifications() override; + enum class ServerStatus { + Unknown = -1, + NotRunning, + Running + }; + Q_ENUM(ServerStatus) + SourcesModel *sourcesModel() const; FilterProxyModel *filteredModel() const; NotificationManager::Settings *settings() const; + ServerStatus serverStatus() const; + Q_SIGNAL void serverStatusChanged(); + QString serverVendor() const; + Q_SIGNAL void serverVendorChanged(); + QString serverName() const; + Q_SIGNAL void serverNameChanged(); + QString initialDesktopEntry() const; void setInitialDesktopEntry(const QString &desktopEntry); @@ -76,11 +96,19 @@ private: void processPendingDeletions(); + void setServerStatus(ServerStatus status); + void setServerInformation(const QString &vendor, const QString &name); + void updateServerName(); + SourcesModel *m_sourcesModel; FilterProxyModel *m_filteredModel; NotificationManager::Settings *m_settings; + ServerStatus m_serverStatus = ServerStatus::Unknown; + QString m_serverVendor; + QString m_serverName; + QString m_initialDesktopEntry; QString m_initialNotifyRcName; QString m_initialEventId; diff --git a/kcms/notifications/kcm.cpp b/kcms/notifications/kcm.cpp --- a/kcms/notifications/kcm.cpp +++ b/kcms/notifications/kcm.cpp @@ -21,6 +21,12 @@ #include "kcm.h" #include +#include +#include +#include +#include +#include +#include #include #include #include @@ -44,6 +50,8 @@ #include +static QString s_notificationServiceName = QStringLiteral("org.freedesktop.Notifications"); + K_PLUGIN_FACTORY_WITH_JSON(KCMNotificationsFactory, "kcm_notifications.json", registerPlugin();) KCMNotifications::KCMNotifications(QObject *parent, const QVariantList &args) @@ -54,6 +62,8 @@ { const char uri[] = "org.kde.private.kcms.notifications"; + qmlRegisterUncreatableType(uri, 1, 0, "KCM", + QStringLiteral("Cannot create instances of KCMNotifications")); qmlRegisterUncreatableType(uri, 1, 0, "SourcesModel", QStringLiteral("Cannot create instances of SourcesModel")); qmlRegisterType(); @@ -88,6 +98,14 @@ setInitialDesktopEntry(parser.value(desktopEntryOption)); setInitialNotifyRcName(parser.value(notifyRcNameOption)); setInitialEventId(parser.value(eventIdOption)); + + auto *watcher = new QDBusServiceWatcher(s_notificationServiceName, QDBusConnection::sessionBus(), + QDBusServiceWatcher::WatchForOwnerChange, this); + connect(watcher, &QDBusServiceWatcher::serviceOwnerChanged, this, [this]() { + updateServerName(); + }); + + updateServerName(); } KCMNotifications::~KCMNotifications() @@ -110,6 +128,77 @@ return m_settings; } +KCMNotifications::ServerStatus KCMNotifications::serverStatus() const +{ + return m_serverStatus; +} + +void KCMNotifications::setServerStatus(KCMNotifications::ServerStatus status) +{ + if (m_serverStatus != status) { + m_serverStatus = status; + emit serverStatusChanged(); + } +} + +QString KCMNotifications::serverVendor() const +{ + return m_serverVendor; +} + +QString KCMNotifications::serverName() const +{ + return m_serverName; +} + +void KCMNotifications::setServerInformation(const QString &vendor, const QString &name) +{ + if (m_serverVendor != vendor) { + m_serverVendor = vendor; + emit serverVendorChanged(); + } + if (m_serverName != name) { + m_serverName = name; + emit serverNameChanged(); + } +} + +void KCMNotifications::updateServerName() +{ + // This explicit check is so we don't end up launching the notification service + // and more importantly not get stuck on our own waitforname process. + if (!QDBusConnection::sessionBus().interface()->isServiceRegistered(s_notificationServiceName)) { + setServerStatus(ServerStatus::NotRunning); + setServerInformation(QString(), QString()); + return; + } + + QDBusMessage msg = QDBusMessage::createMethodCall(s_notificationServiceName, + QStringLiteral("/org/freedesktop/Notifications"), + QStringLiteral("org.freedesktop.Notifications"), + QStringLiteral("GetServerInformation")); + auto call = QDBusConnection::sessionBus().asyncCall(msg); + + auto *watcher = new QDBusPendingCallWatcher(call, this); + connect(watcher, &QDBusPendingCallWatcher::finished, this, [this](QDBusPendingCallWatcher *watcher) { + QDBusPendingReply reply = *watcher; + watcher->deleteLater(); + + if (reply.isError()) { + qWarning() << "Failed to determine notification server information" << reply.error().message(); + setServerStatus(ServerStatus::Unknown); + setServerInformation(QString(), QString()); + return; + } + + const QString name = reply.argumentAt(0).toString(); + const QString vendor = reply.argumentAt(1).toString(); + + setServerInformation(vendor, name); + setServerStatus(ServerStatus::Running); + }); +} + QString KCMNotifications::initialDesktopEntry() const { return m_initialDesktopEntry; diff --git a/kcms/notifications/package/contents/ui/main.qml b/kcms/notifications/package/contents/ui/main.qml --- a/kcms/notifications/package/contents/ui/main.qml +++ b/kcms/notifications/package/contents/ui/main.qml @@ -26,8 +26,11 @@ import org.kde.notificationmanager 1.0 as NotificationManager +import org.kde.private.kcms.notifications 1.0 as Private + KCM.SimpleKCM { id: root + KCM.ConfigModule.quickHelp: i18n("This module lets you manage application and system notifications.") KCM.ConfigModule.buttons: KCM.ConfigModule.Help | KCM.ConfigModule.Apply // Sidebar on SourcesPage is 1/3 of the width at a minimum of 12, so assume 3 * 12 = 36 as preferred @@ -46,6 +49,22 @@ } Kirigami.FormLayout { + Kirigami.InlineMessage { + Kirigami.FormData.isSection: true + Layout.fillWidth: true + type: Kirigami.MessageType.Error + text: i18n("Could not find a 'Notifications' widget."); + visible: kcm.serverStatus === Private.KCM.NotRunning + } + + Kirigami.InlineMessage { + Kirigami.FormData.isSection: true + Layout.fillWidth: true + type: Kirigami.MessageType.Information + text: i18n("Notifications are currently provided by '%1' instead of Plasma.", kcm.serverName) + visible: kcm.serverStatus === Private.KCM.Running && (kcm.serverVendor !== "KDE" || kcm.serverName !== "Plasma") + } + QtControls.CheckBox { Kirigami.FormData.label: i18n("Do not disturb:") text: i18nc("Do not disturb when screens are mirrored", "When screens are mirrored")