diff --git a/applets/notifications/package/contents/ui/global/Globals.qml b/applets/notifications/package/contents/ui/global/Globals.qml --- a/applets/notifications/package/contents/ui/global/Globals.qml +++ b/applets/notifications/package/contents/ui/global/Globals.qml @@ -460,4 +460,11 @@ interval: 250 onTriggered: positionPopups() } + + // Keeps the Inhibited property on DBus in sync with our inhibition handling + property Binding serverInhibitedBinding: Binding { + target: NotificationManager.Server + property: "inhibited" + value: globals.inhibited + } } diff --git a/libnotificationmanager/declarative/notificationmanagerplugin.cpp b/libnotificationmanager/declarative/notificationmanagerplugin.cpp --- a/libnotificationmanager/declarative/notificationmanagerplugin.cpp +++ b/libnotificationmanager/declarative/notificationmanagerplugin.cpp @@ -23,6 +23,7 @@ #include "notifications.h" #include "job.h" #include "settings.h" +#include "server.h" #include @@ -35,4 +36,8 @@ qmlRegisterType(uri, 1, 0, "Notifications"); qmlRegisterUncreatableType(uri, 1, 0, "Job", QStringLiteral("Can only access Job via JobDetailsRole of JobsModel")); qmlRegisterType(uri, 1, 0, "Settings"); + qmlRegisterSingletonType(uri, 1, 0, "Server", [](QQmlEngine *, QJSEngine *) -> QObject* { + QQmlEngine::setObjectOwnership(&Server::self(), QQmlEngine::CppOwnership); + return &Server::self(); + }); } diff --git a/libnotificationmanager/server.h b/libnotificationmanager/server.h --- a/libnotificationmanager/server.h +++ b/libnotificationmanager/server.h @@ -40,6 +40,14 @@ { Q_OBJECT + /** + * Whether notifications are currently inhibited. + * + * @note This does not keep track of inhibitions on its own, + * you need to calculate this yourself. + */ + Q_PROPERTY(bool inhibited READ inhibited WRITE setInhibited NOTIFY inhibitedChanged) + public: ~Server() override; @@ -68,10 +76,28 @@ bool isValid() const; /** - * Whether an application requested to inhibit notifications. + * Whether notifications are currently inhibited. + * + * @note This does not keep track of inhibitions on its own, + * you need to calculate this yourself. + * @since 5.17 */ bool inhibited() const; + /** + * Whether notifications are currently effectively inhibited. + * + * @note You need to keep track of inhibitions and call this + * yourself when appropriate. + * @since 5.17 + */ + void setInhibited(bool inhibited); + + /** + * Whether an application requested to inhibit notifications. + */ + bool inhibitedByApplication() const; + // should we return a struct or pair or something? QStringList inhibitionApplications() const; QStringList inhibitionReasons() const; @@ -136,6 +162,14 @@ */ void inhibitedChanged(bool inhibited); + /** + * Emitted when inhibitions by application have been changed. + * Becomes true as soon as there is one inhibition and becomes + * false again when all inhibitions have been lifted. + * @since 5.17 + */ + void inhibitedByApplicationChanged(bool inhibited); + /** * Emitted when the list of applications holding a notification * inhibition changes. diff --git a/libnotificationmanager/server.cpp b/libnotificationmanager/server.cpp --- a/libnotificationmanager/server.cpp +++ b/libnotificationmanager/server.cpp @@ -34,11 +34,13 @@ : QObject(parent) , d(new ServerPrivate(this)) { - connect(d.data(), &ServerPrivate::inhibitedChanged, this, [this] { + connect(d.data(), &ServerPrivate::effectiveInhibitedChanged, this, [this] { emit inhibitedChanged(inhibited()); }); - connect(d.data(), &ServerPrivate::inhibitionAdded, this, &Server::inhibitionApplicationsChanged); - connect(d.data(), &ServerPrivate::inhibitionRemoved, this, &Server::inhibitionApplicationsChanged); + connect(d.data(), &ServerPrivate::externalInhibitedChanged, this, [this] { + emit inhibitedByApplicationChanged(inhibitedByApplication()); + }); + connect(d.data(), &ServerPrivate::externalInhibitionsChanged, this, &Server::inhibitionApplicationsChanged); connect(d.data(), &ServerPrivate::serviceOwnershipLost, this, &Server::serviceOwnershipLost); } @@ -79,13 +81,23 @@ bool Server::inhibited() const { - return d->inhibited(); + return d->effectiveInhibited(); +} + +void Server::setInhibited(bool inhibited) +{ + d->setInternalInhibited(inhibited); +} + +bool Server::inhibitedByApplication() const +{ + return d->externalInhibited(); } QStringList Server::inhibitionApplications() const { QStringList applications; - const auto inhibitions = d->inhibitions(); + const auto inhibitions = d->externalInhibitions(); applications.reserve(inhibitions.count()); for (const auto &inhibition : inhibitions) { applications.append(!inhibition.applicationName.isEmpty() ? inhibition.applicationName : inhibition.desktopEntry); @@ -96,7 +108,7 @@ QStringList Server::inhibitionReasons() const { QStringList reasons; - const auto inhibitions = d->inhibitions(); + const auto inhibitions = d->externalInhibitions(); reasons.reserve(inhibitions.count()); for (const auto &inhibition : inhibitions) { reasons.append(inhibition.reason); @@ -106,5 +118,5 @@ void Server::clearInhibitions() { - d->clearInhibitions(); + d->clearExternalInhibitions(); } diff --git a/libnotificationmanager/server_p.h b/libnotificationmanager/server_p.h --- a/libnotificationmanager/server_p.h +++ b/libnotificationmanager/server_p.h @@ -45,7 +45,7 @@ // DBus // Inhibitions - Q_PROPERTY(bool Inhibited READ inhibited) + Q_PROPERTY(bool Inhibited READ effectiveInhibited) public: ServerPrivate(QObject *parent); @@ -64,24 +64,32 @@ const QString &reason, const QVariantMap &hints); void UnInhibit(uint cookie); - bool inhibited() const; // property getter + bool effectiveInhibited() const; // property getter Q_SIGNALS: // DBus void NotificationClosed(uint id, uint reason); void ActionInvoked(uint id, const QString &actionKey); - void inhibitedChanged(); - void inhibitionAdded(); - void inhibitionRemoved(); + void effectiveInhibitedChanged(); + + void externalInhibitedChanged(); + void externalInhibitionsChanged(); + void serviceOwnershipLost(); public: // stuff used by public class bool init(); uint add(const Notification ¬ification); - QList inhibitions() const; - void clearInhibitions(); + // Server only handles external application inhibitions but we still want the Inhibited property + // expose the actual inhibition state for applications to check. + // This is called by Settings to update the other inhibitions (until, screens mirrored, etc) + void setInternalInhibited(bool inhibited); + + bool externalInhibited() const; + QList externalInhibitions() const; + void clearExternalInhibitions(); bool m_valid = false; uint m_highestNotificationId = 1; @@ -94,9 +102,11 @@ QDBusServiceWatcher *m_inhibitionWatcher = nullptr; uint m_highestInhibitionCookie = 0; - QHash m_inhibitions; + QHash m_externalInhibitions; QHash m_inhibitionServices; + bool m_internalInhibited = false; + Notification m_lastNotification; }; diff --git a/libnotificationmanager/server_p.cpp b/libnotificationmanager/server_p.cpp --- a/libnotificationmanager/server_p.cpp +++ b/libnotificationmanager/server_p.cpp @@ -90,7 +90,7 @@ return false; } - connect(this, &ServerPrivate::inhibitedChanged, this, [this] { + connect(this, &ServerPrivate::effectiveInhibitedChanged, this, [this] { // emit DBus change signal... QDBusMessage signal = QDBusMessage::createSignal( QStringLiteral("/org/freedesktop/Notifications"), @@ -101,7 +101,7 @@ signal.setArguments({ QStringLiteral("org.freedesktop.Notifications"), QVariantMap{ // updated - {QStringLiteral("Inhibited"), inhibited()}, + {QStringLiteral("Inhibited"), effectiveInhibited()}, }, QStringList() // invalidated }); @@ -323,17 +323,25 @@ ++m_highestInhibitionCookie; - m_inhibitions.insert(m_highestInhibitionCookie, { + const bool oldInhibited = effectiveInhibited(); + const bool oldExternalInhibited = externalInhibited(); + + m_externalInhibitions.insert(m_highestInhibitionCookie, { desktop_entry, applicationName, reason, hints }); m_inhibitionServices.insert(m_highestInhibitionCookie, dbusService); - emit inhibitedChanged(); - emit inhibitionAdded(); + if (effectiveInhibited() != oldInhibited) { + emit effectiveInhibitedChanged(); + } + if (externalInhibited() != oldExternalInhibited) { + emit externalInhibitedChanged(); + } + emit externalInhibitionsChanged(); return m_highestInhibitionCookie; } @@ -363,38 +371,59 @@ return; } + const bool oldInhibited = effectiveInhibited(); + m_inhibitionWatcher->removeWatchedService(service); - m_inhibitions.remove(cookie); + m_externalInhibitions.remove(cookie); m_inhibitionServices.remove(cookie); - if (m_inhibitions.isEmpty()) { - emit inhibitedChanged(); - emit inhibitionRemoved(); + if (effectiveInhibited() != oldInhibited) { + emit effectiveInhibitedChanged(); } + if (m_externalInhibitions.isEmpty()) { + emit externalInhibitedChanged(); + } + emit externalInhibitionsChanged(); +} + +QList ServerPrivate::externalInhibitions() const +{ + return m_externalInhibitions.values(); } -QList ServerPrivate::inhibitions() const +bool ServerPrivate::effectiveInhibited() const { - return m_inhibitions.values(); + return m_internalInhibited || !m_externalInhibitions.isEmpty(); } -bool ServerPrivate::inhibited() const +void ServerPrivate::setInternalInhibited(bool inhibited) { - // TODO this currently only returns whether an app has an inhibition going, - // there's no way for apps to query whether user enabled do not disturb from the applet - // so they could change their behavior. - return !m_inhibitions.isEmpty(); + const bool oldInhibited = effectiveInhibited(); + m_internalInhibited = inhibited; + if (effectiveInhibited() != oldInhibited) { + emit effectiveInhibitedChanged(); + } } -void ServerPrivate::clearInhibitions() +bool ServerPrivate::externalInhibited() const { - if (m_inhibitions.isEmpty()) { + return !m_externalInhibitions.isEmpty(); +} + +void ServerPrivate::clearExternalInhibitions() +{ + if (m_externalInhibitions.isEmpty()) { return; } + const bool oldInhibited = effectiveInhibited(); m_inhibitionWatcher->setWatchedServices(QStringList()); // remove all watches m_inhibitionServices.clear(); - m_inhibitions.clear(); - emit inhibitedChanged(); - emit inhibitionRemoved(); + m_externalInhibitions.clear(); + + if (effectiveInhibited() != oldInhibited) { + emit effectiveInhibitedChanged(); + } + emit externalInhibitedChanged(); + emit externalInhibitionsChanged(); } diff --git a/libnotificationmanager/settings.cpp b/libnotificationmanager/settings.cpp --- a/libnotificationmanager/settings.cpp +++ b/libnotificationmanager/settings.cpp @@ -179,7 +179,7 @@ setLive(true); - connect(&Server::self(), &Server::inhibitedChanged, + connect(&Server::self(), &Server::inhibitedByApplicationChanged, this, &Settings::notificationsInhibitedByApplicationChanged); connect(&Server::self(), &Server::inhibitionApplicationsChanged, this, &Settings::notificationInhibitionApplicationsChanged); @@ -552,7 +552,7 @@ bool Settings::notificationsInhibitedByApplication() const { - return Server::self().inhibited(); + return Server::self().inhibitedByApplication(); } QStringList Settings::notificationInhibitionApplications() const