Skip to content

Commit

Permalink
Notifications: make daemon a singleton (#168)
Browse files Browse the repository at this point in the history
* make notification's Daemon a singleton instead of a namespace

* fotmatting
  • Loading branch information
NamorNiradnug authored Aug 6, 2023
1 parent 64c2aab commit a7aaf8b
Show file tree
Hide file tree
Showing 6 changed files with 165 additions and 167 deletions.
1 change: 1 addition & 0 deletions src/panel/panel.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,7 @@ class WayfirePanel::impl
Gtk::HBox& box)
{
const auto lock_sn_watcher = Watcher::Instance();
const auto lock_notification_daemon = Daemon::Instance();
container.clear();
auto widgets = tokenize(list);
for (auto widget_name : widgets)
Expand Down
166 changes: 48 additions & 118 deletions src/panel/widgets/notifications/daemon.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,6 @@
#define FDN_PATH "/org/freedesktop/Notifications"
#define FDN_NAME "org.freedesktop.Notifications"

namespace Daemon
{
static std::map<Notification::id_type, const Notification> notifications;

const auto introspection_data =
Gio::DBus::NodeInfo::create_for_xml("<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
"<node name=\"" FDN_PATH "\">"
Expand Down Expand Up @@ -58,67 +54,22 @@ const auto introspection_data =
"</node>")
->lookup_interface();

bool is_running = false;
guint owner_id = 0;

// used to emit dbus signals
Glib::RefPtr<Gio::DBus::Connection> connection;

notification_signal signal_notification_new;
notification_signal signal_notification_replaced;
notification_signal signal_notification_closed;

notification_signal signalNotificationNew()
{
return signal_notification_new;
}

notification_signal signalNotificationReplaced()
{
return signal_notification_replaced;
}

notification_signal signalNotificationClosed()
{
return signal_notification_closed;
}

sigc::signal<void> signal_daemon_stopped;

sigc::signal<void> signalDaemonStopped()
{
return signal_daemon_stopped;
}

using DBusMethod =
void (*)(const Glib::RefPtr<Gio::DBus::Connection> & connection, const Glib::ustring & sender,
const Glib::VariantContainerBase & parameters,
const Glib::RefPtr<Gio::DBus::MethodInvocation> & invocation);

#define dbus_method(name) \
static void dbusMethod ## name(const Glib::RefPtr<Gio::DBus::Connection> & connection, \
const Glib::ustring & sender, \
const Glib::VariantContainerBase & parameters, \
const Glib::RefPtr<Gio::DBus::MethodInvocation> & invocation)

dbus_method(GetCapabilities)
dbus_method(Daemon::GetCapabilities)
{
static const auto value = Glib::Variant<std::tuple<std::vector<Glib::ustring>>>::create(
{{"action-icons", "actions", "body", "body-hyperlinks", "body-markup", "body-images",
"persistance"}});
invocation->return_value(value);
connection->flush();
}

dbus_method(Notify)
dbus_method(Daemon::Notify)
try {
const auto notification = Notification(parameters, sender);
const auto id = notification.id;
const auto id_var =
Glib::VariantContainerBase::create_tuple(Glib::Variant<Notification::id_type>::create(id));

invocation->return_value(id_var);
connection->flush();

bool is_replacing = notifications.count(id) == 1;
if (is_replacing)
Expand All @@ -140,97 +91,82 @@ try {
std::cerr << "Error at " << __PRETTY_FUNCTION__ << ": " << err.what() << std::endl;
}

dbus_method(CloseNotification)
dbus_method(Daemon::CloseNotification)
{
Glib::VariantBase id_var;
parameters.get_child(id_var, 0);
invocation->return_value(Glib::VariantContainerBase());
closeNotification(Glib::VariantBase::cast_dynamic<Glib::Variant<Notification::id_type>>(id_var).get(),
CloseReason::MethodCalled);
Daemon::Instance()->closeNotification(
Glib::VariantBase::cast_dynamic<Glib::Variant<Notification::id_type>>(id_var).get(),
Daemon::CloseReason::MethodCalled);
}

dbus_method(GetServerInformation)
dbus_method(Daemon::GetServerInformation)
{
static const auto info =
Glib::Variant<std::tuple<Glib::ustring, Glib::ustring, Glib::ustring, Glib::ustring>>::create(
{"wf-panel", "wayfire.org", "0.8.0", "1.2"});
invocation->return_value(info);
connection->flush();
}

#undef dbus_method

void on_interface_method_call(const Glib::RefPtr<Gio::DBus::Connection> & connection,
const Glib::ustring & sender,
const Glib::ustring & object_path, const Glib::ustring & interface_name,
const Glib::ustring & method_name, const Glib::VariantContainerBase & parameters,
void Daemon::on_interface_method_call(const Glib::RefPtr<Gio::DBus::Connection> & connection,
const Glib::ustring & sender, const Glib::ustring & object_path,
const Glib::ustring & interface_name, const Glib::ustring & method_name,
const Glib::VariantContainerBase & parameters,
const Glib::RefPtr<Gio::DBus::MethodInvocation> & invocation)
{
#define DBUS_METHOD_PAIR(name) \
{ \
#name, dbusMethod ## name \
}

static const std::map<Glib::ustring, DBusMethod> methods = {
DBUS_METHOD_PAIR(GetCapabilities), DBUS_METHOD_PAIR(Notify), DBUS_METHOD_PAIR(CloseNotification),
DBUS_METHOD_PAIR(GetServerInformation)};

#undef DBUS_METHOD_PAIR
#define try_invoke_method(_name) \
if (method_name == #_name) \
_name ## dbus_method(sender, parameters, invocation)

if (methods.count(method_name) != 0)
{
methods.at(method_name)(connection, sender, parameters, invocation);
} else
{
std::cerr << "Notifications: Error: no such method " << method_name << "\n";
}
try_invoke_method(GetCapabilities);
try_invoke_method(Notify);
try_invoke_method(CloseNotification);
try_invoke_method(GetServerInformation);
}

const auto interface_vtable = Gio::DBus::InterfaceVTable(&on_interface_method_call);

void on_bus_acquired(const Glib::RefPtr<Gio::DBus::Connection> & connection, const Glib::ustring & name)
void Daemon::on_bus_acquired(const Glib::RefPtr<Gio::DBus::Connection> & connection,
const Glib::ustring & name)
{
connection->register_object(FDN_PATH, introspection_data, interface_vtable);
object_id = connection->register_object(FDN_PATH, introspection_data, interface_vtable);
daemon_connection = connection;
}

void on_name_acquired(const Glib::RefPtr<Gio::DBus::Connection> & connection, const Glib::ustring & name)
std::shared_ptr<Daemon> Daemon::Launch()
{
if (name == FDN_NAME)
if (instance.expired())
{
Daemon::connection = connection;
auto new_instance = std::shared_ptr<Daemon>(new Daemon());
instance = new_instance;
return new_instance;
}
}

void on_name_lost(const Glib::RefPtr<Gio::DBus::Connection> & connection, const Glib::ustring & name)
{
std::cerr << "Notifications: Error: DBus connection name has been lost.\n";
stop();
return Instance();
}

void start()
std::shared_ptr<Daemon> Daemon::Instance()
{
if (owner_id == 0)
{
owner_id = Gio::DBus::own_name(Gio::DBus::BUS_TYPE_SESSION, FDN_NAME, &on_bus_acquired,
&on_name_acquired,
&on_name_lost, Gio::DBus::BUS_NAME_OWNER_FLAGS_NONE);
}
return instance.lock();
}

void stop()
Daemon::Daemon() :
owner_id(Gio::DBus::own_name(Gio::DBus::BUS_TYPE_SESSION, FDN_NAME,
sigc::mem_fun(this, &Daemon::on_bus_acquired),
{}, {}, Gio::DBus::BUS_NAME_OWNER_FLAGS_REPLACE))
{}

Daemon::~Daemon()
{
signal_daemon_stopped.emit();
daemon_connection->unregister_object(object_id);
Gio::DBus::unown_name(owner_id);
owner_id = 0;
notifications.clear();
}

const std::map<Notification::id_type, const Notification> & getNotifications()
const std::map<Notification::id_type, const Notification>& Daemon::getNotifications() const
{
return notifications;
}

void closeNotification(Notification::id_type id, CloseReason reason)
void Daemon::closeNotification(Notification::id_type id, CloseReason reason)
{
if (notifications.count(id) == 0)
{
Expand All @@ -239,26 +175,20 @@ void closeNotification(Notification::id_type id, CloseReason reason)

signal_notification_closed.emit(id);
const auto & notification = notifications.at(id);
if (connection)
{
auto body = Glib::Variant<std::tuple<guint32, guint32>>::create({id, reason});
connection->emit_signal(FDN_PATH, FDN_NAME, "NotificationClosed", notification.additional_info.sender,
body);
}
const auto body = Glib::Variant<std::tuple<guint32, guint32>>::create({id, reason});
daemon_connection->emit_signal(FDN_PATH, FDN_NAME, "NotificationClosed",
notification.additional_info.sender, body);
}

void invokeAction(Notification::id_type id, const Glib::ustring & action_key)
void Daemon::invokeAction(Notification::id_type id, const Glib::ustring & action_key)
{
if (notifications.count(id) == 0)
{
return;
}

if (connection)
{
auto body = Glib::Variant<std::tuple<guint32, Glib::ustring>>::create({id, action_key});
connection->emit_signal(FDN_PATH, FDN_NAME, "ActionInvoked", notifications.at(
id).additional_info.sender, body);
}
const auto body = Glib::Variant<std::tuple<guint32, Glib::ustring>>::create({id, action_key});
daemon_connection->emit_signal(FDN_PATH, FDN_NAME, "ActionInvoked", notifications.at(
id).additional_info.sender,
body);
}
} // namespace Daemon
99 changes: 79 additions & 20 deletions src/panel/widgets/notifications/daemon.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,92 @@
#define NOTIFICATION_DAEMON_HPP

#include "notification-info.hpp"
#include <glibmm/object.h>

#include <giomm/dbusconnection.h>

#include <set>

namespace Daemon
{
enum CloseReason : guint32
#define dbus_method(name) \
void name ## dbus_method(const Glib::ustring & sender, const Glib::VariantContainerBase & parameters, \
const Glib::RefPtr<Gio::DBus::MethodInvocation> & invocation)
class Daemon
{
Expired = 1,
Dismissed = 2,
MethodCalled = 3,
Undefined = 4,
};
public:
enum CloseReason : guint32
{
Expired = 1,
Dismissed = 2,
MethodCalled = 3,
Undefined = 4,
};

using notification_signal = sigc::signal<void (Notification::id_type)>;

notification_signal signalNotificationNew()
{
return signal_notification_new;
}

notification_signal signalNotificationReplaced()
{
return signal_notification_replaced;
}

notification_signal signalNotificationClosed()
{
return signal_notification_closed;
}

/*!
* Initializes and launches the daemon.
*
* Returns a shared pointer to the instance.
* Once there are no alive shared pointers to the instance,
* the daemon is automatically destroyed.
*/
static std::shared_ptr<Daemon> Launch();

/*!
* Returns a pointer to the Daemon's instance if it exists
* or an empty `shared_ptr` otherwise.
*/
static std::shared_ptr<Daemon> Instance();

using notification_signal = sigc::signal<void (Notification::id_type)>;
notification_signal signalNotificationNew();
notification_signal signalNotificationReplaced();
notification_signal signalNotificationClosed();
~Daemon();

sigc::signal<void> signalDaemonStopped();
const std::map<Notification::id_type, const Notification> & getNotifications() const;
void closeNotification(Notification::id_type id, CloseReason reason);
void invokeAction(Notification::id_type id, const Glib::ustring & action_key);

void start();
void stop();
private:
inline static std::weak_ptr<Daemon> instance;

const std::map<Notification::id_type, const Notification> & getNotifications();
void closeNotification(Notification::id_type id, CloseReason reason);
void invokeAction(Notification::id_type id, const Glib::ustring & action_key);
} // namespace Daemon
std::map<Notification::id_type, const Notification> notifications;

guint owner_id = 0;
guint object_id;
Glib::RefPtr<Gio::DBus::Connection> daemon_connection;

notification_signal signal_notification_new;
notification_signal signal_notification_replaced;
notification_signal signal_notification_closed;

const Gio::DBus::InterfaceVTable interface_vtable{sigc::mem_fun(this, &Daemon::on_interface_method_call)};

Daemon();

void on_interface_method_call(const Glib::RefPtr<Gio::DBus::Connection> & connection,
const Glib::ustring & sender,
const Glib::ustring & object_path, const Glib::ustring & interface_name,
const Glib::ustring & method_name, const Glib::VariantContainerBase & parameters,
const Glib::RefPtr<Gio::DBus::MethodInvocation> & invocation);

dbus_method(GetCapabilities);
dbus_method(Notify);
dbus_method(CloseNotification);
dbus_method(GetServerInformation);

void on_bus_acquired(const Glib::RefPtr<Gio::DBus::Connection> & connection, const Glib::ustring & name);
};

#endif
Loading

0 comments on commit a7aaf8b

Please sign in to comment.