diff --git a/include/ALabel.hpp b/include/ALabel.hpp index 00daf3c8..74582cf1 100644 --- a/include/ALabel.hpp +++ b/include/ALabel.hpp @@ -7,7 +7,7 @@ namespace waybar { class ALabel : public IModule { public: - ALabel(const Json::Value&, const std::string format); + ALabel(const Json::Value&, const std::string& format); virtual ~ALabel() = default; virtual auto update() -> void; virtual std::string getIcon(uint16_t, const std::string& alt = ""); @@ -24,7 +24,7 @@ class ALabel : public IModule { bool handleToggle(GdkEventButton* const& ev); bool handleScroll(GdkEventScroll*); bool alt = false; - const std::string default_format_; + const std::string& default_format_; }; } // namespace waybar diff --git a/include/modules/battery.hpp b/include/modules/battery.hpp index 29b89b6f..f4fec2ea 100644 --- a/include/modules/battery.hpp +++ b/include/modules/battery.hpp @@ -1,9 +1,9 @@ #pragma once #ifdef FILESYSTEM_EXPERIMENTAL - #include +#include #else - #include +#include #endif #include #include diff --git a/include/modules/sni/host.hpp b/include/modules/sni/host.hpp new file mode 100644 index 00000000..7115a950 --- /dev/null +++ b/include/modules/sni/host.hpp @@ -0,0 +1,40 @@ +#pragma once + +#include +#include +#include +#include +#include "modules/sni/item.hpp" + +namespace waybar::modules::SNI { + +class Host { + public: + Host(const Json::Value&, const std::function&)>&, + const std::function&)>&); + ~Host(); + private: + void busAcquired(const Glib::RefPtr&, Glib::ustring); + void nameAppeared(const Glib::RefPtr&, Glib::ustring, const Glib::ustring&); + void nameVanished(const Glib::RefPtr&, Glib::ustring); + static void proxyReady(GObject*, GAsyncResult*, gpointer); + static void registerHost(GObject*, GAsyncResult*, gpointer); + static void itemRegistered(SnWatcher*, const gchar*, gpointer); + static void itemUnregistered(SnWatcher*, const gchar*, gpointer); + + std::tuple getBusNameAndObjectPath(const std::string); + void addRegisteredItem(std::string service); + + std::vector> items_; + const std::string bus_name_; + const std::string object_path_; + std::size_t bus_name_id_; + std::size_t watcher_id_; + GCancellable* cancellable_ = nullptr; + SnWatcher* watcher_ = nullptr; + const Json::Value &config_; + std::function&)> on_add_; + std::function&)> on_remove_; +}; + +} diff --git a/include/modules/sni/sni.hpp b/include/modules/sni/item.hpp similarity index 72% rename from include/modules/sni/sni.hpp rename to include/modules/sni/item.hpp index a374e6af..2c1ce7ba 100644 --- a/include/modules/sni/sni.hpp +++ b/include/modules/sni/item.hpp @@ -5,24 +5,25 @@ #include #include #ifdef FILESYSTEM_EXPERIMENTAL - #include +#include #else - #include +#include #endif namespace waybar::modules::SNI { class Item { public: - Item(std::string, std::string, Glib::Dispatcher*, Json::Value); + Item(std::string, std::string, const Json::Value&); + ~Item() = default; std::string bus_name; std::string object_path; - Gtk::EventBox *event_box; int icon_size; int effective_icon_size; - Gtk::Image *image; + Gtk::Image image; + Gtk::EventBox event_box; std::string category; std::string id; std::string status; @@ -35,26 +36,25 @@ public: std::string attention_icon_name; std::string attention_movie_name; std::string icon_theme_path; - DbusmenuGtkMenu *menu = nullptr; + std::string menu; + DbusmenuGtkMenu *dbus_menu = nullptr; Gtk::Menu *gtk_menu = nullptr; bool item_is_menu; private: static void proxyReady(GObject *obj, GAsyncResult *res, gpointer data); static void getAll(GObject *obj, GAsyncResult *res, gpointer data); - static void handleActivate(GObject *, GAsyncResult *, gpointer); - static void handleSecondaryActivate(GObject *, GAsyncResult *, gpointer); void updateImage(); - bool showMenu(GdkEventButton *const &ev); Glib::RefPtr extractPixBuf(GVariant *variant); Glib::RefPtr getIconByName(std::string name, int size); + static void onMenuDestroyed(Item *self); + bool makeMenu(GdkEventButton *const &ev); bool handleClick(GdkEventButton *const & /*ev*/); - Glib::Dispatcher *dp_; + Glib::RefPtr conn_; GCancellable *cancellable_ = nullptr; SnItem *proxy_ = nullptr; - Json::Value config_; }; } // namespace waybar::modules::SNI diff --git a/include/modules/sni/snh.hpp b/include/modules/sni/snh.hpp deleted file mode 100644 index 057d7323..00000000 --- a/include/modules/sni/snh.hpp +++ /dev/null @@ -1,38 +0,0 @@ -#pragma once - -#include -#include -#include -#include -#include "modules/sni/sni.hpp" - -namespace waybar::modules::SNI { - -class Host { - public: - Host(Glib::Dispatcher*, const Json::Value&); - std::vector items; - private: - static void busAcquired(GDBusConnection*, const gchar*, gpointer); - static void nameAppeared(GDBusConnection*, const gchar*, const gchar*, - gpointer); - static void nameVanished(GDBusConnection*, const gchar*, gpointer); - static void proxyReady(GObject*, GAsyncResult*, gpointer); - static void registerHost(GObject*, GAsyncResult*, gpointer); - static void itemRegistered(SnWatcher*, const gchar*, gpointer); - static void itemUnregistered(SnWatcher*, const gchar*, gpointer); - - std::tuple getBusNameAndObjectPath(const gchar*); - void addRegisteredItem(const gchar* service); - - uint32_t bus_name_id_; - uint32_t watcher_id_; - std::string bus_name_; - std::string object_path_; - Glib::Dispatcher* dp_; - GCancellable* cancellable_ = nullptr; - SnWatcher* watcher_ = nullptr; - const Json::Value &config_; -}; - -} diff --git a/include/modules/sni/tray.hpp b/include/modules/sni/tray.hpp index 5be478f4..fbc31419 100644 --- a/include/modules/sni/tray.hpp +++ b/include/modules/sni/tray.hpp @@ -4,8 +4,8 @@ #include #include "util/json.hpp" #include "IModule.hpp" -#include "modules/sni/snw.hpp" -#include "modules/sni/snh.hpp" +#include "modules/sni/watcher.hpp" +#include "modules/sni/host.hpp" namespace waybar::modules::SNI { @@ -15,6 +15,9 @@ class Tray : public IModule { auto update() -> void; operator Gtk::Widget &(); private: + void onAdd(std::unique_ptr& item); + void onRemove(std::unique_ptr& item); + std::thread thread_; const Json::Value& config_; Gtk::Box box_; diff --git a/include/modules/sni/snw.hpp b/include/modules/sni/watcher.hpp similarity index 100% rename from include/modules/sni/snw.hpp rename to include/modules/sni/watcher.hpp diff --git a/meson.build b/meson.build index 1d16890f..844cda7d 100644 --- a/meson.build +++ b/meson.build @@ -83,9 +83,9 @@ if dbusmenu_gtk.found() add_project_arguments('-DHAVE_DBUSMENU', language: 'cpp') src_files += files( 'src/modules/sni/tray.cpp', - 'src/modules/sni/snw.cpp', - 'src/modules/sni/snh.cpp', - 'src/modules/sni/sni.cpp' + 'src/modules/sni/watcher.cpp', + 'src/modules/sni/host.cpp', + 'src/modules/sni/item.cpp' ) endif diff --git a/protocol/dbus-status-notifier-item.xml b/protocol/dbus-status-notifier-item.xml index f8c3231a..e46eb3c6 100644 --- a/protocol/dbus-status-notifier-item.xml +++ b/protocol/dbus-status-notifier-item.xml @@ -1,78 +1,47 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + - - - + + + - - - + + + - - - - - - + + + + + + + + + + + - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/ALabel.cpp b/src/ALabel.cpp index 9dd590e5..a8d7b276 100644 --- a/src/ALabel.cpp +++ b/src/ALabel.cpp @@ -3,7 +3,7 @@ #include -waybar::ALabel::ALabel(const Json::Value& config, const std::string format) +waybar::ALabel::ALabel(const Json::Value& config, const std::string& format) : config_(config), format_(config_["format"].isString() ? config_["format"].asString() : format), default_format_(format_) diff --git a/src/modules/sni/snh.cpp b/src/modules/sni/host.cpp similarity index 51% rename from src/modules/sni/snh.cpp rename to src/modules/sni/host.cpp index 2daccee9..a5b07fb4 100644 --- a/src/modules/sni/snh.cpp +++ b/src/modules/sni/host.cpp @@ -1,57 +1,52 @@ -#include "modules/sni/snh.hpp" +#include "modules/sni/host.hpp" #include using namespace waybar::modules::SNI; -Host::Host(Glib::Dispatcher* dp, const Json::Value &config) -: dp_(dp), config_(config) +Host::Host(const Json::Value &config, const std::function&)>& on_add, + const std::function&)>& on_remove) +: bus_name_("org.kde.StatusNotifierHost-" + std::to_string(getpid()) + "-1"), + object_path_("/StatusNotifierHost"), + bus_name_id_(Gio::DBus::own_name(Gio::DBus::BusType::BUS_TYPE_SESSION, bus_name_, + sigc::mem_fun(*this, &Host::busAcquired))), + config_(config), on_add_(on_add), on_remove_(on_remove) { - GBusNameOwnerFlags flags = static_cast( - G_BUS_NAME_OWNER_FLAGS_NONE); - bus_name_ = "org.kde.StatusNotifierHost-" + std::to_string(getpid()); - object_path_ = "/StatusNotifierHost"; - bus_name_id_ = g_bus_own_name(G_BUS_TYPE_SESSION, - bus_name_.c_str(), flags, - &Host::busAcquired, nullptr, nullptr, this, nullptr); } -void Host::busAcquired(GDBusConnection* connection, - const gchar* name, gpointer data) +Host::~Host() { - auto host = static_cast(data); - host->watcher_id_ = g_bus_watch_name( - G_BUS_TYPE_SESSION, - "org.kde.StatusNotifierWatcher", - G_BUS_NAME_WATCHER_FLAGS_NONE, - &Host::nameAppeared, &Host::nameVanished, data, nullptr); + Gio::DBus::unwatch_name(bus_name_id_); } -void Host::nameAppeared(GDBusConnection* connection, - const gchar* name, const gchar* name_owner, gpointer data) +void Host::busAcquired(const Glib::RefPtr& conn, Glib::ustring name) { - auto host = static_cast(data); - if (host->cancellable_ != nullptr) { + watcher_id_ = Gio::DBus::watch_name(conn, "org.kde.StatusNotifierWatcher", + sigc::mem_fun(*this, &Host::nameAppeared), sigc::mem_fun(*this, &Host::nameVanished)); +} + +void Host::nameAppeared(const Glib::RefPtr& conn, const Glib::ustring name, + const Glib::ustring& name_owner) +{ + if (cancellable_ != nullptr) { // TODO return; } - host->cancellable_ = g_cancellable_new(); + cancellable_ = g_cancellable_new(); sn_watcher_proxy_new( - connection, + conn->gobj(), G_DBUS_PROXY_FLAGS_NONE, "org.kde.StatusNotifierWatcher", "/StatusNotifierWatcher", - host->cancellable_, &Host::proxyReady, data); + cancellable_, &Host::proxyReady, this); } -void Host::nameVanished(GDBusConnection* connection, - const gchar* name, gpointer data) +void Host::nameVanished(const Glib::RefPtr& conn, const Glib::ustring name) { - auto host = static_cast(data); - g_cancellable_cancel(host->cancellable_); - g_clear_object(&host->cancellable_); - g_clear_object(&host->watcher_); - host->items.clear(); + g_cancellable_cancel(cancellable_); + g_clear_object(&cancellable_); + g_clear_object(&watcher_); + items_.clear(); } void Host::proxyReady(GObject* src, GAsyncResult* res, @@ -105,8 +100,7 @@ void Host::registerHost(GObject* src, GAsyncResult* res, g_strfreev(items); } -void Host::itemRegistered( - SnWatcher* watcher, const gchar* service, gpointer data) +void Host::itemRegistered(SnWatcher* watcher, const gchar* service, gpointer data) { auto host = static_cast(data); host->addRegisteredItem(service); @@ -117,35 +111,28 @@ void Host::itemUnregistered( { auto host = static_cast(data); auto [bus_name, object_path] = host->getBusNameAndObjectPath(service); - for (auto it = host->items.begin(); it != host->items.end(); ++it) { - if (it->bus_name == bus_name && it->object_path == object_path) { - host->items.erase(it); + for (auto it = host->items_.begin(); it != host->items_.end(); ++it) { + if ((*it)->bus_name == bus_name && (*it)->object_path == object_path) { + host->on_remove_(*it); + host->items_.erase(it); break; } } - host->dp_->emit(); } std::tuple Host::getBusNameAndObjectPath( - const gchar* service) + const std::string service) { - std::string bus_name; - std::string object_path; - gchar* tmp = g_strstr_len(service, -1, "/"); - if (tmp != nullptr) { - gchar** str = g_strsplit(service, "/", 2); - bus_name = str[0]; - object_path = tmp; - g_strfreev(str); - } else { - bus_name = service; - object_path = "/StatusNotifierItem"; + auto it = service.find("/"); + if (it != std::string::npos) { + return {service.substr(0, it), service.substr(it)}; } - return { bus_name, object_path }; + return {service, "/StatusNotifierItem"}; } -void Host::addRegisteredItem(const gchar* service) +void Host::addRegisteredItem(std::string service) { auto [bus_name, object_path] = getBusNameAndObjectPath(service); - items.emplace_back(bus_name, object_path, dp_, config_); + items_.emplace_back(new Item(bus_name, object_path, config_)); + on_add_(items_.back()); } diff --git a/src/modules/sni/sni.cpp b/src/modules/sni/item.cpp similarity index 78% rename from src/modules/sni/sni.cpp rename to src/modules/sni/item.cpp index 75be7936..de1ebd45 100644 --- a/src/modules/sni/sni.cpp +++ b/src/modules/sni/item.cpp @@ -1,19 +1,16 @@ -#include "modules/sni/sni.hpp" +#include "modules/sni/item.hpp" #include -waybar::modules::SNI::Item::Item(std::string bn, std::string op, - Glib::Dispatcher *dp, Json::Value config) - : bus_name(bn), object_path(op), event_box(Gtk::manage(new Gtk::EventBox())), - icon_size(16), effective_icon_size(0), image(Gtk::manage(new Gtk::Image())), - dp_(dp), config_(config) +waybar::modules::SNI::Item::Item(std::string bn, std::string op, const Json::Value& config) + : bus_name(bn), object_path(op), icon_size(16), effective_icon_size(0) { - if (config_["icon-size"].isUInt()) { - icon_size = config_["icon-size"].asUInt(); + if (config["icon-size"].isUInt()) { + icon_size = config["icon-size"].asUInt(); } - event_box->add(*image); - event_box->add_events(Gdk::BUTTON_PRESS_MASK); - event_box->signal_button_press_event().connect( + event_box.add(image); + event_box.add_events(Gdk::BUTTON_PRESS_MASK); + event_box.signal_button_press_event().connect( sigc::mem_fun(*this, &Item::handleClick)); cancellable_ = g_cancellable_new(); sn_item_proxy_new_for_bus( @@ -94,12 +91,7 @@ void waybar::modules::SNI::Item::getAll(GObject *obj, GAsyncResult *res, } else if (g_strcmp0(key, "IconThemePath") == 0) { item->icon_theme_path = g_variant_dup_string(value, nullptr); } else if (g_strcmp0(key, "Menu") == 0) { - auto menu = g_variant_dup_string(value, nullptr); - if (menu != nullptr) { - item->menu = dbusmenu_gtkmenu_new(item->bus_name.data(), menu); - item->gtk_menu = Glib::wrap(GTK_MENU(g_object_ref_sink(item->menu)), false); - item->gtk_menu->attach_to_widget(*item->event_box); - } + item->menu = g_variant_dup_string(value, nullptr); } else if (g_strcmp0(key, "ItemIsMenu") == 0) { item->item_is_menu = g_variant_get_boolean(value); } @@ -119,8 +111,7 @@ void waybar::modules::SNI::Item::getAll(GObject *obj, GAsyncResult *res, item->icon_theme_path.c_str()); } item->updateImage(); - item->event_box->set_tooltip_text(item->title); - item->dp_->emit(); + // item->event_box.set_tooltip_text(item->title); // TODO: handle change } @@ -172,23 +163,10 @@ waybar::modules::SNI::Item::extractPixBuf(GVariant *variant) { return Glib::RefPtr{}; } -bool waybar::modules::SNI::Item::showMenu(GdkEventButton *const &ev) -{ - if (gtk_menu != nullptr) { - #if GTK_CHECK_VERSION(3, 22, 0) - gtk_menu->popup_at_pointer(reinterpret_cast(ev)); - #else - gtk_menu->popup(ev->button, ev->time); - #endif - return true; - } - return false; -} - void waybar::modules::SNI::Item::updateImage() { - image->set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); - image->set_pixel_size(icon_size); + image.set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); + image.set_pixel_size(icon_size); if (!icon_name.empty()) { try { // Try to find icons specified by path and filename @@ -203,10 +181,10 @@ void waybar::modules::SNI::Item::updateImage() // the tray pixbuf = pixbuf->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR); - image->set(pixbuf); + image.set(pixbuf); } } else { - image->set(getIconByName(icon_name, icon_size)); + image.set(getIconByName(icon_name, icon_size)); } } catch (Glib::Error &e) { std::cerr << "Exception: " << e.what() << std::endl; @@ -215,7 +193,7 @@ void waybar::modules::SNI::Item::updateImage() // An icon extracted may be the wrong size for the tray icon_pixmap = icon_pixmap->scale_simple(icon_size, icon_size, Gdk::InterpType::INTERP_BILINEAR); - image->set(icon_pixmap); + image.set(icon_pixmap); } } @@ -242,29 +220,47 @@ waybar::modules::SNI::Item::getIconByName(std::string name, int request_size) { Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); } -void waybar::modules::SNI::Item::handleActivate(GObject *src, GAsyncResult *res, - gpointer data) { - auto item = static_cast(data); - sn_item_call_activate_finish(item->proxy_, res, nullptr); +void waybar::modules::SNI::Item::onMenuDestroyed(Item *self) +{ + self->gtk_menu = nullptr; + self->dbus_menu = nullptr; } -void waybar::modules::SNI::Item::handleSecondaryActivate(GObject *src, - GAsyncResult *res, gpointer data) { - auto item = static_cast(data); - sn_item_call_secondary_activate_finish(item->proxy_, res, nullptr); +bool waybar::modules::SNI::Item::makeMenu(GdkEventButton *const &ev) +{ + std::cout << bus_name << std::endl; + if (gtk_menu == nullptr) { + if (!menu.empty()) { + dbus_menu = dbusmenu_gtkmenu_new(bus_name.data(), menu.data()); + if (dbus_menu != nullptr) { + g_object_ref_sink(G_OBJECT(dbus_menu)); + g_object_weak_ref(G_OBJECT(dbus_menu), (GWeakNotify)onMenuDestroyed, this); + gtk_menu = Glib::wrap(GTK_MENU(dbus_menu)); + gtk_menu->attach_to_widget(event_box); + } + } + } + if (gtk_menu != nullptr) { +#if GTK_CHECK_VERSION(3, 22, 0) + gtk_menu->popup_at_pointer(reinterpret_cast(ev)); +#else + gtk_menu->popup(ev->button, ev->time); +#endif + return true; + } + return false; } bool waybar::modules::SNI::Item::handleClick(GdkEventButton *const &ev) { - if (ev->type == GDK_BUTTON_PRESS) { - if (!showMenu(ev)) { - sn_item_call_activate( - proxy_, ev->x, ev->y, nullptr, &Item::handleActivate, this); + if ((ev->button == 1 && item_is_menu) || ev->button == 3) { + if (!makeMenu(ev)) { + return sn_item_call_context_menu_sync(proxy_, ev->x, ev->y, nullptr, nullptr); } - } else if (ev->type == GDK_2BUTTON_PRESS) { - sn_item_call_secondary_activate( - proxy_, ev->x, ev->y, nullptr, &Item::handleSecondaryActivate, this); - } else { - return false; + } else if (ev->button == 1) { + return sn_item_call_activate_sync(proxy_, ev->x, ev->y, nullptr, nullptr); + } else if (ev->button == 2) { + return sn_item_call_secondary_activate_sync(proxy_, ev->x, ev->y, + nullptr, nullptr); } - return true; + return false; } diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index e5ec9c1f..c119a337 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -3,7 +3,9 @@ #include waybar::modules::SNI::Tray::Tray(const Json::Value &config) - : config_(config), watcher_(), host_(&dp, config) + : config_(config), watcher_(), host_(config, + std::bind(&Tray::onAdd, this, std::placeholders::_1), + std::bind(&Tray::onRemove, this, std::placeholders::_1)) { std::cout << "Tray is in beta, so there may be bugs or even be unusable." << std::endl; if (config_["spacing"].isUInt()) { @@ -11,12 +13,19 @@ waybar::modules::SNI::Tray::Tray(const Json::Value &config) } } +void waybar::modules::SNI::Tray::onAdd(std::unique_ptr& item) +{ + box_.pack_start(item->event_box); + dp.emit(); +} + +void waybar::modules::SNI::Tray::onRemove(std::unique_ptr& item) +{ + box_.remove(item->event_box); + dp.emit(); +} + auto waybar::modules::SNI::Tray::update() -> void { - auto childrens = box_.get_children(); - // childrens.erase(childrens.begin(), childrens.end()); - for (auto &item : host_.items) { - box_.pack_start(*item.event_box); - } if (box_.get_children().size() > 0) { box_.set_name("tray"); box_.show_all(); diff --git a/src/modules/sni/snw.cpp b/src/modules/sni/watcher.cpp similarity index 99% rename from src/modules/sni/snw.cpp rename to src/modules/sni/watcher.cpp index de699082..0cb714e0 100644 --- a/src/modules/sni/snw.cpp +++ b/src/modules/sni/watcher.cpp @@ -1,4 +1,4 @@ -#include "modules/sni/snw.hpp" +#include "modules/sni/watcher.hpp" #include