From 108b1092e599d507c838015b8da95988c9ffdc7b Mon Sep 17 00:00:00 2001 From: topisani Date: Thu, 4 Oct 2018 18:03:01 +0200 Subject: [PATCH] WIP sni dbus-menu support. --- include/modules/sni/sni.hpp | 3 +- meson.build | 2 + protocol/meson.build | 6 +- src/modules/network.cpp | 4 +- src/modules/sni/snh.cpp | 2 +- src/modules/sni/sni.cpp | 149 ++++++++++++++++++++++-------------- src/modules/sni/snw.cpp | 2 +- src/modules/sni/tray.cpp | 19 ++--- 8 files changed, 112 insertions(+), 75 deletions(-) diff --git a/include/modules/sni/sni.hpp b/include/modules/sni/sni.hpp index eb57902c..60aee86a 100644 --- a/include/modules/sni/sni.hpp +++ b/include/modules/sni/sni.hpp @@ -11,7 +11,8 @@ class Item { ~Item(); int icon_size; int effective_icon_size; - Gtk::Image* image; + Gtk::Widget* widget = nullptr; + Gtk::Image* image = nullptr; std::string category; std::string id; std::string status; diff --git a/meson.build b/meson.build index 4705aab0..640c58e7 100644 --- a/meson.build +++ b/meson.build @@ -32,6 +32,7 @@ wayland_cursor = dependency('wayland-cursor') wayland_protos = dependency('wayland-protocols') wlroots = dependency('wlroots', fallback: ['wlroots', 'wlroots']) gtkmm = dependency('gtkmm-3.0') +dbusmenu_gtk = dependency('dbusmenu-gtk3-0.4') jsoncpp = dependency('jsoncpp') sigcpp = dependency('sigc++-2.0') libnl = dependency('libnl-3.0', required: false) @@ -90,6 +91,7 @@ executable( libinput, wayland_cursor, gtkmm, + dbusmenu_gtk, libnl, libnlgen, libpulse diff --git a/protocol/meson.build b/protocol/meson.build index 01cb771b..17fe3264 100644 --- a/protocol/meson.build +++ b/protocol/meson.build @@ -54,10 +54,14 @@ client_protos_headers += gdbus_header.process('./dbus-status-notifier-watcher.xm client_protos_src += gdbus_code.process('./dbus-status-notifier-item.xml') client_protos_headers += gdbus_header.process('./dbus-status-notifier-item.xml') +client_protos_src += gdbus_code.process('./dbus-menu.xml') +client_protos_headers += gdbus_header.process('./dbus-menu.xml') + lib_client_protos = static_library( 'client_protos', client_protos_src + client_protos_headers, - dependencies: [wayland_client, gtkmm] + dependencies: [wayland_client, gtkmm], + include_directories: include_directories('..'), ) # for the include directory client_protos = declare_dependency( diff --git a/src/modules/network.cpp b/src/modules/network.cpp index 44f2916c..94d5c7a4 100644 --- a/src/modules/network.cpp +++ b/src/modules/network.cpp @@ -142,7 +142,7 @@ int waybar::modules::Network::getExternalInterface() int ifidx = -1; /* Prepare request. */ - uint32_t reqlen = NLMSG_SPACE(sizeof(*rt)); + constexpr uint32_t reqlen = NLMSG_SPACE(sizeof(*rt)); char req[reqlen] = {0}; /* Build the RTM_GETROUTE request. */ @@ -228,7 +228,7 @@ int waybar::modules::Network::getExternalInterface() break; } for (uint32_t i = 0; i < dstlen; i += 1) { - c |= *(unsigned char *)(RTA_DATA(attr) + i); + c |= *((unsigned char *)RTA_DATA(attr) + i); } has_destination = (c == 0); break; diff --git a/src/modules/sni/snh.cpp b/src/modules/sni/snh.cpp index 49a6fb89..8bef40f1 100644 --- a/src/modules/sni/snh.cpp +++ b/src/modules/sni/snh.cpp @@ -145,4 +145,4 @@ void waybar::modules::SNI::Host::addRegisteredItem(const gchar* service) getBusNameAndObjectPath(service, &bus_name, &object_path); items.emplace_back(bus_name, object_path, dp_); -} \ No newline at end of file +} diff --git a/src/modules/sni/sni.cpp b/src/modules/sni/sni.cpp index 0cd06a71..60dd51b8 100644 --- a/src/modules/sni/sni.cpp +++ b/src/modules/sni/sni.cpp @@ -1,29 +1,27 @@ #include "modules/sni/sni.hpp" +#include + #include waybar::modules::SNI::Item::Item(std::string bus_name, std::string object_path, - Glib::Dispatcher& dp) - : icon_size(16), effective_icon_size(0), - image(Gtk::manage(new Gtk::Image())), - bus_name_(bus_name), object_path_(object_path), dp_(dp) -{ + Glib::Dispatcher &dp) + : icon_size(16), effective_icon_size(0), + image(Gtk::manage(new Gtk::Image())), bus_name_(bus_name), + object_path_(object_path), dp_(dp) { cancellable_ = g_cancellable_new(); - sn_org_kde_status_notifier_item_proxy_new_for_bus(G_BUS_TYPE_SESSION, - G_DBUS_PROXY_FLAGS_NONE, bus_name_.c_str(), object_path_.c_str(), - cancellable_, &Item::proxyReady, this); + sn_org_kde_status_notifier_item_proxy_new_for_bus( + G_BUS_TYPE_SESSION, G_DBUS_PROXY_FLAGS_NONE, bus_name_.c_str(), + object_path_.c_str(), cancellable_, &Item::proxyReady, this); } -waybar::modules::SNI::Item::~Item() -{ -} +waybar::modules::SNI::Item::~Item() {} -void waybar::modules::SNI::Item::proxyReady(GObject* obj, GAsyncResult* res, - gpointer data) -{ - GError* error = nullptr; - SnOrgKdeStatusNotifierItem* proxy = - sn_org_kde_status_notifier_item_proxy_new_for_bus_finish(res, &error); +void waybar::modules::SNI::Item::proxyReady(GObject *obj, GAsyncResult *res, + gpointer data) { + GError *error = nullptr; + SnOrgKdeStatusNotifierItem *proxy = + sn_org_kde_status_notifier_item_proxy_new_for_bus_finish(res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free(error); return; @@ -37,18 +35,24 @@ void waybar::modules::SNI::Item::proxyReady(GObject* obj, GAsyncResult* res, } auto conn = g_dbus_proxy_get_connection(G_DBUS_PROXY(proxy)); g_dbus_connection_call(conn, item->bus_name_.c_str(), - item->object_path_.c_str(), "org.freedesktop.DBus.Properties", "GetAll", - g_variant_new("(s)", "org.kde.StatusNotifierItem"), - G_VARIANT_TYPE("(a{sv})"), G_DBUS_CALL_FLAGS_NONE, -1, - item->cancellable_, &Item::getAll, data); + item->object_path_.c_str(), + "org.freedesktop.DBus.Properties", "GetAll", + g_variant_new("(s)", "org.kde.StatusNotifierItem"), + G_VARIANT_TYPE("(a{sv})"), G_DBUS_CALL_FLAGS_NONE, -1, + item->cancellable_, &Item::getAll, data); } -void waybar::modules::SNI::Item::getAll(GObject* obj, GAsyncResult* res, - gpointer data) -{ - GError* error = nullptr; +static auto nonull(const char *c) { + if (c != nullptr) + return c; + return ""; +} + +void waybar::modules::SNI::Item::getAll(GObject *obj, GAsyncResult *res, + gpointer data) { + GError *error = nullptr; auto conn = G_DBUS_CONNECTION(obj); - GVariant* properties = g_dbus_connection_call_finish(conn, res, &error); + GVariant *properties = g_dbus_connection_call_finish(conn, res, &error); if (g_error_matches(error, G_IO_ERROR, G_IO_ERROR_CANCELLED)) { g_error_free(error); return; @@ -59,41 +63,41 @@ void waybar::modules::SNI::Item::getAll(GObject* obj, GAsyncResult* res, g_error_free(error); return; } - GVariantIter* it = nullptr; + GVariantIter *it = nullptr; g_variant_get(properties, "(a{sv})", &it); - gchar* key; - GVariant* value; + gchar *key; + GVariant *value; while (g_variant_iter_next(it, "{sv}", &key, &value)) { if (g_strcmp0(key, "Category") == 0) { - item->category = g_variant_dup_string(value, nullptr); + item->category = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "Id") == 0) { - item->id = g_variant_dup_string(value, nullptr); + item->id = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "Title") == 0) { - item->title = g_variant_dup_string(value, nullptr); + item->title = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "Status") == 0) { - item->status = g_variant_dup_string(value, nullptr); + item->status = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "WindowId") == 0) { - item->window_id = g_variant_get_int32 (value); + item->window_id = g_variant_get_int32(value); } else if (g_strcmp0(key, "IconName") == 0) { - item->icon_name = g_variant_dup_string(value, nullptr); + item->icon_name = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "IconPixmap") == 0) { // TODO: icon pixmap } else if (g_strcmp0(key, "OverlayIconName") == 0) { - item->overlay_icon_name = g_variant_dup_string(value, nullptr); + item->overlay_icon_name = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "OverlayIconPixmap") == 0) { // TODO: overlay_icon_pixmap } else if (g_strcmp0(key, "AttentionIconName") == 0) { - item->attention_icon_name = g_variant_dup_string(value, nullptr); + item->attention_icon_name = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "AttentionIconPixmap") == 0) { // TODO: attention_icon_pixmap } else if (g_strcmp0(key, "AttentionMovieName") == 0) { - item->attention_movie_name = g_variant_dup_string(value, nullptr); + item->attention_movie_name = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "ToolTip") == 0) { // TODO: tooltip } else if (g_strcmp0(key, "IconThemePath") == 0) { - item->icon_theme_path = g_variant_dup_string(value, nullptr); + item->icon_theme_path = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "Menu") == 0) { - item->menu = g_variant_dup_string(value, nullptr); + item->menu = nonull(g_variant_dup_string(value, nullptr)); } else if (g_strcmp0(key, "ItemIsMenu") == 0) { item->item_is_menu = g_variant_get_boolean(value); } @@ -103,31 +107,37 @@ void waybar::modules::SNI::Item::getAll(GObject* obj, GAsyncResult* res, g_variant_iter_free(it); g_variant_unref(properties); if (item->id.empty() || item->category.empty() || item->status.empty()) { - std::cerr << "Invalid Status Notifier Item: " + item->bus_name_ + "," - + item->object_path_ << std::endl; + std::cerr << "Invalid Status Notifier Item: " + item->bus_name_ + "," + + item->object_path_ + << std::endl; return; } if (!item->icon_theme_path.empty()) { - GtkIconTheme* icon_theme = gtk_icon_theme_get_default(); + GtkIconTheme *icon_theme = gtk_icon_theme_get_default(); gtk_icon_theme_append_search_path(icon_theme, - item->icon_theme_path.c_str()); + item->icon_theme_path.c_str()); } item->updateImage(); item->dp_.emit(); // TODO: handle change } -void waybar::modules::SNI::Item::updateImage() -{ +void waybar::modules::SNI::Item::updateImage() { if (!icon_name.empty()) { auto pixbuf = getIconByName(icon_name, icon_size); if (pixbuf->gobj() == nullptr) { // Try to find icons specified by path and filename - pixbuf = Gdk::Pixbuf::create_from_file(icon_name); - if (pixbuf->gobj() != nullptr) { - // An icon specified by path and filename may be the wrong size for the tray - pixbuf->scale_simple(icon_size - 2, icon_size - 2, - Gdk::InterpType::INTERP_BILINEAR); + try { + pixbuf = Gdk::Pixbuf::create_from_file(icon_name); + if (pixbuf->gobj() != nullptr) { + // An icon specified by path and filename may be the wrong size for + // the tray + pixbuf->scale_simple(icon_size - 2, icon_size - 2, + Gdk::InterpType::INTERP_BILINEAR); + } + } catch (Glib::Error &e) { + std::cerr << "Exception: " << e.what() << std::endl; + pixbuf = getIconByName("image-missing", icon_size); } } if (pixbuf->gobj() == nullptr) { @@ -138,14 +148,37 @@ void waybar::modules::SNI::Item::updateImage() image->set_from_icon_name("image-missing", Gtk::ICON_SIZE_MENU); image->set_pixel_size(icon_size); } + auto *evt_box = Gtk::manage(new Gtk::EventBox()); + evt_box->add(*image); + widget = evt_box; + if (!menu.empty()) { + auto *dbmenu = dbusmenu_gtkmenu_new(bus_name_.data(), menu.data()); + Gtk::Menu *dmenu = Glib::wrap(GTK_MENU(dbmenu), false); + if (dbmenu && dmenu) { + widget->signal_button_press_event().connect( + [this, dmenu](GdkEventButton *btn) { + if (!dmenu->get_parent()) { + dmenu->reparent(*image); + } + if (!dmenu->get_attach_widget()) { + dmenu->attach_to_widget(*widget); + } + dmenu->popup(btn->button, btn->time); + return true; + }); + } + } else { + widget->signal_button_press_event().connect([this](GdkEventButton *btn) { + std::cout << this->menu << std::endl; + return true; + }); + } } -Glib::RefPtr waybar::modules::SNI::Item::getIconByName( - std::string name, int request_size) -{ +Glib::RefPtr +waybar::modules::SNI::Item::getIconByName(std::string name, int request_size) { int icon_size = 0; - Glib::RefPtr icon_theme = - Gtk::IconTheme::get_default(); + Glib::RefPtr icon_theme = Gtk::IconTheme::get_default(); icon_theme->rescan_if_needed(); auto sizes = icon_theme->get_icon_sizes(name.c_str()); for (auto size : sizes) { @@ -161,5 +194,5 @@ Glib::RefPtr waybar::modules::SNI::Item::getIconByName( icon_size = request_size; } return icon_theme->load_icon(name.c_str(), icon_size, - Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); -} \ No newline at end of file + Gtk::IconLookupFlags::ICON_LOOKUP_FORCE_SIZE); +} diff --git a/src/modules/sni/snw.cpp b/src/modules/sni/snw.cpp index fc314f54..3ec33ae0 100644 --- a/src/modules/sni/snw.cpp +++ b/src/modules/sni/snw.cpp @@ -166,4 +166,4 @@ void waybar::modules::SNI::Watcher::updateRegisteredItems( obj, items); g_variant_unref(variant); g_free(items); -} \ No newline at end of file +} diff --git a/src/modules/sni/tray.cpp b/src/modules/sni/tray.cpp index 6ddf7447..e0414179 100644 --- a/src/modules/sni/tray.cpp +++ b/src/modules/sni/tray.cpp @@ -2,16 +2,15 @@ #include -waybar::modules::SNI::Tray::Tray(const Json::Value& config) - : config_(config), watcher_(), host_(dp) -{ -} +waybar::modules::SNI::Tray::Tray(const Json::Value &config) + : config_(config), watcher_(), host_(dp) {} -auto waybar::modules::SNI::Tray::update() -> void -{ +auto waybar::modules::SNI::Tray::update() -> void { for (auto item : host_.items) { - item.image->set_tooltip_text(item.title); - box_.pack_start(*item.image); + if (item.widget) { + item.widget->set_tooltip_text(item.title); + box_.pack_start(*item.widget); + } } if (box_.get_children().size() > 0) { box_.set_name("tray"); @@ -21,6 +20,4 @@ auto waybar::modules::SNI::Tray::update() -> void } } -waybar::modules::SNI::Tray::operator Gtk::Widget &() { - return box_; -} +waybar::modules::SNI::Tray::operator Gtk::Widget &() { return box_; }