quickshell/src/services/notifications/server.cpp

205 lines
5.8 KiB
C++

#include "server.hpp"
#include <functional>
#include <qcontainerfwd.h>
#include <qcoreapplication.h>
#include <qdbusconnection.h>
#include <qdbusmetatype.h>
#include <qdbusservicewatcher.h>
#include <qlogging.h>
#include <qloggingcategory.h>
#include <qqmlengine.h>
#include <qtmetamacros.h>
#include <qtypes.h>
#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<DBusNotificationImage>();
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<void()>& 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<Notification>* 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