#include "server.hpp" #include #include #include #include #include #include #include #include #include #include #include #include "../../core/model.hpp" #include "dbus_notifications.h" #include "dbusimage.hpp" #include "notification.hpp" namespace qs::service::notifications { Q_LOGGING_CATEGORY(logNotifications, "quickshell.service.notifications"); NotificationServer::NotificationServer() { qDBusRegisterMetaType(); new DBusNotificationServer(this); qCInfo(logNotifications) << "Starting notification server"; auto bus = QDBusConnection::sessionBus(); if (!bus.isConnected()) { qCWarning(logNotifications) << "Could not connect to DBus. Notification service will not work."; return; } if (!bus.registerObject("/org/freedesktop/Notifications", this)) { qCWarning(logNotifications) << "Could not register Notification server object with DBus. " "Notification server will not work."; return; } QObject::connect( &this->serviceWatcher, &QDBusServiceWatcher::serviceUnregistered, this, &NotificationServer::onServiceUnregistered ); this->serviceWatcher.setWatchMode(QDBusServiceWatcher::WatchForUnregistration); this->serviceWatcher.addWatchedService("org.freedesktop.Notifications"); this->serviceWatcher.setConnection(bus); NotificationServer::tryRegister(); } NotificationServer* NotificationServer::instance() { static auto* instance = new NotificationServer(); // NOLINT return instance; } void NotificationServer::switchGeneration(bool reEmit, const std::function& clearHook) { auto notifications = this->mNotifications.valueList(); this->mNotifications.valueList().clear(); this->idMap.clear(); clearHook(); if (reEmit) { for (auto* notification: notifications) { notification->setLastGeneration(); notification->setTracked(false); emit this->notification(notification); if (!notification->isTracked()) { emit this->NotificationClosed(notification->id(), notification->closeReason()); delete notification; } else { this->idMap.insert(notification->id(), notification); this->mNotifications.insertObject(notification); } } } else { for (auto* notification: notifications) { emit this->NotificationClosed(notification->id(), NotificationCloseReason::Expired); delete notification; } } } ObjectModel* NotificationServer::trackedNotifications() { return &this->mNotifications; } void NotificationServer::deleteNotification( Notification* notification, NotificationCloseReason::Enum reason ) { if (!this->idMap.contains(notification->id())) return; emit notification->closed(reason); this->mNotifications.removeObject(notification); this->idMap.remove(notification->id()); emit this->NotificationClosed(notification->id(), reason); delete notification; } void NotificationServer::tryRegister() { auto bus = QDBusConnection::sessionBus(); auto success = bus.registerService("org.freedesktop.Notifications"); if (success) { qCInfo(logNotifications) << "Registered notification server with dbus."; } else { qCWarning(logNotifications ) << "Could not register notification server at org.freedesktop.Notifications, presumably " "because one is already registered."; qCWarning(logNotifications ) << "Registration will be attempted again if the active service is unregistered."; } } void NotificationServer::onServiceUnregistered(const QString& /*unused*/) { qCDebug(logNotifications) << "Active notification server unregistered, attempting registration"; this->tryRegister(); } void NotificationServer::CloseNotification(uint id) { auto* notification = this->idMap.value(id); if (notification) { this->deleteNotification(notification, NotificationCloseReason::CloseRequested); } } QStringList NotificationServer::GetCapabilities() const { auto capabilities = QStringList(); if (this->support.persistence) capabilities += "persistence"; if (this->support.body) { capabilities += "body"; if (this->support.bodyMarkup) capabilities += "body-markup"; if (this->support.bodyHyperlinks) capabilities += "body-hyperlinks"; if (this->support.bodyImages) capabilities += "body-images"; } if (this->support.actions) { capabilities += "actions"; if (this->support.actionIcons) capabilities += "action-icons"; } if (this->support.image) capabilities += "icon-static"; capabilities += this->support.extraHints; return capabilities; } QString NotificationServer::GetServerInformation(QString& vendor, QString& version, QString& specVersion) { vendor = "quickshell"; version = QCoreApplication::applicationVersion(); specVersion = "1.2"; return "quickshell"; } uint NotificationServer::Notify( const QString& appName, uint replacesId, const QString& appIcon, const QString& summary, const QString& body, const QStringList& actions, const QVariantMap& hints, int expireTimeout ) { auto* notification = replacesId == 0 ? nullptr : this->idMap.value(replacesId); auto old = notification != nullptr; if (!notification) { notification = new Notification(this->nextId++, this); QQmlEngine::setObjectOwnership(notification, QQmlEngine::CppOwnership); } notification->updateProperties(appName, appIcon, summary, body, actions, hints, expireTimeout); if (!old) { emit this->notification(notification); if (!notification->isTracked()) { emit this->NotificationClosed(notification->id(), notification->closeReason()); delete notification; } else { this->idMap.insert(notification->id(), notification); this->mNotifications.insertObject(notification); } } return notification->id(); } } // namespace qs::service::notifications