From 13278d518ae4055da0c8d63fe4de8029890a4a49 Mon Sep 17 00:00:00 2001 From: Arif Hasanic Date: Sat, 31 Jan 2026 22:46:19 +0100 Subject: [PATCH] add notifications --- CMakeLists.txt | 2 + include/app.hpp | 4 +- include/services/notification.hpp | 75 ++++++++++++++++++++++ include/widgets/notification.hpp | 10 +++ resources/bar.css | 9 +++ src/app.cpp | 7 ++- src/components/workspaceIndicator.cpp | 2 - src/services/hyprland.cpp | 1 - src/services/notification.cpp | 89 +++++++++++++++++++++++++++ src/widgets/notification.cpp | 34 ++++++++++ 10 files changed, 226 insertions(+), 7 deletions(-) create mode 100644 include/services/notification.hpp create mode 100644 include/widgets/notification.hpp create mode 100644 src/services/notification.cpp create mode 100644 src/widgets/notification.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index bcd3560..5b169e9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,10 +28,12 @@ target_sources(bar_lib src/widgets/clock.cpp src/widgets/date.cpp + src/widgets/notification.cpp src/widgets/volumeWidget.cpp src/widgets/webWidget.cpp src/services/hyprland.cpp + src/services/notification.cpp src/services/tray.cpp src/widgets/tray.cpp diff --git a/include/app.hpp b/include/app.hpp index f67ba9c..5f261bd 100644 --- a/include/app.hpp +++ b/include/app.hpp @@ -4,6 +4,7 @@ #include "bar/bar.hpp" #include "services/hyprland.hpp" +#include "services/notification.hpp" #include "glibmm/refptr.h" #include "gtkmm/application.h" @@ -17,8 +18,9 @@ class App { private: Glib::RefPtr app; std::vector> bars; + std::shared_ptr notificationService = nullptr; HyprlandService *hyprlandService = nullptr; - TrayService *trayService = TrayService::getInstance(); + TrayService *trayService = TrayService::getInstance(); void setupServices(); }; \ No newline at end of file diff --git a/include/services/notification.hpp b/include/services/notification.hpp new file mode 100644 index 0000000..5118667 --- /dev/null +++ b/include/services/notification.hpp @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + + +#include "gdkmm/monitor.h" +#include "giomm/dbusconnection.h" +#include "giomm/dbusownname.h" +#include "glib.h" + +const Glib::ustring introspection_xml = R"( + + + + + + + + + + + + + + + + + + + + + + + + + + + +)"; + +class NotificationService { + + public: + NotificationService() : notificationIdCounter(1) { + + Gio::DBus::own_name( + Gio::DBus::BusType::SESSION, + "org.freedesktop.Notifications", + sigc::mem_fun(*this, &NotificationService::onBusAcquired), + {}, // Name acquired slot (optional) + {}, // Name lost slot (optional) + Gio::DBus::BusNameOwnerFlags::REPLACE); + } + void onBusAcquired(const Glib::RefPtr &connection, const Glib::ustring &name); + + private: + guint notificationIdCounter; + const Gio::DBus::InterfaceVTable &getMessageInterfaceVTable(); + void on_method_call(const Glib::RefPtr &connection, + const Glib::ustring &sender, + const Glib::ustring &object_path, + const Glib::ustring &interface_name, + const Glib::ustring &method_name, + const Glib::VariantContainerBase ¶meters, + const Glib::RefPtr &invocation); + + void handle_notify(const Glib::VariantContainerBase ¶meters, + const Glib::RefPtr &invocation); + void createNotificationPopup(const Glib::ustring &title, const Glib::ustring &message); +}; diff --git a/include/widgets/notification.hpp b/include/widgets/notification.hpp new file mode 100644 index 0000000..781ea90 --- /dev/null +++ b/include/widgets/notification.hpp @@ -0,0 +1,10 @@ +#pragma once + +#include +#include +#include +#include "gdkmm/monitor.h" +class NotificationWidget { + public: + NotificationWidget(std::shared_ptr monitor, const Glib::ustring &title, const Glib::ustring &message); +}; \ No newline at end of file diff --git a/resources/bar.css b/resources/bar.css index 543c22d..423e9a6 100644 --- a/resources/bar.css +++ b/resources/bar.css @@ -142,3 +142,12 @@ button { opacity: 1; } } + +.notification-popup { + border-radius: 8px; + padding: 8px 12px; + background: rgba(30, 30, 30, 0.948); + box-shadow: 0 4px 30px rgba(0, 0, 0, 0.2); + border: 1px solid rgba(80, 80, 80, 0.8); + font-size: 14px; +} diff --git a/src/app.cpp b/src/app.cpp index b9096ef..2eb2805 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -1,13 +1,14 @@ #include "app.hpp" -#include -#include #include +#include +#include "services/notification.hpp" App::App() { this->app = Gtk::Application::create("org.example.mybar"); this->setupServices(); this->hyprlandService = HyprlandService::getInstance(); + this->notificationService = std::make_shared(); app->signal_activate().connect([&]() { auto display = Gdk::Display::get_default(); @@ -17,7 +18,7 @@ App::App() { auto monitor = std::dynamic_pointer_cast( monitors->get_object(i) ); - + if (monitor) { auto bar = std::make_shared(monitor->gobj()); diff --git a/src/components/workspaceIndicator.cpp b/src/components/workspaceIndicator.cpp index e63a58c..7797cef 100644 --- a/src/components/workspaceIndicator.cpp +++ b/src/components/workspaceIndicator.cpp @@ -1,6 +1,4 @@ #include "components/workspaceIndicator.hpp" -#include -#include "services/hyprland.hpp" #include "gtkmm/gestureclick.h" #include "gtkmm/label.h" diff --git a/src/services/hyprland.cpp b/src/services/hyprland.cpp index 3d43610..bba56af 100644 --- a/src/services/hyprland.cpp +++ b/src/services/hyprland.cpp @@ -1,6 +1,5 @@ #include "services/hyprland.hpp" -#include #include #include #include diff --git a/src/services/notification.cpp b/src/services/notification.cpp new file mode 100644 index 0000000..37c6d1b --- /dev/null +++ b/src/services/notification.cpp @@ -0,0 +1,89 @@ +#include "services/notification.hpp" + +#include +#include "widgets/notification.hpp" +#include "gtkmm/object.h" + +void NotificationService::onBusAcquired(const Glib::RefPtr &connection, const Glib::ustring &name) { + std::cout << "Acquired bus name: " << name << std::endl; + + auto introspection_data = Gio::DBus::NodeInfo::create_for_xml(introspection_xml); + + // Register the object at the standard path + connection->register_object( + "/org/freedesktop/Notifications", + introspection_data->lookup_interface("org.freedesktop.Notifications"), + getMessageInterfaceVTable()); +} + +const Gio::DBus::InterfaceVTable &NotificationService::getMessageInterfaceVTable() { + static Gio::DBus::InterfaceVTable vtable( + sigc::mem_fun(*this, &NotificationService::on_method_call)); + return vtable; +} + +void NotificationService::on_method_call(const Glib::RefPtr &, + const Glib::ustring &, + const Glib::ustring &, + const Glib::ustring &, + const Glib::ustring &method_name, + const Glib::VariantContainerBase ¶meters, + const Glib::RefPtr &invocation) { + + if (method_name == "Notify") { + handle_notify(parameters, invocation); + } else if (method_name == "GetCapabilities") { + auto caps = std::vector{"body"}; + invocation->return_value(Glib::VariantContainerBase::create_tuple( + Glib::Variant>::create(caps))); + } else if (method_name == "GetServerInformation") { + invocation->return_value(Glib::VariantContainerBase::create_tuple({Glib::Variant::create("MyGtkmmNotifier"), + Glib::Variant::create("Custom"), + Glib::Variant::create("1.0"), + Glib::Variant::create("1.2")})); + } else { + // Handle other methods or return error + invocation->return_value(Glib::VariantContainerBase()); + } +} + +void NotificationService::handle_notify(const Glib::VariantContainerBase ¶meters, + const Glib::RefPtr &invocation) { + + Glib::VariantBase app_name_var, replaces_id_var, app_icon_var, summary_var, body_var, actions_var, hints_var, timeout_var; + parameters.get_child(app_name_var, 0); + parameters.get_child(summary_var, 3); + parameters.get_child(body_var, 4); + + Glib::ustring summary = Glib::VariantBase::cast_dynamic>(summary_var).get(); + Glib::ustring body = Glib::VariantBase::cast_dynamic>(body_var).get(); + + std::cout << "Notification Received: " << summary << " - " << body << std::endl; + + createNotificationPopup(summary, body); + + guint id = notificationIdCounter++; + invocation->return_value(Glib::VariantContainerBase::create_tuple( + Glib::Variant::create(id))); +} + +void NotificationService::createNotificationPopup(const Glib::ustring &title, const Glib::ustring &message) { + auto display = Gdk::Display::get_default(); + if (!display) { + std::cerr << "Error: No default display found" << std::endl; + return; + } + + auto monitors = display->get_monitors(); + if (!monitors) { + std::cerr << "Error: No monitors found" << std::endl; + return; + } + + for (guint i = 0; i < monitors->get_n_items(); ++i) { + auto monitor = std::dynamic_pointer_cast(monitors->get_object(i)); + if (!monitor) continue; + + auto widget = std::make_shared(monitor, title, message); + } +} diff --git a/src/widgets/notification.cpp b/src/widgets/notification.cpp new file mode 100644 index 0000000..f587c3b --- /dev/null +++ b/src/widgets/notification.cpp @@ -0,0 +1,34 @@ +#include "widgets/notification.hpp" + +#include "gtk4-layer-shell.h" + +NotificationWidget::NotificationWidget(std::shared_ptr monitor, const Glib::ustring &title, const Glib::ustring &message) { + if (!monitor) return; + + auto win = new Gtk::Window(); + win->set_title(title); + win->set_default_size(300, 100); + + auto label = Gtk::make_managed(message); + label->set_use_markup(true); + win->set_child(*label); + + gtk_layer_init_for_window(win->gobj()); + gtk_layer_set_monitor(win->gobj(), monitor->gobj()); + gtk_layer_set_layer(win->gobj(), GTK_LAYER_SHELL_LAYER_OVERLAY); + gtk_layer_set_anchor(win->gobj(), GTK_LAYER_SHELL_EDGE_TOP, TRUE); + gtk_layer_set_anchor(win->gobj(), GTK_LAYER_SHELL_EDGE_RIGHT, TRUE); + gtk_layer_set_margin(win->gobj(), GTK_LAYER_SHELL_EDGE_TOP, 2); + + win->add_css_class("notification-popup"); + + win->show(); + + // Auto close after 3 seconds for demo purposes + Glib::signal_timeout().connect([win]() { + win->close(); + delete win; + return false; // Don't repeat + }, + 3000); +}